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

  1. Create an entity with a translation
  2. Clone the entity or the entity translation.
  3. Change a non-translatable or translatable entity field e.g. uuid or label on the cloned entity object.
  4. 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.
  5. 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

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

tstoeckler created an issue. See original summary.

tstoeckler’s picture

Here'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.

Status: Needs review » Needs work

The last submitted patch, 2: 2828073-2.patch, failed testing.

tstoeckler’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 2: 2828073-2.patch, failed testing.

The last submitted patch, 2: 2828073-2.patch, failed testing.

joelpittet’s picture

Status: Needs work » Needs review

Testbot failure, resetting status due to https://www.drupal.org/node/2828045

tstoeckler’s picture

tstoeckler’s picture

FileSize
3.52 KB
3.52 KB

Hah, 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 checks ContentEntityBase::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.

Status: Needs review » Needs work

The last submitted patch, 9: 2828073-9.patch, failed testing.

tstoeckler’s picture

Status: Needs work » Postponed
tstoeckler’s picture

Also 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:

    $translation->translations = &$this->translations;
    $translation->enforceIsNew = &$this->enforceIsNew;

-> Already covered

    $translation->fields = &$this->fields;

-> Handled by #2675010: Cloned entity will point to the same field objects if the clone was created after an entity translation has been initialized

    $translation->newRevision = &$this->newRevision;

-> Handled here

    $translation->values = &$this->values;
    $translation->entityKeys = &$this->entityKeys;
    $translation->translatableEntityKeys = &$this->translatableEntityKeys;

-> 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.

hchonov’s picture

Oh 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....

tstoeckler’s picture

Hah, 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...

hchonov’s picture

Version: 8.2.x-dev » 8.3.x-dev
FileSize
3.81 KB
1.74 KB

Re-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.

tstoeckler’s picture

Title: Enforcing a cloned entity translation to be a new revision propagates to the original entity » Cloning an entity leaves $values, $entityKeys and $translatableEntityKeys pointing to the old object
Issue tags: +Needs issue summary update

OK, 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.

hchonov’s picture

Status: Postponed » Needs work

#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.

hchonov’s picture

Status: Needs work » Closed (duplicate)

Oh 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.

Berdir’s picture

The patch yes, but @tstoeckler changed the purpose of this to take care of the still remaining other things in #16.

hchonov’s picture

Status: Closed (duplicate) » Needs review
FileSize
1.13 KB

Yes 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 :).

Status: Needs review » Needs work

The last submitted patch, 20: 2828073-20.patch, failed testing.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

hchonov’s picture

Priority: Normal » Major
FileSize
1.61 KB

Here 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.

hchonov’s picture

Status: Needs work » Needs review
hchonov’s picture

And here the fix for the entity keys.

hchonov’s picture

Extending the test to show that the problem exists not only with the local cache property $entityKeys but also with the local cache property $translatableEntityKeys.

hchonov’s picture

And here the fix for the translatableEntityKeys local cache property.

hchonov’s picture

Title: Cloning an entity leaves $values, $entityKeys and $translatableEntityKeys pointing to the old object » Cloning an entity with initialized translations leaves $entityKeys and $translatableEntityKeys pointing to the old entity object
Issue summary: View changes
Issue tags: -Needs issue summary update
hchonov’s picture

Issue summary: View changes

The last submitted patch, 23: 2828073-23-test-only.patch, failed testing.

The last submitted patch, 26: 2828073-26-test-translatable-entity-keys.patch, failed testing.

tstoeckler’s picture

Status: Needs review » Needs work

Thanks!! Looks great to me, I just have one quibble with the test before marking it RTBC.

+++ b/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityCloneTest.php
@@ -179,4 +179,41 @@ public function testNewRevisionOnCloneEntityTranslation() {
+      'name' => $this->randomString(),
...
+    $clone->$label_field_name->value = 'test-change-label';

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.

hchonov’s picture

Status: Needs work » Needs review
FileSize
3.49 KB
1.6 KB

Refactoring the test to make it easier to understand.

tstoeckler’s picture

Status: Needs review » Reviewed & tested by the community

Perfect, 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.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

How 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.

  • alexpott committed e65e326 on 8.4.x
    Issue #2828073 by hchonov, tstoeckler: Cloning an entity with...

  • alexpott committed f7f3c5b on 8.3.x
    Issue #2828073 by hchonov, tstoeckler: Cloning an entity with...
hchonov’s picture

How many bugs do we have cloning entities?

Just 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 :).

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.