Problem/Motivation
- In HEAD, FieldableEntityStorageInterface defines various on*() methods such as onFieldDefinitionCreate() and onBundleRename(), so that the entity storage handler can react to things like that that might require storage-specific action, such as updating SQL schema in the case of a SQL backend.
- #2330121: Replace ContentEntityDatabaseStorage::getSchema() with a new EntityTypeListenerInterface implemented by SqlContentEntityStorageSchema is adding some more on*() methods, but in a dedicated interface: EntityTypeListenerInterface.
Despite these the entity manager is the only notified business object, which makes impossible for other interested items to react to any of the events above.
Proposed resolution
Inject the event dispatcher in the entity manager, and dispatch entity type/field storage definitions create/update/delete events. The entity manager remains in charge of proxying notifications to the interested entity handlers.
Provide two traits that allow EntityTypeListenerInterface and FieldStorageDefinitionListenerInterface methods to keep their signature, while being used as listener methods. This means that entity handlers do not need to depend on event objects. This is true also for modules needing to notify the entity manager manually: they can keep calling notification methods the usual way, without needing to know anything about Symfony's event system.
Traits allow this code to be re-used by other subsystems, like for instance Views, that just need to implement the entity/field listener interfaces.
Remaining tasks
RTBC
User interface changes
None
API changes
Changed constructor signatures for the ModuleHandler and EntityManager classes.
| Comment | File | Size | Author |
|---|---|---|---|
| #52 | 2332935-events-52.patch | 1.1 KB | plach |
| #49 | 2332935.49.patch | 42.71 KB | alexpott |
| #48 | 2332935.48.patch | 42.59 KB | alexpott |
| #48 | 47-48-interdiff.txt | 1.42 KB | alexpott |
| #47 | 2332935.47.patch | 42.74 KB | alexpott |
Comments
Comment #1
plachDefinitely +1 on this: at a first glance it looks like the right thing to do IMO.
One doubt I had was whether using hooks or events, since we do not have a clear pattern yet AFAIK. However since all the involved code is OO, I guess events would be a better fit.
Another one was about registration: handlers are not instantiated by the DIC, are there other convenient registration approaches we could leverage?
Comment #2
dawehnerBump up, given that this blocks #2341323: Adapt the references field / table names in views, when corresponding entity schema changes if I understand it right.
Comment #3
catchIt definitely blocks itself. Edit, and yeah likely the views issue as well, was thinking similar.
Comment #4
catchAlso changing the existing methods to events would have to be beta deadline if at all.
Adding an event on top of the on* methods would probably work, so this narrowly avoids being a beta blocker.
Comment #5
catchAlso re-titling, we might use an event here but that's not the reason this issue is critical, just implementation detail.
Comment #6
xjmSo just to clarify #4, the best solution to this issue is beta deadline, and it's not a beta blocker because we could... keep a redundant API to keep BC?
Tagging as a priority for a pre-AMS beta sprint, for sure. Should we actually postpone #2341323: Adapt the references field / table names in views, when corresponding entity schema changes on this or is there another way to solve that?
Comment #7
dawehnerThis is just a rough start for the field storage definitions, to play around with ideas.
Comment #8
catch@xjm yes...
Comment #9
plachSpoke with Daniel about this: we agreed to proceed with events and make the EM proxy the call to handlers implementing
EntityTypeListenerInterface/FieldStorageDefinitionListenerInterface.Comment #10
plachFirst draft. This is critical so not beta deadline, I guess.
Comment #12
plachThis should be more or less complete: it provides two traits that allow
EntityTypeListenerInterfaceandFieldStorageDefinitionListenerInterfacemethods to keep their signature, while being used as listener methods. This means that entity handlers do not need to depend on event objects. This is true also for modules needing to notify the entity manager manually: they can keep calling notification methods the usual way, without needing to know anything about Symfony's event system.Traits allow this code to be re-used by other subsystems, like for instance Views, that just need to implement the entity/field listener interfaces.
Comment #13
jibranIt should be final.
Same here.
Comment #14
plach@jibran:
AFAIK
finaluse is discouraged: is that a pattern I am not aware of?Comment #15
dawehnerCool work!
it would be great to inject it, that is not so much additional work, if you ask me. It just makes it clear how "bad" the situation is. (same with entity manager maybe, but yeah this is already a mess.
Some of the other patterns we use is to have a FooEvents class which has the constants and nothing else, feel free to change it, if you want.
What about adding a small comment about that it is just available in case you update one.
it is not really a method.
I really like this pattern.
Comment #16
dawehnerCool work!
it would be great to inject it, that is not so much additional work, if you ask me. It just makes it clear how "bad" the situation is. (same with entity manager maybe, but yeah this is already a mess.
Some of the other patterns we use is to have a FooEvents class which has the constants and nothing else, feel free to change it, if you want.
What about adding a small comment about that it is just available in case you update one.
it is not really a method.
I really like this pattern.
Comment #17
plachThis adds explicit test coverage and should address #16. I did not inject the entity manager as that dependency is going to be removed when we refactor that code to dispatch a post schema event to which the entity manager itself can respond.
Comment #18
plachUnfinished PHP doc.
Comment #21
plachReverted event dispatcher injection as it causes troubles during installation. Agreed with Daniel we need a dedicated issue to do that: #2350111: Introduce an event to decouple the module installer from the entity manager.
Comment #22
plachComment #24
plachThis should "fix" event dispatcher injection and address #16.2.
Hopefully this should be ready to go now.
The interdiff is against #18.
Comment #26
plachComment #27
plachFixed last failures
Comment #29
dawehnerAwesome! Let's write an IS and a change record (not sure about the second bit).
Comment #30
plachUpdated issue summary. Not sure about the CR, but I guess now that beta is out it would make sense, working on it.
Comment #31
plachComment #32
plachCR draft at https://www.drupal.org/node/2350653
Comment #33
fagoMisses trailing point.
Docs do not match class.
wrong docs.
wrong docs.
Do have event names to be prefixed by component/module or so?
Should not say service - the fact that the dependency is is fulfilled via a service is irrelevant to the class :)
The summary said, one can still call the EM to invoke the methods. But then the events are not dispatched. Should instead, the EM be in charge of dispatching the events maybe?
Comment #34
plachGood point about moving event dispatching into the entity manager, the attached patch does that. I removed event dispatcher injection from the module handler (sigh), but I kept the related code clean-up as it will make re-injecting the event dispatcher easier in #2350111: Introduce an event to decouple the module installer from the entity manager.
Let's see whether I broke something...
Edit: no idea about 5...
Comment #35
plachForgot to revert this change.
Comment #36
plachComment #37
plachComment #38
plachAnything else needed before RTBC-ing this?
Comment #39
dawehnerIt is great to have this moved into a separate method, it just lets you think better about the problem.
Comment #41
plachRerolled
Comment #42
dawehnerstill green.
Comment #43
alexpottI don't think we should be adding
DependencySerializationTraitto things that are in the container. I've traced this back to the serialisation of the DisplayBag in a view. So I've added the trait to the DefaultPluginBag which means that the ViewsPluginManager is not serialised and so on...Are there going to be followups to use
EntityTypeEventSubscriberTraitandFieldStorageDefinitionEventSubscriberTraitin non test code in core?Comment #44
plachYep, Views will need those to react to table layout changes and adjust Views definitions accordingly.
Comment #46
plachMoreover those traits provide a BC layer, so I think it's useful to have them even if core is not using them yet, aside from tests.
Comment #47
alexpottFixed the tests by using DependencySerializationTrait in the correct places.
Comment #48
alexpottRemoving some unused uses - this patch looks good to me and I think my contributions minor enough that I can still rtbc.
Comment #49
alexpottRerolled due to #2246647: Rename PluginBag to LazyPluginCollection
Comment #50
catchCommitted/pushed to 8.0.x, thanks!
Comment #52
plachForgot to remove a couple of @todos, sorry.
Comment #53
alexpottCommitted 2681e4b and pushed to 8.0.x. Thanks!
Comment #56
yched commentedFollowup : #2411791: Provide empty methods rather than abstract methods in EntityTypeEventSubscriberTrait / FieldStorageDefinitionEventSubscriberTrait
Comment #57
tr commentedFor those of you who followed this issue - it introduced a bug which I have just reported in #3260520: GenericEvent is used improperly
Please review that bug report.