#2405469: FileFormatterBase should extend EntityReferenceFormatterBase has a case where ERFormatter needs to assign a custom runtime property on the entity being referenced, containing the ERItem that references it :
$entity->_referringItem = $item
The problem is that ContentEntityBase::__set($name, $value) unconditionally downcasts $value to $value->getValue() before actually assigning it if $value happens to be a TypedData.
--> I assign a FieldItem, but what consuming code reads back is an array.
Downcasting to ->getValue() makes sense if the value is going to be assigned to a FieldItemList that will "re-upcast" it - i.e $name is the name of a "Field API field". For non-Field properties, we should just assign the value as we receive it.
| Comment | File | Size | Author |
|---|---|---|---|
| #20 | d8_ceb_set.interdiff.txt | 1.75 KB | fago |
| #20 | d8_ceb_set.patch | 5.04 KB | fago |
| #6 | interdiff.txt | 1.19 KB | yched |
| #6 | 2426509-ContentEntity_set_TypedData-6.patch | 5.05 KB | yched |
| #1 | 2426509-ContentEntity_set_TypedData-1.patch | 4.53 KB | yched |
Comments
Comment #1
yched commentedPatch, with test.
Comment #3
yched commentedTest-only patch failed as it should,
Patch passed as it should.
Up for reviews :-)
Comment #4
jibranPatch look good to me but can't RTBC this is way over my head.
Comment #5
fagoI'm not sure on much this magic method is really called, but this adds at least two method calls to every invocation. Should we check this isn't adding a performance regression?
Alternatively, could we just support down-casting objects of FieldItemListInterface ? I don't think we have to support something else, this just makes sure we can do:
$node->title = $node2->title;
I've no idea while there is the check for EntityInterface here in the first place.
Comment #6
yched commentedThanks for the review!
1) Agreed, that was actually on my mental todo list here :-)
__set() is the mirror of __get(), and that one has does an "Inline getFieldDefinition for speed" trick, so it makes sense to reproduce that in __set() too.
Due to the "Support setting values via TypedData objects" part, which is specific to __set(), we can't have the exact same logic order between the two functions - no biggie.
2) "downcast any TypedDataInterface that isn't also an EntityInterface" : honestly, that puzzled me too :-) It's not completely clear to me what we want to explicitely support now.
I think I'd agree with "only downcast FieldItemListInterface objects that get passed for assignment". That way, we support copying values, with:
but not anything weirder. Anything else gets passed as is to ItemList->setValue, and if not an array, gets assigned to the first property of the first item.
But then, we have similar downcasting at the lower levels :
- in ItemList::offsetSet()/set(), which supports copying item values (
$e->field[0] = $e->field[1]). It only downcasts if TypedDataInterface, because there's no generic interface for "a ListItem". Should FieldItemList override that to only downcast if FieldItemInterface ?- in FieldItemBase::__set() : same logic as on ContentEntity::__set() : downcast if (TypedDataInterface && !EntityInterface)
I don't clearly get what this one is for, nor what it allows or forbids ? If still relevant, then just like what we're fixing in ContentEntity, shouldn't this only apply to "officially defined properties in the Item" ?
(also, the comment there explicitely mentions that the EntityInterface check is there because "the 'entity' property (of EntityRef fields) is typed data also" - which is no longer the case ;-)
So... patch only takes care of point 1) for now, not sure what we want to do about 2) in ContentEntityBase::__set(), if we want to adjust the lower levels accordingly, and if we do it here or in a separate issue.
Comment #7
yched commentedAlso, since this blocks #2405469: FileFormatterBase should extend EntityReferenceFormatterBase which is critical, adding the tag
Comment #8
effulgentsia commentedIf #7 is true, raising this priority to match that one.
Comment #9
wim leersSo… not sure what's left to be done here? fago needs to review/RTBC?
Comment #10
yched commentedYeah, so IMO:
- the blocker/critical fix here should be limited to the scope of patch #6.
- the side topic discussed #5.2 and #6.2 definitely raise interesting flags, but it's better off in a dedicated issue.
Would be nicer to have @fago's approval, but I honestly don't think committing patch #6 would be risky or constroversial either :-)
Comment #11
larowlanwould putting this before the
result in any speed up? Would that mirror the existing code more and save the lookup?
out of scope but this could go first in the function and then we could exit early
Comment #12
yched commentedre #11
1. That would require us to duplicate the '// Support setting values via property objects." code block, I don't think that's worth the micro-perf gain
2. I think getting / setting field values happens much more often that getting / setting the 'translations' property ?
Comment #13
larowlan+1 rtbc then
Comment #14
wim leersPer #10 & #13.
Comment #15
alexpottlol
Isn't the point here that it results in the the same field object?
Comment #16
yched commented1. I know, I hate myself for this :-)
2. No, as discussed in #6.2, the intent is not to actually move a FieldItemList *object* across different entities (that would have very weird effects, since a FieldItemList references its parent entity), but just to copy *field values* :
That's a very early feature of the "magic-OO Entity Field API" in D8, this patch here doesn't change it.
Temptatively kicking back to @alexpott
Comment #17
alexpottTesting for difference using === is super weird to me.
Comment #18
yched commentedYou mean you'd perefer spl_object_hash() ?
Also, note that === is what the test currently uses in HEAD :
$this->assertTrue($entity->name !== $entity2->name)I'm just inverting the boolean logic to make it symmetrical with the test I'm adding for the behavior on non-field property, but using === for object identity is not introduced by this patch :-)
Comment #19
wim leersBack to RTBC to get feedback from Alex RE: #18.
Comment #20
fagoI think what's puzzling about the line is that the message assertion is wrong. It's actually checking that a non-field property is not touched, thus is identical. Adjusted it.
Whatever reason introduced this, it does not make sense any more as EntityInterface does not extend TypedDataInterface any more. Thus removed the needless double-check.
Else, patch looks good to me also.
ad #6.2: Yeah, this sounds like a separate issue to me as we should ensure it stays consistent across all levels.
Comment #21
yched commentedGood point about the assertion message, unfortunate copy/paste.
About the "instanceof EntityInterface" check : we'll also need to remove it from FieldItemBase::__set(), so I was keeping this for the issue where we unify those downcasts on the three levels, but sure, we can do it here as well :-)
Comment #22
yched commentedBack to RTBC then, to get feedback from Alex RE: #18
+ opened #2430843: Clarify/unify the downcast logic in ContentEntityBase::__set() / ItemList::set() / FieldItemBase::__set() to figure out the questions raised in #5.2 and #6.2
Comment #23
alexpottThis issue addresses a critical bug and is allowed per https://www.drupal.org/core/beta-changes. Committed 1420b77 and pushed to 8.0.x. Thanks!