Revert "Issue #2382675 by plach, yched: ContentEntityBase::addTranslation() should not fire hook_entity_create()"

This reverts commit 58332b2637.
8.0.x
Alex Pott 2015-11-17 10:44:05 +00:00
parent 6edd7db4b1
commit 4f9265c85d
11 changed files with 66 additions and 281 deletions

View File

@ -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");

View File

@ -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;
}
/**

View File

@ -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;
}
/**

View File

@ -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 = []);
}

View File

@ -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");
}
}
}

View File

@ -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}
*/

View File

@ -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 = []) {}
}

View File

@ -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.
*

View File

@ -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.
*

View File

@ -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.');

View File

@ -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'));
}