diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php index aae2896..38005dc 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php @@ -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 @@ -533,6 +533,19 @@ public function getFields($include_computed = TRUE) { /** * {@inheritdoc} */ + public function getTranslatableFields($include_computed = TRUE) { + $fields = array(); + foreach ($this->getFieldDefinitions() as $name => $definition) { + if (($include_computed || !$definition->isComputed()) && $definition->isTranslatable()) { + $fields[$name] = $this->get($name); + } + } + return $fields; + } + + /** + * {@inheritdoc} + */ public function getIterator() { return new \ArrayIterator($this->getFields()); } @@ -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()); } } diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php index c3ae6a3..7c4aa07 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php @@ -405,11 +405,23 @@ 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) { - // 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}(); + // Call the field methods for non translatable fields only once and on the + // default entity translation. + if ($translation->isDefaultTranslation()) { + foreach ($translation->getFields() 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}(); + } + } + else { + foreach ($translation->getTranslatableFields() 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}(); + } } + } return $result; } diff --git a/core/lib/Drupal/Core/Entity/FieldableEntityInterface.php b/core/lib/Drupal/Core/Entity/FieldableEntityInterface.php index 2e5cce7..a83548b 100644 --- a/core/lib/Drupal/Core/Entity/FieldableEntityInterface.php +++ b/core/lib/Drupal/Core/Entity/FieldableEntityInterface.php @@ -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. @@ -186,6 +186,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. * * Note that this is invoked after any changes have been applied. diff --git a/core/modules/entity_reference/src/Tests/EntityReferenceFieldTranslatedReferenceViewTest.php b/core/modules/entity_reference/src/Tests/EntityReferenceFieldTranslatedReferenceViewTest.php index 648920b..f72a4de 100644 --- a/core/modules/entity_reference/src/Tests/EntityReferenceFieldTranslatedReferenceViewTest.php +++ b/core/modules/entity_reference/src/Tests/EntityReferenceFieldTranslatedReferenceViewTest.php @@ -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; diff --git a/core/modules/system/src/Tests/Entity/ContentEntityCloneTest.php b/core/modules/system/src/Tests/Entity/ContentEntityCloneTest.php new file mode 100644 index 0000000..7f7a0a7 --- /dev/null +++ b/core/modules/system/src/Tests/Entity/ContentEntityCloneTest.php @@ -0,0 +1,71 @@ +save(); + + $this->installEntitySchema('entity_test_mul'); + } + + /** + * Tests correct entity reference on fields after clone. + */ + public function testFieldEntityReferenceAfterClone() { + $user = $this->createUser(); + + // Create some test entities. + $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()) { + $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.', + array('%field_name' => $field_name, '%langcode' => $langcode))); + } + else { + $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.', + array('%field_name' => $field_name, '%langcode' => $langcode, '%default_langcode' => $default_langcode))); + } + } + } + } +} diff --git a/core/modules/system/src/Tests/Entity/EntityTranslationTest.php b/core/modules/system/src/Tests/Entity/EntityTranslationTest.php index 11ec589..5e1ebc5 100644 --- a/core/modules/system/src/Tests/Entity/EntityTranslationTest.php +++ b/core/modules/system/src/Tests/Entity/EntityTranslationTest.php @@ -769,4 +769,32 @@ function testEntityAdapter() { } } + /** + * Tests correct entity reference on fields after adding 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()) { + $this->assertEqual($langcode, $field->getEntity()->language()->getId(), format_string('Translatable field %field_name on translation %langcode has correct entity reference in translation %langcode.', + array('%field_name' => $field_name, '%langcode' => $langcode))); + } + else { + $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.', + array('%field_name' => $field_name, '%langcode' => $langcode, '%default_langcode' => $default_langcode))); + } + } + } + } } diff --git a/core/modules/taxonomy/src/Tests/TaxonomyTranslationTestTrait.php b/core/modules/taxonomy/src/Tests/TaxonomyTranslationTestTrait.php index 61e45bd..79e3fed 100644 --- a/core/modules/taxonomy/src/Tests/TaxonomyTranslationTestTrait.php +++ b/core/modules/taxonomy/src/Tests/TaxonomyTranslationTestTrait.php @@ -79,7 +79,7 @@ protected function enableTranslation() { /** * Adds term reference field for the article content type. */ - protected function setUpTermReferenceField() { + protected function setUpTermReferenceField($translatable = FALSE) { $handler_settings = array( 'target_bundles' => array( $this->vocabulary->id() => $this->vocabulary->id(), @@ -88,7 +88,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') diff --git a/core/modules/taxonomy/src/Tests/TermTranslationFieldViewTest.php b/core/modules/taxonomy/src/Tests/TermTranslationFieldViewTest.php index 9474d11..e9d2afe 100644 --- a/core/modules/taxonomy/src/Tests/TermTranslationFieldViewTest.php +++ b/core/modules/taxonomy/src/Tests/TermTranslationFieldViewTest.php @@ -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; }