Revert "Issue #2382675 by plach, yched: ContentEntityBase::addTranslation() should not fire hook_entity_create()"
This reverts commit 58332b2637
.
8.0.x
parent
6edd7db4b1
commit
4f9265c85d
|
@ -69,6 +69,9 @@ class ConfigEntityType extends EntityType implements ConfigEntityTypeInterface {
|
|||
// Always add a default 'uuid' key.
|
||||
$this->entity_keys['uuid'] = 'uuid';
|
||||
$this->entity_keys['langcode'] = 'langcode';
|
||||
if (isset($this->handlers['storage'])) {
|
||||
$this->checkStorageClass($this->handlers['storage']);
|
||||
}
|
||||
$this->handlers += array(
|
||||
'storage' => 'Drupal\Core\Config\Entity\ConfigEntityStorage',
|
||||
);
|
||||
|
@ -132,12 +135,23 @@ class ConfigEntityType extends EntityType implements ConfigEntityTypeInterface {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \Drupal\Core\Config\Entity\ConfigEntityStorage.
|
||||
*
|
||||
* @throws \Drupal\Core\Config\Entity\Exception\ConfigEntityStorageClassException
|
||||
* Exception thrown when the provided class is not an instance of
|
||||
* \Drupal\Core\Config\Entity\ConfigEntityStorage.
|
||||
*/
|
||||
public function setStorageClass($class) {
|
||||
$this->checkStorageClass($class);
|
||||
parent::setStorageClass($class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the provided class is an instance of ConfigEntityStorage.
|
||||
*
|
||||
* @param string $class
|
||||
* The class to check.
|
||||
*
|
||||
* @see \Drupal\Core\Config\Entity\ConfigEntityStorage.
|
||||
*/
|
||||
protected function checkStorageClass($class) {
|
||||
if (!is_a($class, 'Drupal\Core\Config\Entity\ConfigEntityStorage', TRUE)) {
|
||||
throw new ConfigEntityStorageClassException("$class is not \\Drupal\\Core\\Config\\Entity\\ConfigEntityStorage or it does not extend it");
|
||||
|
|
|
@ -808,13 +808,6 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
return !empty($this->translations[$langcode]['status']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isNewTranslation() {
|
||||
return $this->translations[$this->activeLangcode]['status'] == static::TRANSLATION_CREATED;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -829,11 +822,37 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
throw new \InvalidArgumentException("The entity cannot be translated since it is language neutral ({$this->defaultLangcode}).");
|
||||
}
|
||||
|
||||
// Initialize the translation object.
|
||||
/** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
|
||||
$storage = $this->entityManager()->getStorage($this->getEntityTypeId());
|
||||
// Instantiate a new empty entity so default values will be populated in the
|
||||
// specified language.
|
||||
$entity_type = $this->getEntityType();
|
||||
|
||||
$default_values = array(
|
||||
$entity_type->getKey('bundle') => $this->bundle(),
|
||||
$this->langcodeKey => $langcode,
|
||||
);
|
||||
$entity = $this->entityManager()
|
||||
->getStorage($this->getEntityTypeId())
|
||||
->create($default_values);
|
||||
|
||||
foreach ($entity as $name => $field) {
|
||||
if (!isset($values[$name]) && !$field->isEmpty()) {
|
||||
$values[$name] = $field->getValue();
|
||||
}
|
||||
}
|
||||
$values[$this->langcodeKey] = $langcode;
|
||||
$values[$this->defaultLangcodeKey] = FALSE;
|
||||
|
||||
$this->translations[$langcode]['status'] = static::TRANSLATION_CREATED;
|
||||
return $storage->createTranslation($this, $langcode, $values);
|
||||
$translation = $this->getTranslation($langcode);
|
||||
$definitions = $translation->getFieldDefinitions();
|
||||
|
||||
foreach ($values as $name => $value) {
|
||||
if (isset($definitions[$name]) && $definitions[$name]->isTranslatable()) {
|
||||
$translation->values[$name][$langcode] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $translation;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,10 +13,7 @@ use Drupal\Core\Field\FieldDefinitionInterface;
|
|||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Base class for content entity storage handlers.
|
||||
*/
|
||||
abstract class ContentEntityStorageBase extends EntityStorageBase implements ContentEntityStorageInterface {
|
||||
abstract class ContentEntityStorageBase extends EntityStorageBase implements DynamicallyFieldableEntityStorageInterface {
|
||||
|
||||
/**
|
||||
* The entity bundle key.
|
||||
|
@ -90,32 +87,13 @@ abstract class ContentEntityStorageBase extends EntityStorageBase implements Con
|
|||
$bundle = $values[$this->bundleKey];
|
||||
}
|
||||
$entity = new $this->entityClass(array(), $this->entityTypeId, $bundle);
|
||||
$this->initFieldValues($entity, $values);
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes field values.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
|
||||
* An entity object.
|
||||
* @param array $values
|
||||
* (optional) An associative array of initial field values keyed by field
|
||||
* name. If none is provided default values will be applied.
|
||||
* @param array $field_names
|
||||
* (optional) An associative array of field names to be initialized. If none
|
||||
* is provided all fields will be initialized.
|
||||
*/
|
||||
protected function initFieldValues(ContentEntityInterface $entity, array $values = [], array $field_names = []) {
|
||||
// Populate field values.
|
||||
foreach ($entity as $name => $field) {
|
||||
if (!$field_names || isset($field_names[$name])) {
|
||||
if (isset($values[$name])) {
|
||||
$entity->$name = $values[$name];
|
||||
}
|
||||
elseif (!array_key_exists($name, $values)) {
|
||||
$entity->get($name)->applyDefaultValue();
|
||||
}
|
||||
if (isset($values[$name])) {
|
||||
$entity->$name = $values[$name];
|
||||
}
|
||||
elseif (!array_key_exists($name, $values)) {
|
||||
$entity->get($name)->applyDefaultValue();
|
||||
}
|
||||
unset($values[$name]);
|
||||
}
|
||||
|
@ -124,23 +102,7 @@ abstract class ContentEntityStorageBase extends EntityStorageBase implements Con
|
|||
foreach ($values as $name => $value) {
|
||||
$entity->$name = $value;
|
||||
}
|
||||
|
||||
// Make sure modules can alter field initial values.
|
||||
$this->invokeHook('field_values_init', $entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createTranslation(ContentEntityInterface $entity, $langcode, array $values = []) {
|
||||
$translation = $entity->getTranslation($langcode);
|
||||
$definitions = array_filter($translation->getFieldDefinitions(), function(FieldDefinitionInterface $definition) { return $definition->isTranslatable(); });
|
||||
$field_names = array_map(function(FieldDefinitionInterface $definition) { return $definition->getName(); }, $definitions);
|
||||
$values[$this->langcodeKey] = $langcode;
|
||||
$values[$this->getEntityType()->getKey('default_langcode')] = FALSE;
|
||||
$this->initFieldValues($translation, $values, $field_names);
|
||||
$this->invokeHook('translation_create', $entity);
|
||||
return $translation;
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Entity\ContentEntityStorageInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Entity;
|
||||
|
||||
/**
|
||||
* A storage that supports content entity types.
|
||||
*/
|
||||
interface ContentEntityStorageInterface extends DynamicallyFieldableEntityStorageInterface {
|
||||
|
||||
/**
|
||||
* Constructs a new entity translation object, without permanently saving it.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
|
||||
* The entity object being translated.
|
||||
* @param string $langcode
|
||||
* The translation language code.
|
||||
* @param array $values
|
||||
* (optional) An associative array of initial field values keyed by field
|
||||
* name. If none is provided default values will be applied.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\ContentEntityInterface
|
||||
* A new entity translation object.
|
||||
*/
|
||||
public function createTranslation(ContentEntityInterface $entity, $langcode, array $values = []);
|
||||
|
||||
}
|
|
@ -30,20 +30,4 @@ class ContentEntityType extends EntityType implements ContentEntityTypeInterface
|
|||
return 'content';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \Drupal\Core\Entity\ContentEntityStorageInterface.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* If the provided class does not implement
|
||||
* \Drupal\Core\Entity\ContentEntityStorageInterface.
|
||||
*/
|
||||
protected function checkStorageClass($class) {
|
||||
$required_interface = ContentEntityStorageInterface::class;
|
||||
if (!is_subclass_of($class, $required_interface)) {
|
||||
throw new \InvalidArgumentException("$class does not implement $required_interface");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -276,9 +276,6 @@ class EntityType implements EntityTypeInterface {
|
|||
$this->handlers += array(
|
||||
'access' => 'Drupal\Core\Entity\EntityAccessControlHandler',
|
||||
);
|
||||
if (isset($this->handlers['storage'])) {
|
||||
$this->checkStorageClass($this->handlers['storage']);
|
||||
}
|
||||
|
||||
// Automatically add the EntityChanged constraint if the entity type tracks
|
||||
// the changed time.
|
||||
|
@ -462,20 +459,9 @@ class EntityType implements EntityTypeInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function setStorageClass($class) {
|
||||
$this->checkStorageClass($class);
|
||||
$this->handlers['storage'] = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the provided class is an instance of ConfigEntityStorage.
|
||||
*
|
||||
* @param string $class
|
||||
* The class to check.
|
||||
*/
|
||||
protected function checkStorageClass($class) {
|
||||
// Nothing to check by default.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Entity\KeyValueStore\KeyValueContentEntityStorage.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Entity\KeyValueStore;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\ContentEntityStorageInterface;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
|
||||
/**
|
||||
* Provides a key value backend for content entities.
|
||||
*/
|
||||
class KeyValueContentEntityStorage extends KeyValueEntityStorage implements ContentEntityStorageInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function countFieldData($storage_definition, $as_bool = FALSE) {}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasData() {}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function purgeFieldData(FieldDefinitionInterface $field_definition, $batch_size) {}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function finalizePurge(FieldStorageDefinitionInterface $storage_definition) {}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onFieldDefinitionCreate(FieldDefinitionInterface $field_definition) {}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onFieldDefinitionUpdate(FieldDefinitionInterface $field_definition, FieldDefinitionInterface $original) {}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onFieldDefinitionDelete(FieldDefinitionInterface $field_definition) {}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $storage_definition) {}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original) {}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $storage_definition) {}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createTranslation(ContentEntityInterface $entity, $langcode, array $values = []) {}
|
||||
|
||||
}
|
|
@ -780,9 +780,10 @@ function hook_entity_bundle_delete($entity_type_id, $bundle) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Acts when creating a new entity.
|
||||
* Act on a newly created entity.
|
||||
*
|
||||
* This hook runs after a new entity object has just been instantiated.
|
||||
* This hook runs after a new entity object has just been instantiated. It can
|
||||
* be used to set initial values, e.g. to provide defaults.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity object.
|
||||
|
@ -791,13 +792,16 @@ function hook_entity_bundle_delete($entity_type_id, $bundle) {
|
|||
* @see hook_ENTITY_TYPE_create()
|
||||
*/
|
||||
function hook_entity_create(\Drupal\Core\Entity\EntityInterface $entity) {
|
||||
\Drupal::logger('example')->info('Entity created: @label', ['@label' => $entity->label()]);
|
||||
if ($entity instanceof FieldableEntityInterface && !$entity->foo->value) {
|
||||
$entity->foo->value = 'some_initial_value';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts when creating a new entity of a specific type.
|
||||
* Act on a newly created entity of a specific type.
|
||||
*
|
||||
* This hook runs after a new entity object has just been instantiated.
|
||||
* This hook runs after a new entity object has just been instantiated. It can
|
||||
* be used to set initial values, e.g. to provide defaults.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity object.
|
||||
|
@ -806,7 +810,9 @@ function hook_entity_create(\Drupal\Core\Entity\EntityInterface $entity) {
|
|||
* @see hook_entity_create()
|
||||
*/
|
||||
function hook_ENTITY_TYPE_create(\Drupal\Core\Entity\EntityInterface $entity) {
|
||||
\Drupal::logger('example')->info('ENTITY_TYPE created: @label', ['@label' => $entity->label()]);
|
||||
if (!$entity->foo->value) {
|
||||
$entity->foo->value = 'some_initial_value';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1005,38 +1011,6 @@ function hook_ENTITY_TYPE_update(Drupal\Core\Entity\EntityInterface $entity) {
|
|||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts when creating a new entity translation.
|
||||
*
|
||||
* This hook runs after a new entity translation object has just been
|
||||
* instantiated.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $translation
|
||||
* The entity object.
|
||||
*
|
||||
* @ingroup entity_crud
|
||||
* @see hook_ENTITY_TYPE_translation_create()
|
||||
*/
|
||||
function hook_entity_translation_create(\Drupal\Core\Entity\EntityInterface $translation) {
|
||||
\Drupal::logger('example')->info('Entity translation created: @label', ['@label' => $translation->label()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts when creating a new entity translation of a specific type.
|
||||
*
|
||||
* This hook runs after a new entity translation object has just been
|
||||
* instantiated.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $translation
|
||||
* The entity object.
|
||||
*
|
||||
* @ingroup entity_crud
|
||||
* @see hook_entity_translation_create()
|
||||
*/
|
||||
function hook_ENTITY_TYPE_translation_create(\Drupal\Core\Entity\EntityInterface $translation) {
|
||||
\Drupal::logger('example')->info('ENTITY_TYPE translation created: @label', ['@label' => $translation->label()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Respond to creation of a new entity translation.
|
||||
*
|
||||
|
@ -1912,44 +1886,6 @@ function hook_entity_field_access_alter(array &$grants, array $context) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts when initializing a fieldable entity object.
|
||||
*
|
||||
* This hook runs after a new entity object or a new entity translation object
|
||||
* has just been instantiated. It can be used to set initial values, e.g. to
|
||||
* provide defaults.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\FieldableEntityInterface $entity
|
||||
* The entity object.
|
||||
*
|
||||
* @ingroup entity_crud
|
||||
* @see hook_ENTITY_TYPE_field_values_init()
|
||||
*/
|
||||
function hook_entity_field_values_init(\Drupal\Core\Entity\FieldableEntityInterface $entity) {
|
||||
if ($entity instanceof \Drupal\Core\Entity\ContentEntityInterface && !$entity->foo->value) {
|
||||
$entity->foo->value = 'some_initial_value';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts when initializing a fieldable entity object.
|
||||
*
|
||||
* This hook runs after a new entity object or a new entity translation object
|
||||
* has just been instantiated. It can be used to set initial values, e.g. to
|
||||
* provide defaults.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\FieldableEntityInterface $entity
|
||||
* The entity object.
|
||||
*
|
||||
* @ingroup entity_crud
|
||||
* @see hook_entity_field_values_init()
|
||||
*/
|
||||
function hook_ENTITY_TYPE_field_values_init(\Drupal\Core\Entity\FieldableEntityInterface $entity) {
|
||||
if (!$entity->foo->value) {
|
||||
$entity->foo->value = 'some_initial_value';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposes "pseudo-field" components on content entities.
|
||||
*
|
||||
|
|
|
@ -28,14 +28,6 @@ interface TranslatableInterface {
|
|||
*/
|
||||
public function isDefaultTranslation();
|
||||
|
||||
/**
|
||||
* Checks whether the translation is new.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the translation is new, FALSE otherwise.
|
||||
*/
|
||||
public function isNewTranslation();
|
||||
|
||||
/**
|
||||
* Returns the languages the data is translated to.
|
||||
*
|
||||
|
|
|
@ -328,7 +328,6 @@ class EntityTranslationTest extends EntityLanguageTestBase {
|
|||
// 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->assertIdentical($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);
|
||||
|
@ -354,7 +353,6 @@ class EntityTranslationTest extends EntityLanguageTestBase {
|
|||
$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->assertNotIdentical($entity, $translation, 'The entity and the translation object differ from one another.');
|
||||
$this->assertTrue($entity->hasTranslation($langcode), 'The new translation exists.');
|
||||
$this->assertEqual($translation->language()->getId(), $langcode, 'The translation language matches the specified one.');
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
function keyvalue_test_entity_type_alter(array &$entity_types) {
|
||||
/** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
|
||||
if (isset($entity_types['entity_test_label'])) {
|
||||
$entity_types['entity_test_label']->setStorageClass('Drupal\Core\Entity\KeyValueStore\KeyValueContentEntityStorage');
|
||||
$entity_types['entity_test_label']->setStorageClass('Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage');
|
||||
$entity_keys = $entity_types['entity_test_label']->getKeys();
|
||||
$entity_types['entity_test_label']->set('entity_keys', $entity_keys + array('uuid' => 'uuid'));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue