Updated: Comment #11
Problem/Motivation
When ConfigEntities and configurable plugins are used together, the plugin configuration must be stored as property on the entity.
If the plugin configuration is changed directly, the entity property will be updated on save.
This is currently handled either by EntityInterface::preSave(), or ConfigEntityInterface::getExportProperties().
However, if the entity property is updated directly, the plugin configuration may not be updated, and in some cases, completely overwritten upon save.
Proposed resolution
Since there are two directions the config must be synced, there are two places to have code:
1) Move all of the plugin-to-entity handling to EntityInterface::preSave() and out of ConfigEntityInterface::getExportProperties().
2) Add entity-to-plugin handling via ConfigEntityInterface::set()
This also has to be handled for both single and multiple plugin bags.
Entities with multiple plugin bags have public methods for retrieving the bags, but they are all differently named.
We would like to have a trait with an abstract protected method, but until that point, we're stuck with a public method on an interface.
The name of the property the entity uses to store the plugin configuration is also variable. That should also be on the trait, but for now is placed on ConfigEntityBase and left NULL by default.
This way, a ConfigEntity that is using a PluginBag just needs to use a trait, and specify their property if its not called $configuration, and everything will just work.
Remaining tasks
Please please please find a better name for EntityWithPluginBagInterface
Turn EntityWithPluginBagInterface into a trait before commit if possible.
User interface changes
N/A
API changes
DefaultPluginBag::setConfiguration() is now called DefaultPluginBag::setInstanceConfiguration(), since it was for setting the configuration of a single instance.
PluginBag has two new abstract methods, getConfiguration() and setConfiguration(). These are for setting and getting all of the configuration that those plugin bags represent, be they single or multiple.
There is a new (terrible named) \Drupal\Core\Config\Entity\EntityWithPluginBagInterface, which has a single method, getPluginBag(). This should become a trait as soon as possible.
A ConfigEntity class must implement EntityWithPluginBagInterface and provide a $pluginConfigKey specifying which property is used to store the plugin configuration.
The constructor of DefaultSinglePluginBag has finally been cleaned up. Instead of expecting an array of instance IDs as the second parameter, it takes a single string:
// Before
$plugin_bag = new DefaultSinglePluginBag($manager, array($instance_id), $configuration);
// After
$plugin_bag = new DefaultSinglePluginBag($manager, $instance_id, $configuration);
Comment | File | Size | Author |
---|---|---|---|
#19 | interdiff.txt | 2.66 KB | tim.plunkett |
#19 | plugin-2195753-19.patch | 43.01 KB | tim.plunkett |
Comments
Comment #1
tim.plunkettI'm going to work on a more generic version of this, since we need this fix whenever a config entity has a ConfigurablePluginInterface.
Since unlike most other plugins, BlockInterface always implements ConfigurablePluginInterface, it is the easiest example.
I do have a couple questions though:
Comment #5
tim.plunkettHere's the rest of the entities using a single plugin, I couldn't figure out image styles tonight.
Remaining: FilterFormat, Tour, ImageStyle, View.
There's a fair chance that not all of them are broken the same, but we should have tests either way.
Comment #7
tim.plunkettJust fixing the block test for now.
Comment #8
tim.plunkettHmm, I wrote the filter format test, which should have failed... But it didn't (well except for a schema clash!)
Sooo, not sure what to do here.
Comment #9
tim.plunkettSame deal. It seems entities with multiple plugins work just fine?
I haven't really stopped to think through why, just wrote these tests while watching some Olympics :)
Comment #10
tim.plunkettIt turns out that the config import bug is actually a very simple one, but is not the actual bug we were all seeing and expecting.
Entities that always instantiate their plugin bag in their __construct will break on config import, those that check for an existing instance first will not.
But the addition of code to set() is still needed, and across the board, and I finally came up with some tests to prove it.
After further discussion today in IRC with @pwolanin and @EclipseGc, I went ahead and genericized the implementation.
It's a little ugly right now, but it can be much cleaner with traits.
I'll update the issue summary in a bit, but here's the patch.
I also consolidated the test coverage after discussion in IRC.
Comment #11
tim.plunkettComment #13
tim.plunkettAddressed the test failures.
Comment #14
pwolanin CreditAttribution: pwolanin commentedI like this in as much having a consistent approach will help avoid re-introducing the bug in contrib modules.
Comment #15
jibranparent has no $block_id param so use of {@inheritdoc} is wrong.
doc block missing.
Comment #16
tim.plunkettBlockPluginBag was missing docs before this patch, and I don't change that param *at all*. But I'm going to fix it now just so we never have to talk about it ever again :)
Fixed the other comment.
Comment #17
neclimdulThe PluginBag changes look solid and sane. I like them. All the entity stuff though is out of my ballpark, someone else will have to RTBC that.
Comment #18
dawehnerNice work!
Can we explain why we want to avoid the extra call to setConfiguration? I can't imagine that the reason is performance.
How did that ended up in the patch? This is a change I don't understand in the context of the patch, but sure this does not block the patch.
If we change this, we should probably also remove {@inheritdoc} as it is totally wrong here, to be honest.
Should we put the BlockPluginManager in its own method?
AAAAAAAAAAAAAAh
Should we change the documentation here as well?
Comment #19
tim.plunkett1) Talked about it in IRC, calling set() is more consistent even if a bit overkill.
2) I think you answered this question in 5 :)
3) Whoops! Agreed.
4) I don't think that's necessary, we don't do that in any of the other entities. When it's only being used by a `new $pluginBagClass()` call anyway, it doesn't really affect much.
6) Fixed.
Comment #20
dawehnerThank you!
Comment #21
tim.plunkettAdded https://drupal.org/node/2203617.
I made https://drupal.org/node/2203633 its own change record because it will require ported modules to change.
As far as updating existing change records...
Comment #22
alexpottConfigEntityImportTest is a nice test!
Committed 0230aa2 and pushed to 8.x. Thanks!
We need to check existing change notices to ensure they are correct after this fix. Maybe https://drupal.org/node/1878416 needs work.
Removed some use statements left lying around.