Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
Plugin instances can be added to config entities directly or by using PluginBag. Since content entities must have fields, and especially now the schema is derived from the property definitions and the default storage depends on that, it's become very hard to set plugin instances on content entities as properties.
Proposed resolution
Add a plugin bag field item base class that can be extended for specific plugin types.
Remaining tasks
None.
User interface changes
None.
API changes
None.
Comment | File | Size | Author |
---|---|---|---|
#35 | drupal_2294745_35.patch | 14.06 KB | Xano |
#35 | interdiff.txt | 837 bytes | Xano |
#12 | drupal_2294745_12.patch | 15.36 KB | Xano |
Comments
Comment #1
XanoThis is an outline of what I had in mind. I'll have to dive into typed data a bit more to find out how to return computed values for particular properties (the plugin instances). We probably want to reset the instance and the plugin configuration when the plugin ID is changed.
Comment #3
XanoOh yeah. I have no idea what data type to use for an arbitrary object instance and if there's any chance we don't have to force developers to make their plugins be typed data as well, I want to take it.
Comment #4
XanoComment #5
tim.plunkettMost of this issue was covered in other condition issues, and I actually disagree with the __construct change, since that would make it different from blocks, image effects, filters, and variants...
EDIT: Cross post, I'll let Xano reopen if he chooses.
Comment #6
tim.plunkettAHHH wrong issue, meant to post on #2144577: Improve ConditionPluginBase implementation of ConfigurablePluginInterface
Comment #7
XanoWe probably shouldn't use an actual PluginBag anymore, but make the field item behave in a similar way.
What I'm trying to do is make the plugin_configuration property behave like a map while the plugin has not been instantiated. After the plugin has been instantiated, the plugin_configuration property must proxy getting and setting its value to the actual plugin. I haven't figured out how to do this. Tips are welcome.
I have plenty of time to work on this. If you don't have to work on this issue, please provide feedback instead of writing patches, as this issue to me is a way to understand the typed data API a bit better.
Comment #8
XanoThis is still untested, but it's pretty much what I was going for. I do need to find a solution for
getTypedDataPluginId()
as we obviously cannot have an abstract static method (its caller is static, so that's why it can't be non-static).I guess now is the time for uncensored feedback to see if this is getting close to a good solution or just a complete mess. Thanks!
Comment #9
tstoecklerHave yet to try out the code but from looking at it I think it's about as good as we can have it right now.
Some things:
These property definitions are not only static coincidentally, but by design. I think 'any' is fine for now.
As discussed with Xano in IRC this is a problem. One shouldn't have to override both, but that's currently how it is. Also e.g. CommentItem in core does *not* override get() (only __get()) which proves that we need to fix that.
Comment #10
XanoComment #11
dawehnerDid you considered to add a plugin type feature to plugin managers or discoveries or something else? This would allow to write a generic implementation here, without relying on service IDs directly.
Comment #12
XanoThis patch contains a test that works 25%. Setting and getting the plugin ID on the field does not work yet and I'm not sure why.
That would be cool. Is there an issue about that we can link to?
Comment #14
dawehnerI am pretty convinced that you want more something like the following.
Comment #16
XanoThe instance should only be rest if the plugin ID changes. If the configuration is changed, it should be proxied to the instance if it is configurable. Similarly, when the plugin configuration property is read, its value should come from the instance, if it exists and it is configurable.
Oops.
Comment #17
dawehnerI don't really get the second usecase, why should you want to get the configuration from the plugin instance, even you already have it available. The same point could be made for the plugin ID.
Comment #18
XanoUse case: load a content entity that has plugin data in this field. Some code instantiates the plugin, works with it, and changes its configuration. Entity API then saves the entity, but if the call to the value of the plugin configuration property is not proxied to the plugin instance, the configuration that is retrieved from the entity is the old one that was originally loaded from storage, instead of the up-to-date configuration from the plugin instance.
Proxying the plugin ID is not necessary, because once that changes, the instance must be unset from the field anyway. The instance may continue to exist, but no longer as a value of the field.
Comment #19
BerdirIs that really such a huge problem? It's the same for config entities and EntityWithPluginBagsInterface, see ConfigEntityBase::preSave(). If it works there, it's OK to live with for content entities too?
Comment #20
XanoSurely it will work for base fields, but it's not really pretty. The patch from #12 shows, I believe, that proxying is not crazy hard to do. Once someone decides to turn this in a configurable field, the
preSave()
solution will no longer work.Unless there is a good reason not to try and proxy calls, I think we should implement it.
Comment #21
BerdirComment #22
XanoBecause when a user adds a configurable plugin bag field to a content entity, the
preSave()
method will not automatically be added/update with code to store the field's instance's values.Comment #23
Berdirfield item/type classes can have preSave() method too, and it should be implemented here, then there is no difference between base and configurable fields?
We can't use that interface I mentioned, I was just using that as an example.
Comment #24
XanoIt's actually not just saving an entity. If you set the field's configuration, then get the instance, then set new configuration, it will not be in sync with the instance that may be used elsewhere.
Comment #25
XanoComment #26
XanoThe tests should pass. I will need to re-evaluate whether we should still expose the instance as typed data if the contained plugin implements TypedDataInterface.
Comment #28
XanoComment #32
andypostwill require protected $pluginManager on class
So maybe better use field setting to define plugin manager like ER fields needs entity_type setting configured
So no way to pass plugin specific defaults?
this should chain settings clear!
maybe check the plugin is configurable and get its default settings
There's no type for binary blob?
plugin_id maybe?
do we have any constant for that?
so no way to leave the field empty?
Comment #33
XanoIt won't, because 1) dependency injection is still impossible for typed data and 2) even when it will we, the base field doesn't care about that. It only needs the method.
How? We cannot assume all plugin managers can be retrieved from the container, for instance.
Good point!
What do you mean?
I thought of that, but the plugin ID itself does not say much about the contained plugin if it is configurable. I concluded not having a main property name would be the safest way to let people ask for a specific property instead of giving them something that might be useless.
Comment #35
XanoI implemented this.
Comment #37
Xano@andypost: could you please reply to #33? I would love for this to get into 8.0.0, otherwise I will have to postpone some contrib release to 8.1.0 and that would be a terrible shame.
Comment #38
andypostI mean that setting value mostly causes
onChange()
to fire, but that could not be a case for field properties.The trick for changing pluginID is not clear to me. Maybe better to define and interface method to allow set plugin with config as second param. Otherwise it's confusing to change plugin but the settings will stay old...
Other points are explained for me and fine.
Comment #39
XanoFor this issue to be useful for base fields, #2248977: Complete support for multi-value base fields in ContentEntitySchemaHandler and use it for the user.roles field needs to be fixed first.
Comment #40
XanoI re-opened #2134761: Convert plugins on payment entities to fields where I continued using the patch from #35. I figured a practical use case would allow for better manual testing. I found many more bugs that I fixed and tested. Once that issue has been fixed, I will try and use the improved code to finish the patch here.
Comment #41
XanoThis issue is pretty hard to solve because Field API magically creates empty items, ignores field item's default values (
applyDefaultValue()
and instead setsNULL
everywhere), and sets$notify
toFALSE
in many places for no documented reasons. These behaviors pretty much prevent developers from re-using existing data type plugins if they want any sort of synchronization between properties in a parent field/property.I know there are issues to remove magic empty field item creation. Am I wrong about the other things and is there actually any documentation for those behaviors?
Comment #42
XanoI've been working on this feature in #2134761: Convert plugins on payment entities to fields for the past few weeks. Developing it for a practical use case helped me discover several flaws in the different approaches I tried to use. I now have a working and re-usable solution that will be committed to Payment. I will use this experience to port to code to Drupal core, which, because the actual plugin code is not Payment-specific, should be fairly straightforward.
There will of course be tests for this functionality, but because some aspects need integration testing, I am wondering if there is a part of core that I can convert as part of the patch, both to provide a concrete use case people can learn from and to have integration testing.
Comment #43
BerdirWe are not using plugins anywhere in content entities AFAIK.
I'm not sure if we can get this into 8.0.x, doubt it. The issue is currently defined as a task, but it is more a feature. So maybe publish it as a standalone contrib module first, then we can then get into core with 8.1.x?
Comment #44
bojanz CreditAttribution: bojanz commentedThis is a very worthy core addition. Let's try again for 8.2.x
Comment #45
XanoFor those who missed it: this idea eventually ended up in the Plugin module, along with some other plugin system improvements.
Comment #46
XanoUnassigning myself.
Comment #49
joachim CreditAttribution: joachim as a volunteer commented> this idea eventually ended up in the Plugin module
There is also an implementation of a plugin reference field in Commerce.
How similar are the two approaches?
Comment #59
joachim CreditAttribution: joachim at Factorial GmbH commentedI think as a first step, we need an issue that formalises the concept of a 'plugin type': #3260378: Add a concept of a 'plugin type' ID.