function EntityTranslationTest::doTestEntityTranslationAPI
Same name in other branches
- 9 core/tests/Drupal/KernelTests/Core/Entity/EntityTranslationTest.php \Drupal\KernelTests\Core\Entity\EntityTranslationTest::doTestEntityTranslationAPI()
- 8.9.x core/tests/Drupal/KernelTests/Core/Entity/EntityTranslationTest.php \Drupal\KernelTests\Core\Entity\EntityTranslationTest::doTestEntityTranslationAPI()
- 11.x core/tests/Drupal/KernelTests/Core/Entity/EntityTranslationTest.php \Drupal\KernelTests\Core\Entity\EntityTranslationTest::doTestEntityTranslationAPI()
Executes the Entity Translation API tests for the given entity type.
Parameters
string $entity_type: The entity type to run the tests with.
1 call to EntityTranslationTest::doTestEntityTranslationAPI()
- EntityTranslationTest::testEntityTranslationAPI in core/
tests/ Drupal/ KernelTests/ Core/ Entity/ EntityTranslationTest.php - Tests the Entity Translation API behavior.
File
-
core/
tests/ Drupal/ KernelTests/ Core/ Entity/ EntityTranslationTest.php, line 322
Class
- EntityTranslationTest
- Tests entity translation functionality.
Namespace
Drupal\KernelTests\Core\EntityCode
protected function doTestEntityTranslationAPI($entity_type) {
$default_langcode = $this->langcodes[0];
$langcode = $this->langcodes[1];
$langcode_key = $this->entityTypeManager
->getDefinition($entity_type)
->getKey('langcode');
$default_langcode_key = $this->entityTypeManager
->getDefinition($entity_type)
->getKey('default_langcode');
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = $this->entityTypeManager
->getStorage($entity_type)
->create([
'name' => $this->randomMachineName(),
$langcode_key => LanguageInterface::LANGCODE_NOT_SPECIFIED,
]);
$entity->save();
$hooks = $this->getHooksInfo();
$this->assertEmpty($hooks, 'No entity translation hooks are fired when creating an entity.');
// Verify that we obtain the entity object itself when we attempt to
// retrieve a translation referring to it.
$translation = $entity->getTranslation(LanguageInterface::LANGCODE_NOT_SPECIFIED);
$this->assertFalse($translation->isNewTranslation(), 'Existing translations are not marked as new.');
$this->assertSame($entity, $translation, 'The translation object corresponding to a non-default language is the entity object itself when the entity is language-neutral.');
$entity->{$langcode_key}->value = $default_langcode;
$translation = $entity->getTranslation($default_langcode);
$this->assertSame($entity, $translation, 'The translation object corresponding to the default language (explicit) is the entity object itself.');
$translation = $entity->getTranslation(LanguageInterface::LANGCODE_DEFAULT);
$this->assertSame($entity, $translation, 'The translation object corresponding to the default language (implicit) is the entity object itself.');
$this->assertTrue($entity->{$default_langcode_key}->value, 'The translation object is the default one.');
// Verify that trying to retrieve a translation for a locked language when
// the entity is language-aware causes an exception to be thrown.
try {
$entity->getTranslation(LanguageInterface::LANGCODE_NOT_SPECIFIED);
$this->fail('A language-neutral translation cannot be retrieved.');
} catch (\LogicException $e) {
// Expected exception; just continue testing.
}
// Create a translation and verify that the translation object and the
// original object behave independently.
$name = $default_langcode . '_' . $this->randomMachineName();
$entity->name->value = $name;
$name_translated = $langcode . '_' . $this->randomMachineName();
$translation = $entity->addTranslation($langcode);
$this->assertTrue($translation->isNewTranslation(), 'Newly added translations are marked as new.');
$this->assertNotSame($entity, $translation, 'The entity and the translation object differ from one another.');
$this->assertTrue($entity->hasTranslation($langcode), 'The new translation exists.');
$this->assertEquals($langcode, $translation->language()
->getId(), 'The translation language matches the specified one.');
$this->assertEquals($langcode, $translation->{$langcode_key}->value, 'The translation field language value matches the specified one.');
$this->assertFalse($translation->{$default_langcode_key}->value, 'The translation object is not the default one.');
$this->assertEquals($default_langcode, $translation->getUntranslated()
->language()
->getId(), 'The original language can still be retrieved.');
$translation->name->value = $name_translated;
$this->assertEquals($name, $entity->name->value, 'The original name is retained after setting a translated value.');
$entity->name->value = $name;
$this->assertEquals($name_translated, $translation->name->value, 'The translated name is retained after setting the original value.');
// Save the translation and check that the expected hooks are fired.
$translation->save();
$hooks = $this->getHooksInfo();
$this->assertEquals($langcode, $hooks['entity_translation_create'], 'The generic entity translation creation hook has fired.');
$this->assertEquals($langcode, $hooks[$entity_type . '_translation_create'], 'The entity-type-specific entity translation creation hook has fired.');
$this->assertEquals($langcode, $hooks['entity_translation_insert'], 'The generic entity translation insertion hook has fired.');
$this->assertEquals($langcode, $hooks[$entity_type . '_translation_insert'], 'The entity-type-specific entity translation insertion hook has fired.');
// Verify that changing translation language causes an exception to be
// thrown.
try {
$translation->{$langcode_key}->value = $this->langcodes[2];
$this->fail('The translation language cannot be changed.');
} catch (\LogicException $e) {
// Expected exception; just continue testing.
}
// Verify that reassigning the same translation language is allowed.
try {
$translation->{$langcode_key}->value = $langcode;
} catch (\LogicException $e) {
$this->fail('The translation language can be reassigned the same value.');
}
// Verify that changing the default translation flag causes an exception to
// be thrown.
foreach ($entity->getTranslationLanguages() as $t_langcode => $language) {
$translation = $entity->getTranslation($t_langcode);
$default = $translation->isDefaultTranslation();
try {
$translation->{$default_langcode_key}->value = $default;
} catch (\LogicException $e) {
$this->fail('The default translation flag can be reassigned the same value.');
}
try {
$translation->{$default_langcode_key}->value = !$default;
$this->fail('The default translation flag cannot be changed.');
} catch (\LogicException $e) {
// Expected exception; just continue testing.
}
$this->assertEquals($default, $translation->{$default_langcode_key}->value);
}
// Check that after loading an entity the language is the default one.
$entity = $this->reloadEntity($entity);
$this->assertEquals($default_langcode, $entity->language()
->getId(), 'The loaded entity is the original one.');
// Add another translation and check that everything works as expected. A
// new translation object can be obtained also by just specifying a valid
// language.
$langcode2 = $this->langcodes[2];
$translation = $entity->addTranslation($langcode2);
$value = $entity !== $translation && $translation->language()
->getId() == $langcode2 && $entity->hasTranslation($langcode2);
$this->assertTrue($value, 'A new translation object can be obtained also by specifying a valid language.');
$this->assertEquals($default_langcode, $entity->language()
->getId(), 'The original language has been preserved.');
$translation->save();
$hooks = $this->getHooksInfo();
$this->assertEquals($langcode2, $hooks['entity_translation_create'], 'The generic entity translation creation hook has fired.');
$this->assertEquals($langcode2, $hooks[$entity_type . '_translation_create'], 'The entity-type-specific entity translation creation hook has fired.');
$this->assertEquals($langcode2, $hooks['entity_translation_insert'], 'The generic entity translation insertion hook has fired.');
$this->assertEquals($langcode2, $hooks[$entity_type . '_translation_insert'], 'The entity-type-specific entity translation insertion hook has fired.');
// Verify that trying to manipulate a translation object referring to a
// removed translation results in exceptions being thrown.
$entity = $this->reloadEntity($entity);
$translation = $entity->getTranslation($langcode2);
$entity->removeTranslation($langcode2);
foreach ([
'get',
'set',
'__get',
'__set',
'createDuplicate',
] as $method) {
try {
$translation->{$method}('name', $this->randomMachineName());
$this->fail("The {$method} method raises an exception when trying to manipulate a removed translation.");
} catch (\Exception $e) {
// Expected exception; just continue testing.
}
}
// Verify that deletion hooks are fired when saving an entity with a removed
// translation.
$entity->save();
$hooks = $this->getHooksInfo();
$this->assertEquals($langcode2, $hooks['entity_translation_delete'], 'The generic entity translation deletion hook has fired.');
$this->assertEquals($langcode2, $hooks[$entity_type . '_translation_delete'], 'The entity-type-specific entity translation deletion hook has fired.');
$entity = $this->reloadEntity($entity);
$this->assertFalse($entity->hasTranslation($langcode2), 'The translation does not appear among available translations after saving the entity.');
// Check that removing an invalid translation causes an exception to be
// thrown.
foreach ([
$default_langcode,
LanguageInterface::LANGCODE_DEFAULT,
$this->randomMachineName(),
] as $invalid_langcode) {
try {
$entity->removeTranslation($invalid_langcode);
$this->fail("Removing an invalid translation ({$invalid_langcode}) causes an exception to be thrown.");
} catch (\Exception $e) {
// Expected exception; just continue testing.
}
}
// Check that hooks are fired only when actually storing data.
$entity = $this->reloadEntity($entity);
$entity->addTranslation($langcode2);
$entity->removeTranslation($langcode2);
$entity->save();
$hooks = $this->getHooksInfo();
$this->assertTrue(isset($hooks['entity_translation_create']), 'The generic entity translation creation hook is run when adding and removing a translation without storing it.');
unset($hooks['entity_translation_create']);
$this->assertTrue(isset($hooks[$entity_type . '_translation_create']), 'The entity-type-specific entity translation creation hook is run when adding and removing a translation without storing it.');
unset($hooks[$entity_type . '_translation_create']);
$this->assertEmpty($hooks, 'No other hooks beyond the entity translation creation hooks are run when adding and removing a translation without storing it.');
// Check that hooks are fired only when actually storing data.
$entity = $this->reloadEntity($entity);
$entity->addTranslation($langcode2);
$entity->save();
$entity = $this->reloadEntity($entity);
$this->assertTrue($entity->hasTranslation($langcode2), 'Entity has translation after adding one and saving.');
$entity->removeTranslation($langcode2);
$entity->save();
$entity = $this->reloadEntity($entity);
$this->assertFalse($entity->hasTranslation($langcode2), 'Entity does not have translation after removing it and saving.');
// Reset hook firing information.
$this->getHooksInfo();
// Verify that entity serialization does not cause stale references to be
// left around.
$entity = $this->reloadEntity($entity);
$translation = $entity->getTranslation($langcode);
$entity = unserialize(serialize($entity));
$entity->name->value = $this->randomMachineName();
$name = $default_langcode . '_' . $this->randomMachineName();
$entity->getTranslation($default_langcode)->name->value = $name;
$this->assertEquals($name, $entity->name->value, 'No stale reference for the translation object corresponding to the original language.');
$translation2 = $entity->getTranslation($langcode);
$translation2->name->value .= $this->randomMachineName();
$this->assertNotEquals($translation->name->value, $translation2->name->value, 'No stale reference for the actual translation object.');
$this->assertEquals($entity, $translation2->getUntranslated(), 'No stale reference in the actual translation object.');
// Verify that deep-cloning is still available when we are not instantiating
// a translation object, which instead relies on shallow cloning.
$entity = $this->reloadEntity($entity);
$entity->getTranslation($langcode);
$cloned = clone $entity;
$translation = $cloned->getTranslation($langcode);
$this->assertNotSame($entity, $translation->getUntranslated(), 'A cloned entity object has no reference to the original one.');
$entity->removeTranslation($langcode);
$this->assertFalse($entity->hasTranslation($langcode));
$this->assertTrue($cloned->hasTranslation($langcode));
// Check that untranslatable field references keep working after serializing
// and cloning the entity.
$entity = $this->reloadEntity($entity);
$type = $this->randomMachineName();
$entity->getTranslation($langcode)->type->value = $type;
$entity = unserialize(serialize($entity));
$cloned = clone $entity;
$translation = $cloned->getTranslation($langcode);
$translation->type->value = strrev($type);
$this->assertEquals($cloned->type->value, $translation->type->value, 'Untranslatable field references keep working after serializing and cloning the entity.');
// Check that per-language defaults are properly populated. The
// 'entity_test_mul_default_value' entity type is translatable and uses
// entity_test_field_default_value() as a "default value callback" for its
// 'description' field.
$entity = $this->entityTypeManager
->getStorage('entity_test_mul_default_value')
->create([
'name' => $this->randomMachineName(),
'langcode' => $langcode,
]);
$translation = $entity->addTranslation($langcode2);
$expected = [
[
'shape' => "shape:0:description_{$langcode2}",
'color' => "color:0:description_{$langcode2}",
],
[
'shape' => "shape:1:description_{$langcode2}",
'color' => "color:1:description_{$langcode2}",
],
];
$this->assertEquals($expected, $translation->description
->getValue(), 'Language-aware default values correctly populated.');
$this->assertEquals($langcode2, $translation->description
->getLangcode(), 'Field object has the expected langcode.');
// Reset hook firing information.
$this->getHooksInfo();
}
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.