Problem/Motivation
In #2772979: Enforcing a cloned entity translation to be new propagates to the original entity a reference problem with the enforceIsNew
flag on content entities was detected.
The current issue was originally about the newRevision
flag, which has the same problem, but it has been merged into #2675010: Cloned entity will point to the same field objects if the clone was created after an entity translation has been initialized and it is now about the still existing problems with $entityKeys and $translatableEntityKeys.
Steps to reproduce
- Create an entity with a translation
- Clone the entity or the entity translation.
- Change a non-translatable or translatable entity field e.g. uuid or label on the cloned entity object.
- Retrieve the entity key value through the local cache for entity keys e.g. through ::uuid() or ::label(), but first call the method on the original entity and then on the cloned entity.
- This results into calling one of those methods on the cloned entity into returning the value cached by original entity and not the new value.
Proposed resolution
Break the reference of the entityKeys
and translatableEntityKeys
local cache properties in ContentEntityBase::__clone()
.
Remaining tasks
Review.
User interface changes
None
API changes
None
Data model changes
None
Comment | File | Size | Author |
---|---|---|---|
#33 | interdiff-27-33.txt | 1.6 KB | hchonov |
#33 | 2828073-33.patch | 3.49 KB | hchonov |
#27 | interdiff-26-27.txt | 1.05 KB | hchonov |
#27 | 2828073-27.patch | 3.37 KB | hchonov |
#26 | interdiff-25-26.txt | 2.05 KB | hchonov |
Comments
Comment #2
tstoecklerHere's an initial patch, just so I can include a reference to the patch in our project.
Will add docs + test and also try to figure out if any more things are affected by the same problem.
Comment #4
tstoecklerComment #7
joelpittetTestbot failure, resetting status due to https://www.drupal.org/node/2828045
Comment #8
tstoecklerComment #9
tstoecklerHah, so this is actually postponed on #2675010: Cloned entity will point to the same field objects if the clone was created after an entity translation has been initialized because it cannot be properly tested otherwise as
ContentEntityBase::isNewRevision()
also checksContentEntityBase::getRevisionId()
and the field value of the revision ID gets unset in the clone and because of that issue it is also unset in the original.Attached patch should fail and will pass once that is in.
Comment #11
tstoecklerComment #12
tstoecklerAlso in terms of other things that need updating, I checked...
Anything is made a reference in
ContentEntityBase::initializeTranslation()
is potentially problematic. This is the list:-> Already covered
-> Handled by #2675010: Cloned entity will point to the same field objects if the clone was created after an entity translation has been initialized
-> Handled here
-> These are not handled yet.
So we should open a new issue for that. That will also be postponed on #2675010: Cloned entity will point to the same field objects if the clone was created after an entity translation has been initialized, though. Because all of those properties are "derivative" properties of
$this->fields
. I would still like to keep the current scope of this issue as this fixes an actually reproducible bug, whereas the others fix potential bugs, but there are no "known bugs" at the moment with them.Comment #13
hchonovOh my.. this means that the following will make a lot of things break:
$entity_clone = clone $entity; // Now they share the same $values property.
$entity_clone->set('some_field', 'new_value');
serialize($entity_clone); // This copies the field values to the $values property.
=> now the original entity has in $values the updated values from the clone....
Comment #14
tstoecklerHah, that's nice. I was about to post a comment, saying that $this->values is not problematic because it's never actually updated, but you are - as always - two steps ahead of me. I don't even want to contemplate the headache that will cause...
Comment #15
hchonovRe-roll after #2809227: Store revision id in originalRevisionId property to use after revision id is updated + made only one comment for the currently three properties we are cloning.
Comment #16
tstoecklerOK, so now that #2675010: Cloned entity will point to the same field objects if the clone was created after an entity translation has been initialized is taking care of the revision as well, let's repurpose to the other properties found in #12
Still postponed on that.
Comment #17
hchonov#2675010: Cloned entity will point to the same field objects if the clone was created after an entity translation has been initialized is already committed so this issue is no longer postponed, but the patch has to be re-rolled.
Comment #18
hchonovOh sorry, the patch actually has been completely merged into #2675010: Cloned entity will point to the same field objects if the clone was created after an entity translation has been initialized so closing as duplicate of it.
Comment #19
BerdirThe patch yes, but @tstoeckler changed the purpose of this to take care of the still remaining other things in #16.
Comment #20
hchonovYes I've realised that as well and was working on a patch for the new purpose of the issue before I re-open it again.. It is not my day, I guess :).
Comment #23
hchonovHere is a test only patch demonstrating the use case where the $entityKeys property is actually shared between cloned objects when having initalized a translation. This means that if we create a duplicate through the CEB::createDuplicateMethod of an entity which has initialized at least one translation other than the default one then the duplicated entity will have the same e.g. uuid value cached in the $entityKeys property and on save we will get an storage exception for inserting a duplicate entry into the entity shared tables. As this does not have any workaround I think it classifies as major now.
Comment #24
hchonovComment #25
hchonovAnd here the fix for the entity keys.
Comment #26
hchonovExtending the test to show that the problem exists not only with the local cache property $entityKeys but also with the local cache property $translatableEntityKeys.
Comment #27
hchonovAnd here the fix for the translatableEntityKeys local cache property.
Comment #28
hchonovComment #29
hchonovComment #32
tstoecklerThanks!! Looks great to me, I just have one quibble with the test before marking it RTBC.
Let's not use a random string here and compare that with a fixed string. Even if they will never be equal (due to the different length) this makes the test a bit harder to understand than necessary. Let's just use a fixed string in both places.
Same could be said about the UUID, actually, so maybe we can just use fixed values there as well.
Comment #33
hchonovRefactoring the test to make it easier to understand.
Comment #34
tstoecklerPerfect, thank you!
Note: I did upload some patches earlier in this issue, but that was when this issue had a related but completely distinct scope, so I did not myself work on any of the code in the latest patch.
Comment #35
alexpottHow many bugs do we have cloning entities?
Committed and pushed e65e326 to 8.4.x and f7f3c5b to 8.3.x. Thanks!
As this in a bc-compatible bugfix - committed to 8.3.x too.
Comment #38
hchonovJust a couple left :). The one is #2862463: Cloning an entity with initialized translations leaves $values pointing to the old entity object and @tstoeckler will be opening one more because of the default revision property :).