Commit 151050c7 authored by alexpott's avatar alexpott

Issue #2841311 by hchonov, tstoeckler: Initialized fields of an entity clone...

Issue #2841311 by hchonov, tstoeckler: Initialized fields of an entity clone have a reference to the original entity object instead to the cloned entity object
parent 559e61a4
......@@ -1073,35 +1073,29 @@ public function __clone() {
$this->typedData = NULL;
$definitions = $this->getFieldDefinitions();
// Ensure the fields array is actually cloned by overwriting the original
// reference with one pointing to a copy of the array.
$fields = $this->fields;
$this->fields = &$fields;
// The translation cache has to be cleared before cloning the fields
// below so that the call to getTranslation() does not re-use the
// translation objects of the old entity but instead creates new
// translation objects from the newly cloned entity. Otherwise the newly
// cloned field item lists would hold references to the old translation
// objects in their $parent property after the call to setContext().
$this->clearTranslationCache();
foreach ($this->fields as $name => $values) {
$this->fields[$name] = array();
// Untranslatable fields may have multiple references for the same field
// object keyed by language. To avoid creating different field objects
// we retain just the original value, as references will be recreated
// later as needed.
if (!$definitions[$name]->isTranslatable() && count($values) > 1) {
$values = array_intersect_key($values, array(LanguageInterface::LANGCODE_DEFAULT => TRUE));
}
foreach ($values as $langcode => $items) {
$this->fields[$name][$langcode] = clone $items;
$this->fields[$name][$langcode]->setContext($name, $this->getTranslation($langcode)->getTypedData());
}
}
// Because the new translation objects that are created below are
// themselves created by *cloning* the newly cloned entity we need to
// make sure that the references to property values are properly cloned
// before cloning the fields. Otherwise calling
// $items->getEntity()->isNew(), for example, would return the
// $enforceIsNew value of the old entity.
// Ensure the translations array is actually cloned by overwriting the
// original reference with one pointing to a copy of the array.
$this->clearTranslationCache();
$translations = $this->translations;
$this->translations = &$translations;
// Ensure that the following properties are actually cloned by
// overwriting the original references with ones pointing to copies of
// them: enforceIsNew, newRevision and loadedRevisionId.
// them: enforceIsNew, newRevision, loadedRevisionId and fields.
$enforce_is_new = $this->enforceIsNew;
$this->enforceIsNew = &$enforce_is_new;
......@@ -1110,6 +1104,24 @@ public function __clone() {
$original_revision_id = $this->loadedRevisionId;
$this->loadedRevisionId = &$original_revision_id;
$fields = $this->fields;
$this->fields = &$fields;
foreach ($this->fields as $name => $values) {
$this->fields[$name] = array();
// Untranslatable fields may have multiple references for the same field
// object keyed by language. To avoid creating different field objects
// we retain just the original value, as references will be recreated
// later as needed.
if (!$definitions[$name]->isTranslatable() && count($values) > 1) {
$values = array_intersect_key($values, array(LanguageInterface::LANGCODE_DEFAULT => TRUE));
}
foreach ($values as $langcode => $items) {
$this->fields[$name][$langcode] = clone $items;
$this->fields[$name][$langcode]->setContext($name, $this->getTranslation($langcode)->getTypedData());
}
}
}
}
......
......@@ -2,6 +2,7 @@
namespace Drupal\KernelTests\Core\Entity;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\entity_test\Entity\EntityTestMul;
use Drupal\entity_test\Entity\EntityTestMulRev;
use Drupal\language\Entity\ConfigurableLanguage;
......@@ -43,8 +44,15 @@ public function testFieldEntityReferenceAfterClone() {
'user_id' => $user->id(),
'language' => 'en',
]);
$translation = $entity->addTranslation('de');
$clone = clone $entity->addTranslation('de');
// Initialize the fields on the translation objects in order to check that
// they are properly cloned and have a reference to the cloned entity
// object and not to the original one.
$entity->getFields();
$translation->getFields();
$clone = clone $translation;
$this->assertEqual($entity->getTranslationLanguages(), $clone->getTranslationLanguages(), 'The entity and its clone have the same translation languages.');
......@@ -55,10 +63,12 @@ public function testFieldEntityReferenceAfterClone() {
if ($field->getFieldDefinition()->isTranslatable()) {
$args = ['%field_name' => $field_name, '%langcode' => $langcode];
$this->assertEqual($langcode, $field->getEntity()->language()->getId(), format_string('Translatable field %field_name on translation %langcode has correct entity reference in translation %langcode after cloning.', $args));
$this->assertSame($translation, $field->getEntity(), new FormattableMarkup('Translatable field %field_name on translation %langcode has correct reference to the cloned entity object.', $args));
}
else {
$args = ['%field_name' => $field_name, '%langcode' => $langcode, '%default_langcode' => $default_langcode];
$this->assertEqual($default_langcode, $field->getEntity()->language()->getId(), format_string('Non translatable field %field_name on translation %langcode has correct entity reference in the default translation %default_langcode after cloning.', $args));
$this->assertSame($translation->getUntranslated(), $field->getEntity(), new FormattableMarkup('Non translatable field %field_name on translation %langcode has correct reference to the cloned entity object in the default translation %default_langcode.', $args));
}
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment