The computed 'entity' property provides an easy way to load referenced entities one by one.
In many cases though (#2073033: Optimize file usage updates in file/image fields), it's more efficient to load all entities in all items in one single multiple-load.
| Comment | File | Size | Author |
|---|---|---|---|
| #57 | 2073661-psr4-reroll.patch | 12.13 KB | xjm |
| #55 | interdiff.txt | 903 bytes | amateescu |
| #55 | 2073661-55.patch | 12.49 KB | amateescu |
Comments
Comment #1
jibranTagging
Comment #2
yched commentedComment #3
amateescu commentedI think this is another one that should be postponed on #2047229: Make use of classes for entity field and data definitions as I don't see an easy way to get the 'target_type' setting in a list class..
Too bad though, I wanted to write a patch here and use it in #1605290: Enable entity render caching with cache tag support.
Comment #4
yched commentedYou mean how to access the 'target_type' from the Field class ?
Where is this 'target_type' defined ?
I mean, it's in the "field settings" for a ConfigurableEntityReferenceItem, but that one is only defined for configurable fields, so this is not the one we want, since we need this getEntities() method to also work on base fields (node.uid)
It has to available be somewhere else, since the computed 'entity' property knows how to load an entity ($node->uid->entity->name works somehow, right ?)
Also getEntities() is probably not the right name now that all Field / FieldItem classes have a getEntity() method for the *parent* entity. getReferencedEntities() ?
Comment #5
amateescu commentedThe setting is on the field item class (
\Drupal\Core\Entity\Plugin\DataType\EntityReferenceItem) and the list class (Drupal\Core\Entity\Field\Field) has only:And yes, I agree that it should be named getReferencedEntities().
Comment #6
yched commentedI see - it's in $this->definition['settings']['target_type'] for FieldItem, and the Field class can't read that since $definition is protected.
I think this means a public getTargetType() method on EntityReferenceItem, and then then EntityReferenceField::getReferencedEntities() can do $this->list[0]->getTargetType() ?
Comment #7
amateescu commentedThat's what I meant with "I don't see an easy way" above :) Sure, we could do that, or wait until
\Drupal\Core\Entity\Field\Field::getFieldDefinition()returns something useful..Comment #8
yched commentedTrue. I'd tend to think adding EntityReferenceItem::getTargetType() now would be a faster way forward though. The method makes sense anyway, IMO, and the sooner we have getReferencedEntities(), the better for establishing good performance practices.
We can always refactor the methods implementations based on FieldDefinitionInterface later on ?
Comment #9
amateescu commentedWell color me convinced :) This is what I have so far, but of course we can't have nice things like this because of PHP and the way we currently do base fields and configurable fields.
This only works for base fields (which are singe-valued anyway), because the list class for the configurable entity reference field must extend
\Drupal\field\Plugin\Type\FieldType\ConfigField, so it can't also extend the new EntityReferenceField class that I'm adding here :( The only solution seems to have two list classes, what a waste..ARGH!
Comment #10
yched commentedYup... :-(
Won't ConfigurableEntityReferenceItem need its own implementation of getTargetType() ?
Also, getReferencedEntities() should return an array keyed by delta, not entity ids.
Comment #11
berdirWe should probably add some sort of interface for these methods, so that a) code that uses it can easily check if a field has referenced entities (there could be a different implementation, e.g. a computed back-referenced field) and b) we can check if the item class implements that interface and fail with an exception instead of a fatal error (if something is mis-configured/defined).
getReferencedEntities() seems to break the naming pattern a bit (everything else uses target) but as outlined above, it could be used for different use cases so I think it makes sense.
Comment #12
claudiu.cristeaI used
::targetEntities()in #2073033-8: Optimize file usage updates in file/image fields. See @webchick comment from #2062573-37: Add method defaultConfiguration() in ConfigurablePluginInterface why the pattern "getFooBar" should be used mostly on getters.EDIT: And, yes, I propose to use the same name here as it's shorter, simpler and contains the "target" word.
Comment #13
claudiu.cristeaLet's see with this...
Comment #14
yched commentedfor the bot
Comment #15
yched commentedWe should try to reduce the number of exit points. The following should perform equally well (array_filter and foreach on empty arrays are basically no-ops):
For clarity, let's move that line above the place it's actually used a couple lines below.
Similarly, not sure we really need an early exit here. if there are ids but no corresponding entities, the foreach below is only a few cycles.
Streamlined code > microoptimizations IMO - but feel free to disagree.
There should be an isset(), $entities[$target_id] might not exist (if invalid / stale target_id)
Does this work for ConfigurableEntityReferenceFieldItems too ? or does that class need its own implementation of the method ?
Comment #16
claudiu.cristea@yched, on #15.5
As I can see
ConfigurableEntityReferenceFieldIteminheritsgetTargetType()fromEntityReferenceItemviaConfigEntityReferenceItemBase. I admit that I'm lost a little bit in "reference" jungle :)Comment #17
yched commentedSure, what I mean is I'm not sure the inherited method implementation works for config fields. Those will need $this->getFieldSettting('target_type') (the "definition" received by base fields and config fields is not unified yet).
Other than that, if both this issue and the "multiple file load" issue reach RTBC at the same time, we do need to settle on whether the method returns entities keyed by delta or entity id. I'd tend to think by delta, but I'm curious about what the code in the other issue would look like.
Comment #18
claudiu.cristea@yched,
Does the attached test answer your concerns?
No doubt here. We need to preserve deltas. Even this will not be used in Drupal 8 core but for sure will be a useful API tool for contrib modules development.
Comment #20
claudiu.cristeaThat error is raised because
EntityReferenceField(set as list class ofConfigurableEntityReferenceItem) extends\Drupal\Core\Entity\Field\Fieldbut that one has no valid::getFieldDefinition()method:I see that #1988612: Apply formatters and widgets to rendered entity base fields, starting with node.title deals with this. Shouldn't we postpone this till #1988612: Apply formatters and widgets to rendered entity base fields, starting with node.title?
Comment #21
yched commentedSee comment on #1605290-205: Enable entity render caching with cache tag support
Comment #22
amateescu commentedPostponing on #2004244: Move entity revision, content translation, validation and field methods to ContentEntityInterface. And we kinda agreed to name it consistently with what was introduced in #1605290: Enable entity render caching with cache tag support, so updating the title as well.
Comment #23
claudiu.cristea@amateescu,
Don't care really if is
targerEntities()orreferencedEntities()but it would be great, for consistency reasons, to unify the naming. Want to use the term "reference", "referenced"? OK, then let's unify the naming:target_idwill becomereference_id,$target_type=>$reference_type,::getTargetType()would be::getReferencedType()and so on.What you think, guys? Should I fill a followup?
Comment #24
yched commentedI for one can live with referencedEntities() in the API and 'target_id' as the property name.
Comment #25
amateescu commentedI'm not sure we want to go that far.. I think target_type and target_id are fine as they are, it's just targetEntities() that doesn't really make sense to me because it implies "possible target entities", while referencedEntities() clearly describes entities that are already referenced.
Comment #26
amateescu commented#2004244: Move entity revision, content translation, validation and field methods to ContentEntityInterface is in :)
Comment #27
amateescu commentedLots have changed in the meantime, but we still need two item list classes with duplicate code for that method. Also added an interface and replaced targetEntities() from the file item list class. I think this is the best we can do for now, sans traits or more unification of base and configurable list classes.
P.S. patch didn't apply so I have no interdiff, sorry :/
Comment #29
amateescu commentedExcept that this will never work because file does not depend on entity_reference.
The only possibility that I see is to have ConfigurableEntityReferenceFieldItemList provided by the field system instead of the ER module. Which doesn't sound so bad after all, looking as how the base EntityReference item is provided by Core already and D8 is all about entities and references :)
Comment #30
yched commentedNot sure FileItemList extends ConfigERFieldItemList is a great thing either, the latter should be really about e_r.
Ideally we'd have FileItemList extends (Core)/ERFieldItemList (just like e_r's ConfigERFieldItemList) ? Even if that causes some code duplication (provided it's not 200 duplicated LOC), that's better IMO.
(sorry, mostly talking out of my *ss here, cant easily look at actual code)
Comment #31
amateescu commentedThe problem there is that FileItemList needs to extend ConfigFieldItemList, because it's a configurable field, so there is code duplication whatever way you look at it.
The best approach would be to merge ConfigFieldItemList with core's FieldItemList, then everyone can go wild, but I'm not sure if that's possible at the moment :)
Comment #32
yched commentedYup, that's the goal eventually, so maybe let's duplicate *that* code for now ?
Comment #33
yched commentedAlso, the patch should update ContentEntityBase::referencedEntities() to use the new (multi-load) EntityReferenceFieldItemList::referencedEntities() added here, rather than individual mono-loads currently.
Comment #34
amateescu commentedDefinitely. I picked this up a couple of days ago but it's still ugly no matter which direction you take on duplicating code. Let's wait a bit for #2047229: Make use of classes for entity field and data definitions to land and another followup of it which I can't easily find right now.
Comment #35
daffie commentedComment #36
yched commentedWould be good to revive this. We do need to be able to multiple-load the entities referenced in a field
Comment #37
plopescHere is a re-roll from #27. Not sure about the new namespaces, but I think at least it works now.
I had to include a new setting about target_type in
FileItemandImageItemclasses to keep workingConfigurableEntityReferenceFieldItemList::referencedEntities().If works, then we can start to polish it :)
Regards.
Comment #39
plopescNew round, test failed because old
targetEntities()returned results using entity id as key, howeverreferencedEntities()returns results using delta as index. New patch processes it to match with the entity id.Regards.
Comment #40
yched commentedNot a huge fan of having to do this now... that was abstracted out by the getTargetType() method in earlier iterations (removed by @amateescu in #27)
But well, I guess other "generic entity ref handling" code might be caught by the absence of a $definition->getSetting('target_type') on File / Image fields. @effulgentsia once argued that if a field type extends another, it should include all the parent settings, and he's probably right.
If we do that, we need to update the field.*.settings schema entries in file.schema.yml / image.schema.yml for the new setting...
Comment #41
plopescAdding reference to targte_type setting in
field.schema.ymlandimage.schema.ymlfiles.About the existence of the fixed target_type setting or not in file and image field types, IMHO a fixed setting could be converted into a property. However, if a field type have to inherit all the settings from its parent, we should proceed on this way.
Regards.
Comment #42
yched commentedConfigurableEntityReferenceFieldItemList moving from
core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/Field/FieldType
to
core/lib/Drupal/Core/Field/Plugin/Field/FieldType
feels a bit weird, even though I get why it's done that way. Config*Item* classes suck :-/.
I think we should be able to ship a single EntityReferenceFieldItemList class extending ConfigFieldItemList, and use it for both core EntityReferenceItem & entity_ref ConfigurableEntityReferenceItem (would just require a small adjustment to getDefaultValue() so that it doesn't assume it *will* find 'target_uuid' entries).
Probably best left for another patch though.
I'd rather let @amateescu chime in when he comes back.
Comment #43
amateescu commentedI would go even a bit further and say that I'd prefer this patch to happen only after we get rid of ConfigFieldItemList completely..
What we're doing here is needlessly ugly and almost all the code from the current patch will need to be updated for that, so I'm not sure why we need to rush it for this single method.
Comment #44
berdir41: 2073661-41.patch queued for re-testing.
Comment #46
amateescu commentedSince the changes mentioned in #43 happened in the meatime (no more ConfigFieldItemList), this patch actually makes sense now :)
Comment #48
amateescu commentedShould be better now.
Comment #49
yched commentedThanks a lot for reviving this !
Minor : could also be if (empty()) ? (obvious & fast opt-out in case of empty field ?)
Could we avoid such a "false setting" by basing referencedEntities() on a protected getreferencedEntityType(), that Term / File / Image ref field types would simply override to return the (hardcoded) entity_type they're about ?
(I have a weird feeeling of deja-vu here, maybe we discussed that already earlier on)
An 'entity_type' setting makes sense for "flexible" reference fields like EntityRef / ConfigurableEntityRef.
But it feels weird to force "mono/hardcoded entity type" reference field types like term, file, image, to have such a "setting" (all the more if it's actually stored in config and thus actually "changeable")
Comment #50
amateescu commented1. Sure thing, fixed.
2. That setting for file/image/term ref fields was actually introduced quite some time ago and this patch just kept the schema changes for it. I think we should open a different issue to discuss this point because it feels a bit out of scope here. I removed the schema changes from the patch so we can focus only on what we want to do in this issue :)
(FWIW, I agree with you that it feels wrong to have 'target_type' as a setting on those field types, that why I hardcoded it initially in their
propertyDefinitions()method, but, if my memory serves well, @fago or @Berdir didn't like that very much)Comment #51
yched commentedOh, right, I skipped the fact that the 'target_type' setttings are not added by the patch here.
So yeah, agreed that it's then not the task of this patch to add them to the schema.
Patch #50 works for me - just : looks like TaxonomyFormatterBase::prepareView() could now use referencedEntities() as well, instead of the manual multiple load it currently does ?
Comment #52
amateescu commentedYup, that cleans things up a bit :)
Comment #55
amateescu commentedNow with less #fail.
Comment #56
yched commentedNeat !
Comment #57
xjmReroll for #2247991: [May 27] Move all module code from …/lib/Drupal/… to …/src/… for PSR-4.
Comment #59
amateescu commented57: 2073661-psr4-reroll.patch queued for re-testing.
Comment #60
amateescu commentedComment #61
alexpottCommitted 3ffcebc and pushed to 8.x. Thanks!