Problem/Motivation
Migration plugins throughout core and contrib keep having to do this sort of thing:
$delimiter = isset($this->configuration['delimiter']) ? $this->configuration['delimiter'] : '';
Proposed resolution
Implementing https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Component%21Plug... means that plugins can define default values for any configuration in defaultConfiguration().
The result is:
- better DX as you don't have to keep checking array values in $this->configuration are defined
- better DX as the default value for something in $this->configuration is in one place only
- better DX as there's a place where all the configuration options for the class are listed (well the class docs should have that too... but it nudges modules that are lax on docs towards listing them)
This can be done in SourcePluginBase, ProcessPluginBase, & DestinationBase.
To facilitate this, create an @internal ConfigurablePluginTrait in the migrate module to use with migration base plugins. There is discussion to do this in the core plugin system in #2852463: Create a trait and base class to implement \Drupal\Component\Plugin\ConfigurableInterface, however for the migration plugins we want to prevent use of the calculateDependencies method from the interface, as these plugins are not configurations and do not use dependencies. Depending on the outcome of that issue and #2946122: Deprecate ConfigurablePluginInterface and replace with an interface that doesn't extend DependentPluginInterface there may be a future core plugin library solution to address this, and refactoring the migration plugins around a module trait now will make that process much easier, and provide immediate benefits in cleaning up the plugin code. Making the trait @internal will make sure we can refactor around any potential plugin library solution in the future.
Remaining tasks
Postponed on #2852463: Create a trait and base class to implement \Drupal\Component\Plugin\ConfigurableInterface
Create an @internal ConfigurablePluginTrait use with migration base plugins.
Implement ConfigurablePluginInterface in the migration base classes and use the trait. Remove the interface from any core classes that extend these base classes.
Remove unused calculateDependencies() methods from core migration plugins as these plugins are not config entities.
Commit it!
As a follow up to this issue, refactor plugins to set default configurations and remove unnecessary isset() calls. @see #2995393: Refactor migrations plugins that now implement ConfigurablePluginInterface
User interface changes
None.
API changes
None.
Data model changes
None.
| Comment | File | Size | Author |
|---|---|---|---|
| #60 | Interdiff.2937177.57-60.txt | 899 bytes | mikelutz |
| #60 | 2937177-60.drupal.Migrate-plugin-base-classes-should-implement-ConfigurablePluginInterface.patch | 27.51 KB | mikelutz |
Issue fork drupal-2937177
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
joachim commentedI'm minded to try to get #2852463: Create a trait and base class to implement \Drupal\Component\Plugin\ConfigurableInterface in first.
Comment #3
phenaproxima+1 for this idea!
Comment #4
heddn+1 on the idea.
Comment #5
heddnTagging novice. Basically, we'd be doing what is happening over in #2911242-24: Create field collections source migrate plugin. Based on my reading of the related issues, let's introduce this with @TODO's in it to resolve after or with #2852463: Create a trait and base class to implement \Drupal\Component\Plugin\ConfigurableInterface. I'm thinking it makes sense to do this work independently of the related issue, since we can probably move faster in a single module than for the entire plugin system.
Comment #6
joachim commented> I'm thinking it makes sense to do this work independently of the related issue, since we can probably move faster in a single module than for the entire plugin system.
Yes, I think that's better than my suggestion.
When the plugin system provides the trait or base class in #2852463: Create a trait and base class to implement \Drupal\Component\Plugin\ConfigurableInterface, the Migrate plugin base classes can seamlessly switch over to importing/inheriting.
Comment #7
heddnIt is interesting that we also have to implement
calculateDependencies(). I'd be interested in feedback, and if tests pass green.Comment #8
heddnI see the history of things is that the configurable plugin interface came out of field plugins over in #2271419: Allow field types, widgets, formatters to specify config dependencies. And the
calculateDependenciescomes from that. I wonder if we need a configurable plugin interface in the plugin system that doesn't require that method?Comment #10
heddnHere, I've forked the upstream ConfigurablePluginInterface from the plugin system. Because we do not need/want
calculateDependencies(). Depending how this fares here, we might want to consider doing that fork upstream too.Comment #12
joachim commented> https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Component%21Plug...
Reading the docs for that, it looks like the assumption is that a configured plugin is going to be stored inside a config entity, and that therefore that config entity needs to ask the plugin for any dependencies to add to itself.
That's not the case in Migrate IIRC, and also in parts of contrib, e.g. Commerce uses configurable plugins on content entities (which don't care about config dependencies).
So definitely a case for filing an upstream task for adding a parent to ConfigurablePluginInterface.
Comment #13
heddnI'm going to postpone this on #2946122: Deprecate ConfigurablePluginInterface and replace with an interface that doesn't extend DependentPluginInterface.
Comment #14
mikelutz#2946122: Deprecate ConfigurablePluginInterface and replace with an interface that doesn't extend DependentPluginInterface is in progress, but the decision was to continue to use ConfigurablePluginInterface and require DependentPluginInterface to be implemented separately starting in Drupal 9. As such, we can go ahead with implementing ConfigurablePluginInterface as-is.
This was discussed in the weekly maintainers meeting, so I'm reactivating the issue.
Comment #15
robpowellI am queuing up for a test against 8.6.x. When I try to test this locally in phpstorm I get the following errors for all the test data:
I am not sure if this is a local issue which is why I want to run the tests again.
Comment #16
mikelutzThis is due to a bad test ($value shouldn't be passed as the plugin_id in the test because $value could be an array and $plugin_id should be a string) combined with the wrong constructor signature (scalar string should not be in the function definition, breaks older versions of php)
SqlTest also needed to have the setConfiguration method it defined in its test class removed to be compatible with the new interface.
Comment #17
mikelutzComment #18
pifagor1 on the idea
Comment #19
pifagorComment #20
heddnI thought we had discussed using plugin system's ConfigurablePluginInterface. Then we could get rid of this interface.
If we use the plugin system's interface, then let's implement calculateDependencies as Final in the trait. So nothing can expand on it and its easy to remove later.
Comment #21
mikelutzThis patch goes back to the core ConfigurablePluginInterface.
I can't implement calculateDependencies as final because it prevents extensions from implementing DependentPluginInterface.
Extensions should be allowed to override calculateDependencies provided they directly implement DependentPluginInterface. Currently \Drupal\migrate\Plugin\migrate\destination\Entity does this.
Comment #22
heddnWhy does Entity need to implement calculateDependencies? It isn't a config destination? Seems like a hold over from pre 8.1 when these things were still config.
Comment #23
heddnComment #24
heddnThe configuration needs to preserve the array keys, so made that change here. Also cleaned up doc comments.
Comment #27
heddnThis hopefully fixes up some tests. The few I randomly picked returned green in local testing.
Comment #28
phenaproximaLooks like tests are still failing...?!
Comment #29
heddnOoops. Let's upload the actual patch.
Comment #32
mikelutzThis should fix a bunch more of the errors..
Comment #33
mikelutzLets try one more to fix the last test and zap some coding standard issues.
Comment #34
mikelutzDo we want to rewrite the base plugins to make use of this in this issue, or do we want to get this committed and then refactor them in a separate issue?
Comment #35
heddnThe patch here is fairly small and easy to review. It also proves we have a complete set of functionality so let's proceed with it all in a single patch, for now.
Comment #36
heddnYes, this is perfect.
Stray change, maybe?
I don't see any explicit test of this, so maybe we should add some to this?
Comment #37
mikelutzCodesniffer wanted a period at the end of the comment, but that hunk doesn't belong to this issue, I've removed it and added unit tests for the trait. I assume we will add/fix more tests when we actually refactor the plugins around this patch.
Comment #38
heddnI like the explicit test coverage. One thing we might want though is a scenario with integer array keys. To test the preserve integer keys of mergeDeepArray. I've added an additional test for that. That is a very important scenario for some of our configurations.
Comment #39
mikelutzThat should have said "A test class using..."
I fixed that small doc issue that was bugging me, and expanded the testing a bit further to include a couple scenarios with integer keys.
Comment #40
maxocub commentedI don't understand this change. Is it really related to this patch?
Should we create a D9 follow-up to remove this and add a todo here?
Nit: Missing commas.
Nit: Extra line.
Comment #41
mikelutzOn 1, the test is flawed, and a previous version of this patch brought out the flaw and required fixing the test, but I believe the current version will still pass tests, so I've removed it and will reintroduce it in another issue.
for 2 I added a @todo, but I haven't created an issue, because depending on how #2852463: Create a trait and base class to implement \Drupal\Component\Plugin\ConfigurableInterface and
#2946122: Deprecate ConfigurablePluginInterface and replace with an interface that doesn't extend DependentPluginInterface play out, it may be removing that method or replacing that whole trait with a new core trait, I'm just not sure yet. I added a base @todo to remove it if/when the interface gets decoupled for now.
I addressed 3 and 4.
Comment #42
mikelutzComment #43
maxocub commentedThe code looks pretty fine to me. Reading the comments I can't say I totally understand why we are removing the calculateDependencies() calls and the IS do not mention this. Maybe we could benefit from a short explanation in the IS as of why this was needed? Marking as NW & Needs IS update for that.
Comment #44
mikelutzComment #45
heddnReviewed in migrate maintainers meeting with @maxocub @mikelutz @heddn. Obviously we need a CR. And I'd also like to see an example conversion of one field, source, process, destination, idmap, migration plugins. We'll add all the remaining "Convert all the things" in a follow-up, but this let's us prove things work.
Comment #46
mikelutzIn this patch we didn't add configurable plugin interface to idmap or field plugins. Neither really use configuration values, and I don't think they need to implement CPI. We did implement it for the migration plugin, but when I went to see about refactoring it, I noticed that in the constructor you have
Which just sets anything passed in as a configuration as its own property. I definitely don't like this pattern, but changing it is a bigger refactor of the default migration plugin than I expected.
This patch refactors the Source base plugin and the sql source plugin, the process base plugin and the static map process plugin, and the entity base destination plugin.
Comment #47
mikelutzFix those two tests, and break a whole lot more...
If we go through with this migration refactor around configuration, we'll need to document the configuration keys better in the plugin.
Comment #48
mikelutzFixing a chunk of tests.
Comment #50
mikelutzAfter discussion with @phenaproxima I think we should hold off on implementing CPI in the migration plugin as well. Doing it right requires a greater refactoring which touches a number of issues in the migrate D9 plan. Implementing CPI there should be done in another issue as part of that broader context.
So this patch implements CPI for source, destination, and process plugins.
Comment #51
mikelutzAdded CR
Comment #52
mikelutzThis is a more useful interdiff showing the fixed tests from #46
Comment #53
maxocub commentedThis looks pretty good. We now have example usage in source, process & destination plugins and all the tests are green. We also have a follow-up to refactor the rest of the migration plugins (#2995393: Refactor migrations plugins that now implement ConfigurablePluginInterface) and we have a change record (https://www.drupal.org/node/3005417).
I only found one nit that held me from RTBCing this patch:
Too much indentation.
Comment #54
mikelutzbah, good catch. There ya go. :-)
Comment #55
heddnNit: we need a comment why we are using merge deep array.
Comment #56
phenaproximaOnce the nit is fixed, this is RTBC from me. It's really nice to see this cleaned up.
Comment #57
mikelutzComment added.
Comment #58
phenaproximaNice. Thanks, @mikelutz! Let's get this in front of the committers.
Comment #59
larowlanHi folks, can we get an issue summary update explaining why we're doing our own trait in migrate instead of waiting for #2852463: Create a trait and base class to implement \Drupal\Component\Plugin\ConfigurableInterface - thanks
Comment #60
mikelutzI've updated the IS, and after a slack discussion with @phenaproxima and @heddn, I've marked the trait as @internal to make it easier to refactor or remove if a core plugin solution exists in the future.
"To facilitate this, create an @internal ConfigurablePluginTrait in the migrate module to use with migration base plugins. There is discussion to do this in the core plugin system in #2852463: Create a trait and base class to implement \Drupal\Component\Plugin\ConfigurableInterface, however for the migration plugins we want to prevent use of the calculateDependencies method from the interface, as these plugins are not configurations and do not use dependencies. Depending on the outcome of that issue and #2946122: Deprecate ConfigurablePluginInterface and replace with an interface that doesn't extend DependentPluginInterface there may be a future core plugin library solution to address this, and refactoring the migration plugins around a module trait now will make that process much easier, and provide immediate benefits in cleaning up the plugin code. Making the trait @internal will make sure we can refactor around any potential plugin library solution in the future."
Comment #61
andypostBut plugins live in modules so at least they should provide dependency on their provider, is not it?
Comment #62
phenaproximaThat is not a "calculated dependency" -- that's enforced by the plugin discovery system, which won't find plugins in modules that aren't enabled. Migrate does not need
calculateDependencies()and does not use it.Comment #63
phenaproximaAll looks good here. Thanks, @mikelutz! Back to RTBC.
Comment #64
mikelutzPostponed on #2852463: Create a trait and base class to implement \Drupal\Component\Plugin\ConfigurableInterface per @larowlan
Comment #65
megachrizI look into this issue more later, for now I add the tag "Contributed project blocker" because this blocks Feeds Migrate. Feeds Migrate aims to provide an UI for Migrate plugins and therefore it needs Migrate plugins that have configuration be officially declared as configurable.
Comment #66
heddnTriaging issue queue.
Comment #68
mikelutz#2852463: Create a trait and base class to implement \Drupal\Component\Plugin\ConfigurableInterface is in, so back to NW
Comment #69
edmund.dunn commentedComment #70
quietone commented@edmund.dunn, hi, are you still planning to work on this?
Comment #71
quietone commentedBack to postponed since #2852463: Create a trait and base class to implement \Drupal\Component\Plugin\ConfigurableInterface was reverted
Comment #73
wim leersComment #74
andypostComment #81
quietone commentedComment #82
mikelutzThis is now unblocked