Commit 8007ab1b authored by alexpott's avatar alexpott

Issue #2423213 by plach: Schema for newly defined entity types is never created

parent f671f3c0
......@@ -18,27 +18,6 @@
class EntityDefinitionUpdateManager implements EntityDefinitionUpdateManagerInterface {
use StringTranslationTrait;
/**
* Indicates that a definition has just been created.
*
* @var int
*/
const DEFINITION_CREATED = 1;
/**
* Indicates that a definition has changes.
*
* @var int
*/
const DEFINITION_UPDATED = 2;
/**
* Indicates that a definition has just been deleted.
*
* @var int
*/
const DEFINITION_DELETED = 3;
/**
* The entity manager service.
*
......@@ -70,6 +49,22 @@ public function getChangeSummary() {
$summary = array();
foreach ($this->getChangeList() as $entity_type_id => $change_list) {
// Process entity type definition changes.
if (!empty($change_list['entity_type'])) {
$entity_type = $this->entityManager->getDefinition($entity_type_id);
$t_args = array('%entity_type' => $entity_type->getLabel());
switch ($change_list['entity_type']) {
case static::DEFINITION_CREATED:
$summary[$entity_type_id][] = $this->t('Create the %entity_type entity type.', $t_args);
break;
case static::DEFINITION_UPDATED:
$summary[$entity_type_id][] = $this->t('Update the %entity_type entity type.', $t_args);
break;
}
}
// Process field storage definition changes.
if (!empty($change_list['field_storage_definitions'])) {
$storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
......@@ -91,11 +86,6 @@ public function getChangeSummary() {
}
}
}
// Process entity type definition changes.
if (!empty($change_list['entity_type']) && $change_list['entity_type'] == static::DEFINITION_UPDATED) {
$entity_type = $this->entityManager->getDefinition($entity_type_id);
$summary[$entity_type_id][] = $this->t('Update the %entity_type entity type.', array('%entity_type' => $entity_type->getLabel()));
}
}
return $summary;
......@@ -110,10 +100,19 @@ public function applyUpdates() {
// this is necessary when you change an entity type from non-revisionable
// to revisionable and at the same time add revisionable fields to the
// entity type.
if (!empty($change_list['entity_type']) && $change_list['entity_type'] == static::DEFINITION_UPDATED) {
if (!empty($change_list['entity_type'])) {
$entity_type = $this->entityManager->getDefinition($entity_type_id);
$original = $this->entityManager->getLastInstalledDefinition($entity_type_id);
$this->entityManager->onEntityTypeUpdate($entity_type, $original);
switch ($change_list['entity_type']) {
case static::DEFINITION_CREATED:
$this->entityManager->onEntityTypeCreate($entity_type);
break;
case static::DEFINITION_UPDATED:
$original = $this->entityManager->getLastInstalledDefinition($entity_type_id);
$this->entityManager->onEntityTypeUpdate($entity_type, $original);
break;
}
}
// Process field storage definition changes.
......@@ -160,54 +159,55 @@ protected function getChangeList() {
foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) {
$original = $this->entityManager->getLastInstalledDefinition($entity_type_id);
// Only manage changes to already installed entity types. Entity type
// installation is handled elsewhere (e.g.,
// \Drupal\Core\Extension\ModuleHandler::install()).
if (!$original) {
continue;
}
// @todo Support non-storage-schema-changing definition updates too:
// https://www.drupal.org/node/2336895.
if ($this->requiresEntityStorageSchemaChanges($entity_type, $original)) {
$change_list[$entity_type_id]['entity_type'] = static::DEFINITION_UPDATED;
if (!$original) {
$change_list[$entity_type_id]['entity_type'] = static::DEFINITION_CREATED;
}
else {
if ($this->requiresEntityStorageSchemaChanges($entity_type, $original)) {
$change_list[$entity_type_id]['entity_type'] = static::DEFINITION_UPDATED;
}
if ($this->entityManager->getStorage($entity_type_id) instanceof DynamicallyFieldableEntityStorageInterface) {
$field_changes = array();
$storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
$original_storage_definitions = $this->entityManager->getLastInstalledFieldStorageDefinitions($entity_type_id);
if ($this->entityManager->getStorage($entity_type_id) instanceof DynamicallyFieldableEntityStorageInterface) {
$field_changes = array();
$storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
$original_storage_definitions = $this->entityManager->getLastInstalledFieldStorageDefinitions($entity_type_id);
// Detect created field storage definitions.
foreach (array_diff_key($storage_definitions, $original_storage_definitions) as $field_name => $storage_definition) {
$field_changes[$field_name] = static::DEFINITION_CREATED;
}
// Detect created field storage definitions.
foreach (array_diff_key($storage_definitions, $original_storage_definitions) as $field_name => $storage_definition) {
$field_changes[$field_name] = static::DEFINITION_CREATED;
}
// Detect deleted field storage definitions.
foreach (array_diff_key($original_storage_definitions, $storage_definitions) as $field_name => $original_storage_definition) {
$field_changes[$field_name] = static::DEFINITION_DELETED;
}
// Detect deleted field storage definitions.
foreach (array_diff_key($original_storage_definitions, $storage_definitions) as $field_name => $original_storage_definition) {
$field_changes[$field_name] = static::DEFINITION_DELETED;
}
// Detect updated field storage definitions.
foreach (array_intersect_key($storage_definitions, $original_storage_definitions) as $field_name => $storage_definition) {
// @todo Support non-storage-schema-changing definition updates too:
// https://www.drupal.org/node/2336895. So long as we're checking
// based on schema change requirements rather than definition
// equality, skip the check if the entity type itself needs to be
// updated, since that can affect the schema of all fields, so we
// want to process that update first without reporting false
// positives here.
if (!isset($change_list[$entity_type_id]['entity_type']) && $this->requiresFieldStorageSchemaChanges($storage_definition, $original_storage_definitions[$field_name])) {
$field_changes[$field_name] = static::DEFINITION_UPDATED;
// Detect updated field storage definitions.
foreach (array_intersect_key($storage_definitions, $original_storage_definitions) as $field_name => $storage_definition) {
// @todo Support non-storage-schema-changing definition updates too:
// https://www.drupal.org/node/2336895. So long as we're checking
// based on schema change requirements rather than definition
// equality, skip the check if the entity type itself needs to be
// updated, since that can affect the schema of all fields, so we
// want to process that update first without reporting false
// positives here.
if (!isset($change_list[$entity_type_id]['entity_type']) && $this->requiresFieldStorageSchemaChanges($storage_definition, $original_storage_definitions[$field_name])) {
$field_changes[$field_name] = static::DEFINITION_UPDATED;
}
}
}
if ($field_changes) {
$change_list[$entity_type_id]['field_storage_definitions'] = $field_changes;
if ($field_changes) {
$change_list[$entity_type_id]['field_storage_definitions'] = $field_changes;
}
}
}
}
// @todo Support deleting entity definitions when we support base field
// purging. See https://www.drupal.org/node/2282119.
return array_filter($change_list);
}
......
......@@ -34,6 +34,27 @@
*/
interface EntityDefinitionUpdateManagerInterface {
/**
* Indicates that a definition has just been created.
*
* @var int
*/
const DEFINITION_CREATED = 1;
/**
* Indicates that a definition has changes.
*
* @var int
*/
const DEFINITION_UPDATED = 2;
/**
* Indicates that a definition has just been deleted.
*
* @var int
*/
const DEFINITION_DELETED = 3;
/**
* Checks if there are any definition updates that need to be applied.
*
......
......@@ -31,6 +31,8 @@ protected function setUp() {
parent::setUp();
ConfigurableLanguage::createFromLangcode('fr')->save();
$this->config('system.site')->set('langcode', 'fr')->save();
// Make sure new entity type definitions are processed.
\Drupal::service('entity.definition_update_manager')->applyUpdates();
// Clear all caches so that the base field definition, its cache in the
// entity manager, the t() cache, etc. are all cleared.
drupal_flush_all_caches();
......
......@@ -15,6 +15,15 @@
*/
trait EntityDefinitionTestTrait {
/**
* Enables a new entity type definition.
*/
protected function enableNewEntityType() {
$this->state->set('entity_test_new', TRUE);
$this->entityManager->clearCachedDefinitions();
$this->entityDefinitionUpdateManager->applyUpdates();
}
/**
* Resets the entity type definition.
*/
......
......@@ -54,6 +54,26 @@ protected function setUp() {
}
}
/**
* Tests that new entity type definitions are correctly handled.
*/
public function testNewEntityType() {
$entity_type_id = 'entity_test_new';
$schema = $this->database->schema();
// Check that the "entity_test_new" is not defined.
$entity_types = $this->entityManager->getDefinitions();
$this->assertFalse(isset($entity_types[$entity_type_id]), 'The "entity_test_new" entity type does not exist.');
$this->assertFalse($schema->tableExists($entity_type_id), 'Schema for the "entity_test_new" entity type does not exist.');
// Check that the "entity_test_new" is now defined and the related schema
// has been created.
$this->enableNewEntityType();
$entity_types = $this->entityManager->getDefinitions();
$this->assertTrue(isset($entity_types[$entity_type_id]), 'The "entity_test_new" entity type exists.');
$this->assertTrue($schema->tableExists($entity_type_id), 'Schema for the "entity_test_new" entity type has been created.');
}
/**
* Tests when no definition update is needed.
*/
......
......@@ -32,6 +32,8 @@ protected function setUp() {
parent::setUp();
$this->update_url = $GLOBALS['base_url'] . '/update.php';
$this->update_user = $this->drupalCreateUser(array('administer software updates', 'access site in maintenance mode'));
// Make sure updates for new entity type definitions are processed.
\Drupal::service('entity.definition_update_manager')->applyUpdates();
}
/**
......
......@@ -74,10 +74,12 @@ function entity_test_entity_types($filter = NULL) {
* Implements hook_entity_type_alter().
*/
function entity_test_entity_type_alter(array &$entity_types) {
$state = \Drupal::state();
/** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
foreach (entity_test_entity_types() as $entity_type) {
// Optionally specify a translation handler for testing translations.
if (\Drupal::state()->get('entity_test.translation')) {
if ($state->get('entity_test.translation')) {
$translation = $entity_types[$entity_type]->get('translation');
$translation[$entity_type] = TRUE;
$entity_types[$entity_type]->set('translation', $translation);
......@@ -85,7 +87,12 @@ function entity_test_entity_type_alter(array &$entity_types) {
}
// Allow entity_test_update tests to override the entity type definition.
$entity_types['entity_test_update'] = \Drupal::state()->get('entity_test_update.entity_type', $entity_types['entity_test_update']);
$entity_types['entity_test_update'] = $state->get('entity_test_update.entity_type', $entity_types['entity_test_update']);
// Enable the entity_test_new only when needed.
if (!$state->get('entity_test_new')) {
unset($entity_types['entity_test_new']);
}
}
/**
......
<?php
/**
* @file
* Contains \Drupal\entity_test\Entity\EntityTestNew.
*/
namespace Drupal\entity_test\Entity;
/**
* Defines the test entity class for testing definition addition.
*
* This entity type is initially not defined. It is enabled when needed to test
* the related updates.
*
* @ContentEntityType(
* id = "entity_test_new",
* label = @Translation("New test entity"),
* base_table = "entity_test_new",
* entity_keys = {
* "id" = "id",
* "uuid" = "uuid",
* "bundle" = "type",
* "label" = "name",
* "langcode" = "langcode",
* }
* )
*/
class EntityTestNew extends EntityTest {
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment