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.
Suggested commit message:
Issue #1893772 by chx, fubhy, slashrsm: Move entity-type specific storage logic into entity classes.
Problem
- Most entity storage controllers in core right now do NOT override the storage. They apply custom CRUD/business logic for creating/saving/deleting entities.
- That custom CRUD business logic is completely independent from the storage. Storage has to mean Database vs. Remote vs. Config vs. File vs. Whatever only.
Goal
- Limit StorageController to actual storage operations only.
- Move logic to the entity classes.
Related issues
Comment | File | Size | Author |
---|---|---|---|
#62 | 1893772_62.patch | 181.88 KB | chx |
#60 | 1893772_60.patch | 181.81 KB | chx |
#60 | interdiff.txt | 815 bytes | chx |
#59 | 1893772_57.patch | 181.73 KB | chx |
#59 | interdiff.txt | 1.29 KB | chx |
Comments
Comment #1
yched CreditAttribution: yched commented+1, most def.
Comment #2
fago+1 also - the storage controllers right now really take care of storage logic.
Also related #1497374: Switch from Field-based storage to Entity-based storage.
So getting the names right is the hardest part here imo. What about using "storage engine" for the storage operations? For the other one I'm not so sure, CRUD does not map too well as we have not exactly the CRUD methods on it.
Comment #3
sunLooking at EntityStorageControllerInterface, there are only two methods that don't really belong there:
And looking through all storage controller overrides we currently have in core (except ConfigStorageController, of course), they are overriding these methods:
Whereas
load()
andattachLoad()
are commonly used to prepare/apply default values - i.e., not actually manipulating the load storage operation.That said, base
attachLoad()
method on its own is interesting: It loads and attaches field data, and also invokeshook_entity_load()
implementations. Both have their own + independent storages, so that logic shouldn't be part of the StorageController.The only real storage-related method I was able to find is UserStorageController::save() — since that code is strictly tied to a Database/SQL storage.
Regarding name, EntityLogicController would work for me, too — I like how that would finally bring in the terminology of business logic, which is the core of entity types in the first place... Essentially, two entity types that have the same LogicController really shouldn't be two different entity types. ;)
Comment #4
fagohm, logic isn't very descriptive - it could mean a lot. What about having EntityStorageController + EntityStorageEngine?
There is also buildQuery() or so and delete() which goes with SQL/DB.
Actually, I think this should move into the EntityManager now - opened #1893820: Manage entity field definitions in the entity manager.
Comment #5
sunHm, if I can't get a LogicController, can I get a BusinessController? :P
Comment #6
fago:D I'd not say BusinessController is more descriptive? What about StorageLogicController?
Comment #7
chx CreditAttribution: chx commentedSo what's the decision here, how should we proceed.
Comment #8
chx CreditAttribution: chx commentedAnd yes I am willing to work on this.
Comment #9
fagook, so here a proposal to move on:
- Go with "EntityStorageController" and "EntityStorageEngine" for now, thus we postpone the bikeshed of rename EntityStorageController and focus on separating EntityStorageEngine out.
- Fix entity-info to be consistent with storage controller also, i.e. call it "storage_controller_class" there instead of "controller_class" - that's inline with what we do with other controllers.
- Add "storage_engine_class" for the new storage engine.
If we want to rename EntityStorageController we can bikeshed on that in another issue, but move on here.
Comment #10
chx CreditAttribution: chx commentedbuildQuery is a tricky one. How do we deal with that? it's definitely SQL bound and yet it is needed.
Comment #11
BerdirHm.
We currently have three overrides of that. Comment should IMHO go away, joins to other entities are bad. Term should probably go away too, load should not do access control* and the translatable tag is useless, I think we should remove that completely.
The only problematic one seems to be node then, which is doing some trickery with the revision table. If we want to decouple sql storage from the storage controller then we need to somehow get rid of that.
* why are we doing that ?!
Comment #12
fagoWell, if we have customizations of the sql-storage it should be in its own StorageEngine class, e.g. NodeSQLStorageEngine ?
I think it would
* make it more obvious that the storage controller contains the storage logic, hooks, etc. - see issue summary.
* ease customizing the storage of existing entities only, e.g. move nodes to mongo
* ease doing entities with remote storage as you could re-use the storage-logic controller and concentrate on storage
Comment #13
BerdirYes, I think that would make sense for now. It's SQL specific handling that might not be necessary for MongoDB. And secondary, we should try to get rid of most of it as I said above.
@fago: I think you misunderstood me. That note was related to the term_access check in the term storage controller. This is at odds with every other entity type. We check access when querying for entities, not when loading them. It was probably added when the entity loading mechanism was introduced back in 7.x to make the conversion easier but I'm quite sure that this should go away.
Comment #14
fago>@fago: I think you misunderstood me.
Oh sry. I totally agree that loading should not - or better - must not run any access checks.
Yep!
Comment #15
BerdirOpened #1919022: Remove translatable tag and #1919016: Remove term_access tag in TermStorageController::buildQuery() for the discussed tags in the term storage controller.
Comment #16
andypostSo now to implement node_access?
Comment #17
chx CreditAttribution: chx commented#1982880: Refactor the entity system to be evented
Comment #18
msonnabaum CreditAttribution: msonnabaum commentedReopening because I think the discussion of how this is split is beyond chx's issue.
I agree with this thinking, but I dont think it suggests we need another controller. Business logic goes on the object itself. Node's business logic goes on Node, because that is 100% Node's responsibility, not something to be delegated to another object. We'll never come up with a good name for an additional controller, because having one makes no sense.
Comment #19
yched CreditAttribution: yched commentedRelated: #1937266: BlockContent entities uses delete method instead of pre/postdelete
Comment #20
amateescu CreditAttribution: amateescu commented*clap* *clap* .. totally agreed with putting that logic where it belongs, in entity type classes.
Comment #21
fagoWe did also discuss that several times during drupalcon, and we overally agree that this is a good approach. So let's do it.
Comment #22
chx CreditAttribution: chx commentedThis is being worked on in http://drupal.org/node/1857558/ branch 1893772
Comment #23
chx CreditAttribution: chx commentedComment #25
Berdir#23: 1893772_23.patch queued for re-testing.
Comment #26
BerdirTestbot failures, failed to write config files and stuff like that.
Comment #27
andypostQuick review shows that some methods needs doc-block comments
Once they are defined in interface so just {@inheritdoc} needed
Comment #28
chx CreditAttribution: chx commentedTons and tons of doxygen fixes.
Comment #29
chx CreditAttribution: chx commentedComment #30
chx CreditAttribution: chx commentedNotes for reviewers:
Comment #32
tim.plunkett#28: 1893772_28.patch queued for re-testing.
Comment #33
BerdirLong list of documentation nitpicks. A few things about method names.
Noticed that you haven't touched baseFieldDefinitions() yet. That's tricky because that in fact means that it's still not possible to replace storage controllers in a generic way as every entity type will need it. Probably needs a follow-up for that.
Missing documentation.
Should have a type hint I guess?
This should use the injected database connection as the functions below.
Also, documentation.
Generated param stuff should be removed.
Here it needs to be added.
Empty (undocumented) interface, what is this used for?
Should use Drupal::service()
And... more @inheritdocs :)
And more documentation necessary here :)
Missing type on status and Optional. should be (optional). I know this is just moved but we can just as well fix it here, it distracts when reviewing ;)
Same here.
And more @inheritdoc.
Ditto.
Non-namespaced type
And more ;)
No docs on interface.
Missing namespace.
Missing string.
Missing int
Hm, I had the idea of having a default storage controller for entity types that are bundles for another entity type which would call this functions/hooks by default. This makes that a bit weird, would have to be a BundleEntityBase class or something.
Should use $this->database.
We usually use "Term entity" not "Term object" I think.
@inheritdoc.
This doesn't delete roles, it deletes permission/role and user/role assignments.
No docs on interface.
Similar, the documentation her is correct but not sure about the names.
Another left-over to remove.
Methods in this class are also missing @inheritdoc.
Comment #34
Crell CreditAttribution: Crell commentedI've not been in this code in a long time, so it's hard for me to get into much detail. Some comments below, but I'm afraid they're rather superficial. It looks like most of this patch is just moving code around between entities and storage controllers and breaking it up into more methods, which I generally support.
The main concern I have is the static methods chx mentioned. He and I talked in IRC, and he pointed out that they need to operate on multiple objects at once since "everything is a set operation". I hate that we're using static methods here, but since we can't put them on the storage controller (since that needs to be swappable but the logic here is bound to the entity, not the storage mechanism for the entity), I don't have a better solution.
I know you said to ignore the function calls for now since injection is being handled elsewhere, but the very next method has a $this->database in it, meaning the DB is already injected. We can go ahead and convert this one since no new injection is needed for it.
Ibid.
If we're adding this line, at the very least it should be Drupal::, not drupal_container(), as the latter has been deprecated for months.
As above.
Need docblocks.
This makes me very concerned about race conditions. I've run into a *lot* of race conditions lately at work around entity A being saved, which requires entity B to be updated, and entity B is updated based on entity A. Oh, wait, you got entity A via node_load()? Guess what, because of the DB transactions the node load will get the pre-edit entity A, and so you're operating on stale data.
Having a node_load() (function or method, doesn't matter) here raises a red flag in my mind for that reason.
Comment #35
fagoGreat work!
Patch looks good to me, it's mostly doxygen stuff that is missing or needs work, but I'm not repeating that as berdir pointed that already out.
Could be explained a bit better - which object is it? Maybe just describe it as "The revision record to be saved."
Oh yes, we should really do #1729812: Separate storage operations from reactions. This patch doesn't change the likelihood for that issues though.
Comment #36
chx CreditAttribution: chx commentedKeeping up with HEAD (User NG, Actions are config entities). berdir's review is addressed.
Comment #38
chx CreditAttribution: chx commentedBoy, these two fine English gentlemen certainly are moving the drop fast.
Comment #40
chx CreditAttribution: chx commentedComment #42
fubhy CreditAttribution: fubhy commentedWhoops... Turns out that #2017657: Node admin test broken wasn't actually broken. Kinda weird anyways.
Comment #43
fubhy CreditAttribution: fubhy commentedForgot the interdiff.
Comment #45
chx CreditAttribution: chx commentedIt failed and passed due to the commit and rollback of #2012916: Implement access controller for the menu and menu link entity. Thanks fubhy, much nicer fix than I was able to come up with. Note that the test is broken, but differently, #2018315: NodeAdminTest is broken here has been filed.
Comment #49
chx CreditAttribution: chx commentedChasing HEAD.
Comment #50
fubhy CreditAttribution: fubhy commentedJust fixing some nitpicks. I know that some of these have not been introduced here, but since we are moving it all around anyways this is a good chance to fix them.
Comment #52
fubhy CreditAttribution: fubhy commentedComment #53
chx CreditAttribution: chx commentedAdded a few missing 'public' to some functions.
Comment #54
fubhy CreditAttribution: fubhy commentedGiven that previous tests were green too this probably indicates that we are missing test coverage for this method.
This is what we had before... Does not seem right, or? So yeah, looks like we are missing a 'comment cleanup' test.
Given that previous tests were green too this probably indicates that we are missing test coverage for this method.
Comment #56
chx CreditAttribution: chx commentedWe are not adding new tests for existing code here; please file a separate issue. The issue would never ever end if we were to do that.
Re fail, bot fluke.
Comment #57
fubhy CreditAttribution: fubhy commentedRead through the entire patch once more. Found a few minor things and fixed them in this patch. @see interdiff. I think (even though it looks ugly) we should use "unassign" instead of "unAssign" as it's one word.
This is the only thing left for this patch to be RTBC imho. I would've written the documentation but I seriously have no clue how to document these methods/what to write.
Comment #58
fubhy CreditAttribution: fubhy commentedOh yeah, I wasn't implying that we should fix it here. That comment was rather a reminder for me to open a follow-up later.
Comment #59
chx CreditAttribution: chx commentedResolved @todo from #57
Comment #60
chx CreditAttribution: chx commentedrefined one of @return in #59
Comment #61
fubhy CreditAttribution: fubhy commentedCool, RTBC if green (which it will be).
Comment #62
chx CreditAttribution: chx commentedKeeping up with HEAD (FileNG)
Comment #62.0
chx CreditAttribution: chx commentedUpdated issue summary.
Comment #62.1
chx CreditAttribution: chx commentedUpdated issue summary.
Comment #63
alexpottCommitted 1648a47 and pushed to 8.x. Thanks!
Fixed capitalisation on commit...
We definitely need a change notice here but I also think we need to scan existing D8 change notices...
Comment #64
chx CreditAttribution: chx commentedI do not see a need for new change notice because of 1. and 2.:
Comment #65
fagoAwesome!
I noticed NodeStorageController postDelete() has been forgotten, so I've opened #2020837: Node access table is not cleaned up after delete.
Comment #66
chx CreditAttribution: chx commentedberdir found Entities are now classed objects using a defined interface, and that concludes our journey in change notices. Still no need for a new one.
Comment #67
Tor Arne Thune CreditAttribution: Tor Arne Thune commentedComment #68
xjmWhen you update an existing change notice, PLEASE also add the issue you're updating it for to the list of issues on the change notice. Please please please. This is the only way we can keep track of where changes are coming from, and the way that the change notices are auto-linked in the issue summaries. I updated https://drupal.org/node/1400186.
Edit: Also, a summary of the API changes actually made by this issue, in the summary, would be nice for everyone whose patches broke on account of it. :)
Comment #69.0
(not verified) CreditAttribution: commentedupdated commit message