Yet another 'cherry-pick' from the patch in #1847596: Remove Taxonomy term reference field in favor of Entity reference.
When an entity bundle is deleted, entity_reference needs to inspect all its instances that might be affected by that change and update them if needed.
Bug :
- Let bundle 'foo' be referenceable by field_ref
- Delete bundle 'foo', field_ref can still lists 'foo' as a referenceable bundle, but that is ignored since the bundle doesn't exist anymore
- A couple weeks later, create a new bundle with the same name
--> The new bundle is instantly referenceable by field_ref, because 'foo' is stil listed as a referenced bundle, while that was not intended by the site admin.
Beta phase evaluation
Issue category | Bug: bad housekeeping of the "referenceable bundles" setting in e_r fields |
---|---|
Issue priority | Major : blocks #2412569: Allow setting the auto-create bundle on entity reference fields with multiple target bundles, which requires schema/config updates |
Disruption | None |
Comment | File | Size | Author |
---|---|---|---|
#104 | interdiff.txt | 995 bytes | yched |
#104 | 1978714-104.patch | 21.75 KB | yched |
#102 | 1978714-102.patch | 21.75 KB | jibran |
#102 | interdiff.txt | 2 KB | jibran |
Comments
Comment #1
amateescu CreditAttribution: amateescu commentedHere we go.
Comment #2
dawehnerJust in general I'm wondering whether it should be converted on the longrun to key/value with the target bundle. This for example results in a better diff at the end.
It feels wrong to delete the full instance, as it still somehow contains data.
We could use Field::fieldInfo()->getField directly
should be public ...
Nice test coverage. Maybe add a short sentence to the top of the two groups to explain the idea of the test.
Comment #3
amateescu CreditAttribution: amateescu commentedI'm not really sure if that buys us anything, but I'm certainly open to discuss it in a separate issue :)
taxonomy.module does the same thing (in a different place though): http://drupalcode.org/project/drupal.git/blob/refs/heads/8.x:/core/modul...
Fixed all the rest.
Comment #4
Dave ReidBecause we know that when you delete a taxonomy vocabulary, it deletes all of the containing entities. This is not the case for other entity types/bundles. When you delete a node type, it does not delete the nodes of that type. I agree that auto-removing the field does seem out of place. Maybe it should be only limited if the entity type target is taxonomy terms.
Comment #5
amateescu CreditAttribution: amateescu commentedWell, the thing is that we're removing only the instance (not the field like taxonomy.module), and only if that instance is set to reference just the bundle that was removed.
Comment #6
amateescu CreditAttribution: amateescu commentedOn second thought, let's just remove that part from this patch so we can move on with this necessary cleanup.
Comment #7
pfrenssenRerolled
Comment #9
amateescu CreditAttribution: amateescu commentedRemoved the part of the patch that wasn't actually testing the bug we're fixing here. Tests for field validation should be added in #2015701: Convert field type to typed data plugin for entity reference module .
Comment #10
dawehnerIt would be cool to use Unicode::strtolower() instead.
lol, I had a bit of grime while reviewing exactly over the ',' so it looked like a ';'
It is a bit odd to target the same bundle twice?
count of a non array for sure returns 1 ... it should be count($target_bundles) instead. Why do we not just use $this->assertEqual(array($this->bundle)) (potentially also before)
Comment #11
amateescu CreditAttribution: amateescu commentedThanks for reviewing!
Fixed.
It is indeed, removed the second one.
And fixed that last assertion as well, doh! :) No real preference about asserting a count or array equality..
Comment #13
amateescu CreditAttribution: amateescu commentedUsing the latest field api standards should work better.
Comment #14
dawehnerThis looks great now.
Comment #15
tstoecklerThe comment does not match the code, but IMO the comment does actually make sense. The current code makes the instance reference *all* bundles, if I'm not mistaken.
I just read that this was discussed above, but I'm not sure the behavior above was taken into account. I think if you have a field instance set up to specifically reference a certain entity bundle, having that instance reference all bundles of that entity types is a huge WTF. Ideally we would have the instance reference *no bundles at all*, so that it does not show up on the edit form at all, but then lets the administrator decide whether he wants the field to reference a different bundle or if he wants to delete the field.
Should be "Index 1"
Also might make sense to add a check for count() as well, here.
I would have re-rolled for the test changes, but I didn't know what to do about the first item.
Comment #16
amateescu CreditAttribution: amateescu commentedThe problem with the first item is, as always, how to present this in the UI. In code, we can look for all form modes that use that instance and set it to "hidden", but not telling the user about this would be a problem as big as letting that instance in place with the ability to reference all bundles.
Comment #17
tstoecklerOK, so should we simply disallow deleting the bundle then? That would be a pretty major change but it really sounds like that's what would be the ideal solution in this case.
Comment #18
amateescu CreditAttribution: amateescu commentedYeah, that would be pretty intrusive. Maybe an answer to #16 is just do a d_s_m() and tell the user what happened?
Comment #19
pfrenssenRerolled the patch to bring it inline with the latest changes. Setting to needs review for the bot. This still needs work as per the above.
Comment #20
pfrenssenI agree with #18, just show a message to the user. It will mainly be site builders that are going to delete bundles, and they will know how to update the reference fields.
Comment #21
amateescu CreditAttribution: amateescu commentedYep, I think that'll be enough. Should we tag this issue with "usability" or something to get more opinions?
And a small self-review of my original patch / your interdiff:
We really should do better here than getting all field instances. We can use
Field::fieldInfo()->getFieldMap()
and iterate on it to get only 'entity_reference' fields. Also, we need to make sure we're not trying to do anything for base fields, which have the same 'entity_reference' field type afaik.Comment #22
pfrenssenUpdated the patch with the remarks from #15, and provided the message.
I noticed that when a node bundle is deleted it still appears in the list of the entity reference field. This is because the cache of node_type_get_names() is not cleared when a node type is deleted. Created an issue for this: #2169531: Invalidate node_type_get_names() cache when deleting a node type.
Comment #23
pfrenssen@amateescu, I missed your post from #21.
I'll try implementing your idea. If you want to tag this for some more input, sure!
Comment #24
pfrenssenNow only loads the entity_reference field instances, instead of all of them.
Comment #25
amateescu CreditAttribution: amateescu commentedThanks @pfrenssen! Started a review of the latest patch but after finishing it I realised that I wrote almost all the code that needs to be updated, so attaching a new patch instead :)
Comment #26
pfrenssenLol seems like I forgot to update entity_reference_entity_bundle_delete(). Thanks for catching it!!
There's some duplicated code between update entity_reference_entity_bundle_delete() and update entity_reference_entity_bundle_rename() now - the discovery of the entity_reference field instances from the field map. Shall we factor that out into a separate function?
Comment #27
amateescu CreditAttribution: amateescu commentedSince there's only two implementations of that code atm, I'd rather not.
Instead, we should add this 'feature' to Field API itself: something like "Give me all the fields/instances across all entity types and bundles for the X field type" (with 'include_base_fields' as a boolean param) because this is a very common need.
Comment #29
filijonka CreditAttribution: filijonka commentedMade a reroll for this; bit unsure cause by some reason the test in previous patch wasn't merged so added it manually.
Comment #30
amateescu CreditAttribution: amateescu commentedThis patch needs more a rewrite than a reroll :)
Comment #31
filijonka CreditAttribution: filijonka commentedyeah I figured that by the comments but
1. I needed to try to a reroll on a patch involving psr-0 to -4
2. It's often easier to work form a green patch then from a red one.
3. nothing have happened in this since january so figured it needed some attention.
Comment #33
claudiu.cristeaCompletely rewritten, so no interdiff.
Comment #34
claudiu.cristeaThe test is more compact now.
Comment #35
claudiu.cristeaRerolled and applied #2452619: Move entity_reference_create_field function to EntityReferenceTestTrait.
Comment #36
amateescu CreditAttribution: amateescu commentedI think the EntityManager code from the current patch is implementing my idea from #27, but it's hardcoding the new method to ER fields :/ I still think such a method can be useful but it should receive the field type as an argument.
Let's see if @Berdir and/or @yched have some thoughts about this :)
Comment #37
claudiu.cristea@amateescu, I had no idea where to place it. I saw it sitting in EntityManager next to
::getFieldMap()
or::getFieldMapByFieldType()
or other FiledInfo methods. And yes, it hardcodes ER because right now I cannot imagine that this can be used to other fields.Comment #38
amateescu CreditAttribution: amateescu commentedIn case we don't want to expand it with a $field_type argument, that means it has to live somewhere outside EntityManager :P
Comment #39
claudiu.cristeaOK. Here with $field_type but I still think that maybe we need to filter the whole list of fields, check if their field type plugin class descends from entity reference.
Comment #40
jibranThis method is specific to ER so it doesn't make sense to have it in EMI. How would DER going to override it? It's a helper method in a wrong class imo. We should move it to some other class or ER.module file.
Comment #41
yched CreditAttribution: yched commentedOuch - that's really unfortunate, we tried hard to remove dependencies of Core to field.module :-/.
Couldn't getFieldsReferencing() use $this->getFieldDefinitions() instead, and entity_reference_sync_fields() would then limit itself to configurable fields ?
Or, agreed with @jibran that putting ER-specific code in EntityManager feels a little off.
Also, what's the plan once e_r.module is gone ?
Comment #42
amateescu CreditAttribution: amateescu commentedOuch, I didn't realize the new
getFieldsReferencing()
is so specific to the internal structure of ER settings (i.e. it even filter by a specified target bundle) :( Now that I actually read its code, I definitely think it shouldn't live in EntityManager.How about this then:
- we move it somewhere inside field.module
- we drop the $target_bundle argument and let
entity_reference_sync_fields()
do the bundle filtering- we accept an array of target entity types
- we take into account that $storage->settings['target_type'] might be an array, which, even if that's not the case for the core ER field, it should make this helper useful to DER as well (I hope?)
Thoughts? :)
Comment #43
jibranI think moving it out of EntityManager is enough for DER. If we are making it specific for ER field then core shouldn't worry about contrib. As long as it's not in the EntityManager DER can always implements it's own hooks.
Comment #44
amateescu CreditAttribution: amateescu commentedLet's wait for #2412569: Allow setting the auto-create bundle on entity reference fields with multiple target bundles.
Comment #45
andypostThis test should live in taxonomy module
Comment #46
claudiu.cristeaI know, I know... we still have to deal with
auto_create_bundle
that is coming only in #2412569: Allow setting the auto-create bundle on entity reference fields with multiple target bundles but let's have some feedback here.EDIT: Because no one liked to add that field finder to Entity Manager, I created a new service that can accept additional methods in the interface in the future. I really see no place where to move that.
Comment #47
jibranWell it's a shame we don't fire an event when a bundle is renamed this all can live in a one class.
Comment #48
yched CreditAttribution: yched commentedNot sure if we really need a service for the "find field definitions referencing $entity_type + $bundle", but its current name & doc are a bit cryptic / misleading :
I'd more expect that to be something that finds references (like, find entities referencing some other entity, aka backreference) ?
Comment #49
amateescu CreditAttribution: amateescu commentedThat new method/service would probably make more sense as part of one the new services that will be created by this issue: #2337191: Split up EntityManager into many services
Comment #50
claudiu.cristeaFew comments:
Comment #51
yched CreditAttribution: yched commented+1
Comment #52
claudiu.cristea@amateescu, @yched, I'm OK with #49 but right now that is NR for 1 month and it's not a simple issue. We can expect some time till will go in. In the meantime it would be good if we can make some steps forward with this issue. So, let's move on with this and put back that 'finder' in EntityManager. If #2337191: Split up EntityManager into many services will be in first, then we'll update this. Otherwise that will take care to move this method in a new class.
What you think?
Comment #53
yched CreditAttribution: yched commentedMhyeah, I guess you're right ... :-)
Other remarks then :
- The logic in getFieldsReferencing() should leverage the field map (EntityManager::getFieldMap()) rather than loading all field storage config on earth :-)
- There's a question of "do we want all fields including base fields" or "only configurable fields" ?
The specific use case here only wants to update configurable fields, but it would be strange for a "generic getFieldsReferencing() method" to omit base fields ? (As a side note, the field map will get you all fields)
- Not sure statically caching (especially without a way to clear the cache) is a good thing. The field map is already statically cached.
Comment #54
claudiu.cristea@yched, thank you. Now re-reading older comments I found @jibran's comment #40. He, and also @amateescu, are saying that this is too ER specific to live in EntityManager (or its successors). And I'm tempted to agree. So #49 conflicts with #40.
I still think that we need a ER method (but where?) and since ER will move in core, we can add the new method directly in core. Course we include observations from #53.
Out of ideas :(
Comment #55
yched CreditAttribution: yched commentedWell, if a "FieldDefinitionsManager" was split off from EntityManager, then a method in there to find ER fields pointing to some entity-type + bundle could be OK IMO, ER fields are an important, structuring field type provided by Core.
Adding it in the already much crowded EntityManager is more debatable, true.
For the time being, do we really need to put that code in a reusable place ? Looks like the patch only calls the method once ?
Comment #56
claudiu.cristeaWe can make it a procedural helper in ER and in #2429191: Deprecate entity_reference.module and move its functionality to core we'll move it to system module (grrr, how ugly!)
Comment #57
claudiu.cristeaOr...
If #2553169: Dispatch bundle CRUD events would go in, we just add the event subscriber and make the 'finder' a protected method in that subscriber.
Comment #58
claudiu.cristea@yched,
I'm very tempted by #57 because is not forcing us to choose an unnatural home for
getFieldsReferencing()
. The event subscriber would be a good place for that. But then #2553169: Dispatch bundle CRUD events needs a review and commit. Is there a chance for that?Comment #59
claudiu.cristeaTagging.
Comment #60
claudiu.cristeaTagging.
Comment #61
claudiu.cristeaClosed #2571187: EntityReferenceItem should add the target_bundles as config dependencies in favor of this. And yes, we must handle the bundle entity removal through config dependencies.
Comment #62
amateescu CreditAttribution: amateescu commented@claudiu.cristea, in order to move forward here, let's just create a procedural function in e_r.module, then we can move it to a better place either in #2337191: Split up EntityManager into many services or #2429191: Deprecate entity_reference.module and move its functionality to core.
#53 still needs to be addressed so putting at NW for that.
Comment #63
claudiu.cristeaThis includes #53, #61, #62.
Comment #64
yched CreditAttribution: yched commentedClarity nitpick : s/$manager/$entity_manager ?
Not clear what the "manager" is when it's used 10 lines below ;-)
Clarity : s/$handler/$handler_settings ?
Sorry about those, but calling it a "handler" is kind of a lie :-)
$entity_manager
$handler_settings
Minor : the function could use a couple inline comments, 30 lines of code without a comment is usually not good :-)
Nitpick : s/$config/$field_config for clarity ?
Would be simpler as :
?
Is that really needed ? The memory should be freeable by GC the moment we leave the function and the variables get out of scope ?
I don't think we do similar things (unset vars used in the function before leaving it) in any other place ?
Nitpick : the "Sync" part refers to a function name that no longer exists, right ?
Why not just testTargetBundleChanges ?
Comment #65
yched CreditAttribution: yched commentedAlso : that is an awful lot of non-trivial code to repeat for anything that stores "a list of bundles for which some feature is enabled", which seems like a fairly common pattern :-/
Not for this issue to enhance, though...
Comment #66
yched CreditAttribution: yched commentedForgot : EntityReferenceItem::onDependencyRemoval() could use a couple inline comments (mirroring the ones delimiting code sections in calculateDependencies())
Comment #67
jibranCan we include #2479257: EntityReferenceItem::calculateDependencies() assumes $field_definition is FieldConfigBase here as well and close that as duplicate?
Comment #68
yched CreditAttribution: yched commented@jibran : see over there - that issue got fixed meanwhile :)
Comment #69
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedWorking on this.
Comment #70
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedFixes #64 and also brings back a very important part from the initial patches where we also react to bundles being deleted.
Comment #71
claudiu.cristeaThis is useless. We handle this now in
onDependencyRemoval()
. See the tests passing in my latest patch without this hook implementation :)Comment #73
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedIt is not useless at all :) We also need to take into account bundles that are not provided by a config entity type and therefore are not config dependencies.
Comment #74
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedPutting the reasoning from above in the code so it's clear for anyone else looking at it in the future. And we also have to log the problem when a config entity bundle is removed.
Comment #77
jibranPatch looks solid now.
<3 this clean up.
I get it that I have to update DERItem like ERItem. But do I have to add this hook to DER as well?
Can't this also happen in
entity_reference_entity_bundle_rename
?Thanks @yched for closing the issue as duplicate.
Comment #78
claudiu.cristeahook_entity_bundle_delete()
is invoked only by bundles based on config entities. At least in core. If a module wants to provide a bundle that is not a configurable, we needs to provide a mechanism for keep in sync entities.Comment #79
yched CreditAttribution: yched commented@claudiu.cristea : Yeah, we discussed that with @amateescu yesterday.
If an entity type has non-config bundles, then they are most probably hardcoded in code somewhere. If one of them is removed from the code, then the corresponding module should call hook_entity_bundle_delete() in a hook_post_update_N()
I hate the code duplication between onDependencyRemoval() and entity_reference_entity_bundle_delete(), but we couldn't find a way around that.
@jibran re #77.3 : I don't think that can happen, a rename cannot cause the set of bundles to become 0.
Comment #80
yched CreditAttribution: yched commentedNitpick : we could clarify that this is about adjusting the UI behavior.
Suggestion :
"If no checkboxes were checked for 'target_bundles', store NULL ("all bundles are referenceable") rather than empty array ("no bundle is referenceable" - typically happens when all referenceable bundles have been deleted).é
That works, but the NULL case could be a bit more explicit, for clarity.
Maybe :
?
Can we add short inline comments delimiting the two parts ("default values" & "target bundles"), like calculateDependencies() does ?
copy/paste breaks the 80 chars limit ;-)
Also, nitpick, but "warning" is an actual log level, and that's not the one we use here. So maybe just "Log a message if the field no longer has any referenceable bundle" ?
Comment #81
jibranThanks for the explanation @yched.
Can we add a test for non config entity types?
Comment #82
yched CreditAttribution: yched commentedHm, so FWIW, #2172843: Remove ability to update entity bundle machine names just got bumped to critical :-)
Comment #83
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedOk, since now we're sure that we won't allow bundle renames, let's take that part out of the patch. Also fixed #80 and added the test requested in #81.
Comment #84
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedClarifying the title.
Comment #86
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedOops.
Comment #88
yched CreditAttribution: yched commented@amateescu: Thanks !
Now that #2064191: Fix the 'target_bundle' field setting - 'target_bundles' are not validated when entity_reference is enabled got in, we probably want to adjust the code that it added in ERItem::getConstraints(), for the NULL / [] difference added in this patch here.
(if NULL, we don't want to add any Bundle constraint. If [], the Bundle constraint should be fine, since the in_array() check its validator does will always fail)
Comment #89
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThat's a very good point. Fixed :)
Comment #90
yched CreditAttribution: yched commentedYay ! RTBC if green :-)
Comment #91
jibranRTBC +1
Comment #93
yched CreditAttribution: yched commentedGee, those testbot fail notifications on old patches are painful :-/
Comment #94
pfrenssenGood job, I like that the potentially confusing difference between NULL and an empty array is spelled out very clearly in the documentation every time it is used.
Comment #96
jibranIt needs reroll as per https://www.drupal.org/pift-ci-job/44851
Comment #97
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedHere we go.
Comment #98
jibranLet's assign the issue to @alexpott for the review of config dependencies part of the patch.
Comment #99
alexpottComment #100
alexpottreferenceable to be consistent with the rest of core.
I think this should be parent::onDependencyRemoval().
Comment #101
yched CreditAttribution: yched commented@alexpott : Now that bundle renames are out of the equation, I'd think the patch on itself is not critical to get before RC1, and could go in as a bugfix after that.
But it's blocking #2412569: Allow setting the auto-create bundle on entity reference fields with multiple target bundles, which would really need to get in before RC since it implies config/schema changes.
Added that to the IS
Comment #102
jibranFixed #100. IS still needs an update as per #100.
Comment #103
claudiu.cristeaThis is somehow superfluous. If $handler_settings['target_bundles'] is NULL both expressions are FALSE, so the second doesn't make any sense --
isset()
should be enough. Maybe we can test if that is an array?Comment #104
yched CreditAttribution: yched commented@jibran
I did that in #101, but forgot to remove the tag.
@claudiu.cristea :
Correct. This should fix it.
Comment #105
jibranBack to RTBC as #100 got addressed.
PS: I only made a doc fix and a typo fix so I think I'm eligible to RTBC it.
Comment #106
alexpottThis is a good bug fix to make before the rc deadline and it has test coverage. Sites will be more robust after a bundle delete if they use entity reference fields.
I was wondering about the upgrade path - but I think sites that are in this state will already have fixed their problematic er field or it will have broken them - so I think it is okay for this to not have an upgrade path.
Committed fa0af0b and pushed to 8.0.x. Thanks!
I discussed this with @amateescu and @yched in Barcelona therefore adding myself to the issue credit list.
Comment #108
yched CreditAttribution: yched commentedThnaks @alexpott. I'll unpostpone and reroll #2412569: Allow setting the auto-create bundle on entity reference fields with multiple target bundles