We discussed terminology quite a bit during the entity storage discussion in Prague, see the notes here. Attendees of the discussion were plach (discussion lead), yched, fago, berdir, amateescu, das-peter.
This was discussed again in Szeged with fago, plach, Berdir and tstoeckler.
Problem/Motivation
There is no way to find out the ownership of entity fields. As some fields are provided by the entity type itself in the ::baseFieldDefinitions() and some are added in hook_entity_*_field_info() it is important to distinguish this. Also some fields should be taken care of by the storage controller but we also want to support adding fields and caring about the storage yourself.
Proposed resolution
We want to clarify the providing module of a field definition + who is responsible for storing the field. For this purpose the following methods are being added to FieldDefinitionInterface:
getProvider(): The provider of the entity field. For the base fields defined in the entity type the provide will be equal to the provider of the entity type. For fields added in hook_entity_*_field_info() the provider will be the module iplementing the hook. For configurable fields the provider will be 'field'. This allows to distinguish where the field originated. It will also enable to track dependencies, e.g. when a module is configured to rely on a field to be there (a rule configuration needs it to run, a module uses a token of that field, ..).hasCustomStorage(): Returns whether or not the field has a custom storage and thus should be ignored by the storage controller. This is FALSE by default, and hardcoded to FALSE for configurable fields. This will be used by #2183231: Make ContentEntityDatabaseStorage generate static database schemas for content entities.
| Comment | File | Size | Author |
|---|---|---|---|
| #41 | entity-field_keys-2143069-41.patch | 11.63 KB | plach |
Comments
Comment #1
fagoComment #2
amateescu commentedLooks like we can unpostpone this now.
About the plan: 'module' could be easily confused with the same key from D7 which meant the module that provided the field type (changed to 'provider' in D8), should we try to find some other name for it?
Comment #3
sunComment #4
plachWorking on this as it's a pre-requisite for #2183231: Make ContentEntityDatabaseStorage generate static database schemas for content entities, which in turn is a pre-requisite for #1498720: [meta] Make the entity storage system handle changes in the entity and field schema definitions :)
I like the suggestions from #2 and #3, but I will stick with the 'module' key for now to let @amateescu reply to @sun.
Comment #5
plachComment #6
xjmBeta-blocking as a part of #1498720: [meta] Make the entity storage system handle changes in the entity and field schema definitions.
Comment #7
plachInitial work, let's see how it behaves. On test coverage now.
Comment #9
amateescu commentedRe: #3
1) 'provider' is a property that's set automatically by plugin managers on all the plugins (including field types, I suppose), and, in this context, the meaning of 'module' is defined in the issue summary.
2) I kind of agree that a NULL value for base fields is not pretty at all.
How about reusing the 'configurable' TRUE / FALSE flag that just became available after #2191709: Remove the "configurable" flag on field types? And this could also be set automatically by the entity manager in the "field discovery" phase.
Comment #10
plachThis should be better.
Comment #11
tstoecklerThis is looking great. I have a few minor remarks.
+100. This makes a huge amount of sense!
This should be $module right?
See above.
Should be @return $this per our new standards.
Hmm.. I think *has*CustomStorage might be a better name? Not sure though...
Suggestion:
Indicates whether the *entity type's* storage controller should take care of storing the field values or whether *the module providing the field* will do that.
Liar! :-)
Comment #12
tstoecklerComment #13
plachActually correcting stuff after copy/pasting usually helps...
Comment #16
yched commentedNitpick, code organization:
Collect $baseFieldDefinition, then define $provider and assign it to each definition. Also, the code block could use a comment, just like the subsequent steps have their own comment.
Could we use moduleHandler->invoke() rather than a c_u_f() ?
Friendlier with PHPstorm's "find hook invocations" Drupal integration feature)
(for the same reason, better to avoid having the hook name in a var)
Why isset() + is_array() ? The hook is supposed to return an array, so just the foreach should be enough ?
80 char error
- Not sure why we need a mergeDeep() rather than a flat array merge here, but well, seems like it's what we have currently
- var $return is misnamed + unused, you probably mean $base_field_module_definitions :-)
(probably explains the fails)
- couldn't we just merge directly into $base_field_definitions here in the loop, instead of going through a temporary var and then doing one final merge in the main array ?
Same remarks as above :-)
Comment is now stale, the invoke happens above
"is" in method name looks weird, a field can be "multiple" or "translatable", but it cannot *be* "custom storage".
usesCustomStorage() ? hasCustomStorage() ?
Comment #17
yched commented(also, looks like @tstoeckler's #11 still needs to be addressed)
Comment #18
yched commented(also, looks like the issue title + several points in the issue summary should be updated to reflect the current patch - s/module/provider, not NULL for base fields, 'storage' no longer a constant-based flag...)
Comment #19
plachThis should address #11 and #16, which once again demonstrates how copy/pasting is rarely a good long-term strategy :)
Comment #21
plachMmh, I guess entity manager test coverage in not perfect yet...
Comment #22
tstoecklerLooks great!
I still think this should use $module not $provider. I might be missing something.
Comment #23
yched commentedThanks @plach.
Nitpick: for "@return $this", no description is needed.
Comment #24
plachI spoke with @fago about this and we both agree that provider is more conistent with what we already have in the entity type definition.
New patch coming as soon as my laptop's CPU cools down a bit :(
Comment #25
andypost+1 to Provider, provider could be a 'Core' so module makes no sense
Comment #26
tstoecklerRevamped the issue summary a bit.
I don't think we need a (draft) change notice, since this is completely backwards-compatible.
Comment #27
tstoecklerComment #28
plachThis adds test coverage for the provider key and addresses #22
(I completely missed what Tobias was saying, sorry)
Comment #29
plachAnd this adds actual coverage for getters/setters. We should be done here.
Comment #30
tstoecklerLeaving at needs review for 1., otherwise RTBC.
I was going to ask whether you didn't use FieldDefinitionInterface because you're using setProvider(), but then I realized that we're calling it unconditionally on the return value of ::baseFieldDefinitions() (and hook_entity_*_info()) so it should really be on FieldDefinitionInterface.
Thank you so much for commenting your tests!!!! This is soooo helpful!! +1000
Does this add an assertion that it is called with $module? I've never heard of $this->matches(), very interesting.
Comment #32
plachAdded
setProvider()to the interface. Initially I was wondering whether this would make sense too, but then concluded that, since the interface has only getters, for consistency it would be better to avoid that.But in the end there is really no point in preferring consistency over correctness :)
Comment #33
tstoecklerI wanted to RTBC, but I just had small question come to my mind: In getProvider() should we provide some sort of fallback if $this->definition['provider'] is not set? I noticed that in the test you are not calling getProvider() before calling setProvider() first. As it's sort of a misconfiguration to have a field without a provider we could also decide that it's perfectly fine to throw a notice if there's no provider specified, but I'm not sure...
Not marking "needs work" for that, but with interfaces you can just do ->getMock('FooInterface') directly.
Comment #34
plachWell, we are in the same situation with most (all?) other field definition keys, so I don't think provider needs a special treatment.
Comment #35
tstoecklerOK, that works for me.
In this particular case we're pretty safe anyway, as we're always calling setProvider() ourselves.
RTBC.
Comment #36
fagoI do not agree it is though, as the bogus implementations for configurable fields shows it does not make sense for all field definition implementations. That's why we have setters only on the FieldDefinition class. I do not see what should make provider special, so let's follow it here as well.
Also had some other minor remarks, and suggested docu improvements - so just re-rolled it accordingly. Please review.
Comment #37
plachI don't get the reason for this change: FALSE is the default value :(
Any field definition that won't provide a setter will automatically break as soon we try to retrieve field definitions now. We need at very least to add a
method_exists()check.Comment #39
yched commentedI tend to agree with @fago that so far we avoided providing setters on stuff that is not writable for config fields. But then (just like has been done for similar cases), the ->setProvider() calls need to be made within an
instanceof FieldDefinitioncheck.Same as was done in buildBaseFieldDefinitions(): please move $provider = ... line one line below :-)
Also
Comment #40
plachThis should fix failures.
I am not very happy with the current solution so, after discussing this again with @fago, I opened #2225961: Introduce "controlled" setters on FieldDefinitionInterface to avoid special-casing FieldDefinition. Better handle any controversial detail in a non-critical non-beta-blocker issue :)
Comment #41
plachI forgot about the line order issue...
Comment #42
tstoecklerplach++
Back to RTBC then.
Comment #43
fagoChanges look good to me as well + commented on the follow-up.
Comment #45
plach41: entity-field_keys-2143069-41.patch queued for re-testing.
Comment #46
plach#40 was green and #41 just swapped a couple of unrelated lines so it should be ok to move this back to RTBC.
Comment #47
catchCommitted/pushed to 8.x, thanks!