Skip to content
Snippets Groups Projects
Commit 0d06b250 authored by Alex Pott's avatar Alex Pott
Browse files

Issue #2513094 by hchonov, pfrenssen, mkalkbrenner, yched, Berdir:...

Issue #2513094 by hchonov, pfrenssen, mkalkbrenner, yched, Berdir: ContentEntityBase::getTranslatedField and ContentEntityBase::__clone break field reference to parent entity
parent 251ca21b
No related branches found
No related tags found
No related merge requests found
......@@ -489,7 +489,7 @@ protected function getTranslatedField($name, $langcode) {
if (isset($this->values[$name][$langcode])) {
$value = $this->values[$name][$langcode];
}
$field = \Drupal::service('plugin.manager.field.field_type')->createFieldItemList($this, $name, $value);
$field = \Drupal::service('plugin.manager.field.field_type')->createFieldItemList($this->getTranslation($langcode), $name, $value);
if ($default) {
// $this->defaultLangcode might not be set if we are initializing the
// default language code cache, in which case there is no valid
......@@ -530,6 +530,19 @@ public function getFields($include_computed = TRUE) {
return $fields;
}
/**
* {@inheritdoc}
*/
public function getTranslatableFields($include_computed = TRUE) {
$fields = [];
foreach ($this->getFieldDefinitions() as $name => $definition) {
if (($include_computed || !$definition->isComputed()) && $definition->isTranslatable()) {
$fields[$name] = $this->get($name);
}
}
return $fields;
}
/**
* {@inheritdoc}
*/
......@@ -1041,7 +1054,7 @@ public function __clone() {
}
foreach ($values as $langcode => $items) {
$this->fields[$name][$langcode] = clone $items;
$this->fields[$name][$langcode]->setContext($name, $this->getTypedData());
$this->fields[$name][$langcode]->setContext($name, $this->getTranslation($langcode)->getTypedData());
}
}
......
......@@ -405,7 +405,12 @@ protected function invokeFieldMethod($method, ContentEntityInterface $entity) {
$args = array_slice(func_get_args(), 2);
foreach (array_keys($entity->getTranslationLanguages()) as $langcode) {
$translation = $entity->getTranslation($langcode);
foreach ($translation->getFields() as $name => $items) {
// For non translatable fields, there is only one field object instance
// across all translations and it has as parent entity the entity in the
// default entity translation. Therefore field methods on non translatable
// fields should be invoked only on the default entity translation.
$fields = $translation->isDefaultTranslation() ? $translation->getFields() : $translation->getTranslatableFields();
foreach ($fields as $name => $items) {
// call_user_func_array() is way slower than a direct call so we avoid
// using it if have no parameters.
$result[$langcode][$name] = $args ? call_user_func_array([$items, $method], $args) : $items->{$method}();
......
......@@ -175,7 +175,7 @@ public function get($field_name);
public function set($field_name, $value, $notify = TRUE);
/**
* Gets an array of field item lists.
* Gets an array of all field item lists.
*
* @param bool $include_computed
* If set to TRUE, computed fields are included. Defaults to TRUE.
......@@ -185,6 +185,17 @@ public function set($field_name, $value, $notify = TRUE);
*/
public function getFields($include_computed = TRUE);
/**
* Gets an array of field item lists for translatable fields.
*
* @param bool $include_computed
* If set to TRUE, computed fields are included. Defaults to TRUE.
*
* @return \Drupal\Core\Field\FieldItemListInterface[]
* An array of field item lists implementing, keyed by field name.
*/
public function getTranslatableFields($include_computed = TRUE);
/**
* Reacts to changes to a field.
*
......
......@@ -188,7 +188,7 @@ protected function setUpEntityReferenceField() {
'entity_type' => $this->testEntityTypeName,
'type' => 'entity_reference',
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
'translatable' => FALSE,
'translatable' => TRUE,
'settings' => array(
'allowed_values' => array(
array(
......@@ -292,7 +292,7 @@ protected function createReferrerEntity() {
'langcode' => $this->baseLangcode,
));
$node->save();
$node->addTranslation($this->translateToLangcode, array());
$node->addTranslation($this->translateToLangcode, $node->toArray());
$node->save();
return $node;
......
<?php
/**
* @file
* Contains \Drupal\system\Tests\Entity\ContentEntityCloneTest.
*/
namespace Drupal\system\Tests\Entity;
use Drupal\entity_test\Entity\EntityTestMul;
use Drupal\language\Entity\ConfigurableLanguage;
/**
* Tests proper cloning of content entities.
*
* @group Entity
*/
class ContentEntityCloneTest extends EntityUnitTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['language', 'entity_test'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Enable an additional language.
ConfigurableLanguage::createFromLangcode('de')->save();
$this->installEntitySchema('entity_test_mul');
}
/**
* Tests if entity references on fields are still correct after cloning.
*/
public function testFieldEntityReferenceAfterClone() {
$user = $this->createUser();
// Create a test entity.
$entity = EntityTestMul::create([
'name' => $this->randomString(),
'user_id' => $user->id(),
'language' => 'en',
]);
$clone = clone $entity->addTranslation('de');
$this->assertEqual($entity->getTranslationLanguages(), $clone->getTranslationLanguages(), 'The entity and its clone have the same translation languages.');
$default_langcode = $entity->getUntranslated()->language()->getId();
foreach (array_keys($clone->getTranslationLanguages()) as $langcode) {
$translation = $clone->getTranslation($langcode);
foreach ($translation->getFields() as $field_name => $field) {
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));
}
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));
}
}
}
}
}
......@@ -769,4 +769,33 @@ function testEntityAdapter() {
}
}
/**
* Tests if entity references are correct after adding a new translation.
*/
public function testFieldEntityReference() {
$entity_type = 'entity_test_mul';
$controller = $this->entityManager->getStorage($entity_type);
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = $controller->create();
foreach ($this->langcodes as $langcode) {
$entity->addTranslation($langcode);
}
$default_langcode = $entity->getUntranslated()->language()->getId();
foreach (array_keys($entity->getTranslationLanguages()) as $langcode) {
$translation = $entity->getTranslation($langcode);
foreach ($translation->getFields() as $field_name => $field) {
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.', $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.', $args));
}
}
}
}
}
......@@ -78,8 +78,12 @@ protected function enableTranslation() {
/**
* Adds term reference field for the article content type.
*
* @param bool $translatable
* (optional) If TRUE, create a translatable term reference field. Defaults
* to FALSE.
*/
protected function setUpTermReferenceField() {
protected function setUpTermReferenceField($translatable = FALSE) {
$handler_settings = array(
'target_bundles' => array(
$this->vocabulary->id() => $this->vocabulary->id(),
......@@ -88,7 +92,7 @@ protected function setUpTermReferenceField() {
);
$this->createEntityReferenceField('node', 'article', $this->termFieldName, NULL, 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
$field_storage = FieldStorageConfig::loadByName('node', $this->termFieldName);
$field_storage->setTranslatable(FALSE);
$field_storage->setTranslatable($translatable);
$field_storage->save();
entity_get_form_display('node', 'article', 'default')
......
......@@ -50,7 +50,7 @@ protected function setUp() {
$this->vocabulary = $this->createVocabulary();
$this->enableTranslation();
$this->setUpTerm();
$this->setUpTermReferenceField();
$this->setUpTermReferenceField(TRUE);
$this->setUpNode();
}
......@@ -85,7 +85,7 @@ protected function setUpNode() {
'langcode' => $this->baseLangcode,
));
$node->save();
$node->addTranslation($this->translateToLangcode, array());
$node->addTranslation($this->translateToLangcode, $node->toArray());
$node->save();
$this->node = $node;
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment