Commit 5c3b66c8 authored by alexpott's avatar alexpott

Issue #2330121 by plach, effulgentsia, fago: Replace...

Issue #2330121 by plach, effulgentsia, fago: Replace ContentEntityDatabaseStorage::getSchema() with a new EntityTypeListenerInterface implemented by SqlContentEntityStorageSchema.
parent c08a915d
......@@ -34,7 +34,7 @@
*
* @ingroup entity_api
*/
class ContentEntityDatabaseStorage extends ContentEntityStorageBase implements SqlEntityStorageInterface {
class ContentEntityDatabaseStorage extends ContentEntityStorageBase implements SqlEntityStorageInterface, EntityTypeListenerInterface {
/**
* The mapping of field columns to SQL tables.
......@@ -217,13 +217,6 @@ public function getRevisionDataTable() {
return $this->revisionDataTable;
}
/**
* {@inheritdoc}
*/
public function getSchema() {
return $this->schemaHandler()->getSchema();
}
/**
* Gets the schema handler for this entity storage.
*
......@@ -233,7 +226,7 @@ public function getSchema() {
protected function schemaHandler() {
if (!isset($this->schemaHandler)) {
$schema_handler_class = $this->entityType->getHandlerClass('storage_schema') ?: 'Drupal\Core\Entity\Schema\SqlContentEntityStorageSchema';
$this->schemaHandler = new $schema_handler_class($this->entityManager, $this->entityType, $this);
$this->schemaHandler = new $schema_handler_class($this->entityManager, $this->entityType, $this, $this->database);
}
return $this->schemaHandler;
}
......@@ -1365,6 +1358,27 @@ protected function usesDedicatedTable(FieldStorageDefinitionInterface $definitio
return $definition->getProvider() != $this->entityType->getProvider() && !$definition->hasCustomStorage();
}
/**
* {@inheritdoc}
*/
public function onEntityTypeCreate(EntityTypeInterface $entity_type) {
$this->schemaHandler()->onEntityTypeCreate($entity_type);
}
/**
* {@inheritdoc}
*/
public function onEntityTypeUpdate(EntityTypeInterface $entity_type, EntityTypeInterface $original) {
$this->schemaHandler()->onEntityTypeUpdate($entity_type, $original);
}
/**
* {@inheritdoc}
*/
public function onEntityTypeDelete(EntityTypeInterface $entity_type) {
$this->schemaHandler()->onEntityTypeDelete($entity_type);
}
/**
* {@inheritdoc}
*/
......
......@@ -957,6 +957,42 @@ public function getEntityTypeFromClass($class_name) {
throw new NoCorrespondingEntityClassException($class_name);
}
/**
* {@inheritdoc}
*/
public function onEntityTypeCreate(EntityTypeInterface $entity_type) {
// @todo Forward this to all interested handlers, not only storage, once
// iterating handlers is possible: https://www.drupal.org/node/2332857.
$storage = $this->getStorage($entity_type->id());
if ($storage instanceof EntityTypeListenerInterface) {
$storage->onEntityTypeCreate($entity_type);
}
}
/**
* {@inheritdoc}
*/
public function onEntityTypeUpdate(EntityTypeInterface $entity_type, EntityTypeInterface $original) {
// @todo Forward this to all interested handlers, not only storage, once
// iterating handlers is possible: https://www.drupal.org/node/2332857.
$storage = $this->getStorage($entity_type->id());
if ($storage instanceof EntityTypeListenerInterface) {
$storage->onEntityTypeUpdate($entity_type, $original);
}
}
/**
* {@inheritdoc}
*/
public function onEntityTypeDelete(EntityTypeInterface $entity_type) {
// @todo Forward this to all interested handlers, not only storage, once
// iterating handlers is possible: https://www.drupal.org/node/2332857.
$storage = $this->getStorage($entity_type->id());
if ($storage instanceof EntityTypeListenerInterface) {
$storage->onEntityTypeDelete($entity_type);
}
}
/**
* Acts on entity bundle rename.
*
......
......@@ -12,7 +12,7 @@
/**
* Provides an interface for entity type managers.
*/
interface EntityManagerInterface extends PluginManagerInterface {
interface EntityManagerInterface extends PluginManagerInterface, EntityTypeListenerInterface {
/**
* Builds a list of entity type labels suitable for a Form API options list.
......
<?php
/**
* @file
* Contains \Drupal\Core\Entity\EntityTypeListenerInterface.
*/
namespace Drupal\Core\Entity;
/**
* Defines an interface for reacting to entity type creation, deletion, and updates.
*
* @todo Convert to Symfony events: https://www.drupal.org/node/2332935
*/
interface EntityTypeListenerInterface {
/**
* Reacts to the creation of the entity type.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type being created.
*/
public function onEntityTypeCreate(EntityTypeInterface $entity_type);
/**
* Reacts to the update of the entity type.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The updated entity type definition.
* @param \Drupal\Core\Entity\EntityTypeInterface $original
* The original entity type definition.
*/
public function onEntityTypeUpdate(EntityTypeInterface $entity_type, EntityTypeInterface $original);
/**
* Reacts to the deletion of the entity type.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type being deleted.
*/
public function onEntityTypeDelete(EntityTypeInterface $entity_type);
}
......@@ -6,9 +6,10 @@
*/
namespace Drupal\Core\Entity\Schema;
use Drupal\Core\Entity\EntityTypeListenerInterface;
/**
* Defines an interface for handling the storage schema of entities.
*/
interface EntitySchemaHandlerInterface extends EntitySchemaProviderInterface {
interface EntitySchemaHandlerInterface extends EntityTypeListenerInterface {
}
<?php
/**
* @file
* Contains \Drupal\Core\Entity\Schema\EntitySchemaProviderInterface.
*/
namespace Drupal\Core\Entity\Schema;
/**
* Defines a common interface to return the storage schema for entities.
*/
interface EntitySchemaProviderInterface {
/**
* Gets the full schema array for a given entity type.
*
* @return array
* A schema array for the entity type's tables.
*/
public function getSchema();
}
......@@ -7,9 +7,11 @@
namespace Drupal\Core\Entity\Schema;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\ContentEntityDatabaseStorage;
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
/**
* Defines a schema handler that supports revisionable, translatable entities.
......@@ -44,6 +46,13 @@ class SqlContentEntityStorageSchema implements EntitySchemaHandlerInterface {
*/
protected $schema;
/**
* The database connection to be used.
*
* @var \Drupal\Core\Database\Connection
*/
protected $database;
/**
* Constructs a SqlContentEntityStorageSchema.
*
......@@ -53,18 +62,52 @@ class SqlContentEntityStorageSchema implements EntitySchemaHandlerInterface {
* The entity type.
* @param \Drupal\Core\Entity\ContentEntityDatabaseStorage $storage
* The storage of the entity type. This must be an SQL-based storage.
* @param \Drupal\Core\Database\Connection $database
* The database connection to be used.
*/
public function __construct(EntityManagerInterface $entity_manager, ContentEntityTypeInterface $entity_type, ContentEntityDatabaseStorage $storage) {
public function __construct(EntityManagerInterface $entity_manager, ContentEntityTypeInterface $entity_type, ContentEntityDatabaseStorage $storage, Connection $database) {
$this->entityType = $entity_type;
$this->fieldStorageDefinitions = $entity_manager->getFieldStorageDefinitions($entity_type->id());
$this->storage = $storage;
$this->database = $database;
}
/**
* {@inheritdoc}
*/
public function getSchema() {
return $this->getEntitySchema($this->entityType);
public function onEntityTypeCreate(EntityTypeInterface $entity_type) {
$schema_handler = $this->database->schema();
$schema = $this->getEntitySchema($entity_type, TRUE);
foreach ($schema as $table_name => $table_schema) {
if (!$schema_handler->tableExists($table_name)) {
$schema_handler->createTable($table_name, $table_schema);
}
}
}
/**
* {@inheritdoc}
*/
public function onEntityTypeUpdate(EntityTypeInterface $entity_type, EntityTypeInterface $original) {
// @todo Implement proper updates: https://www.drupal.org/node/1498720.
// Meanwhile, treat a change from non-SQL storage to SQL storage as
// identical to creation with respect to SQL schema handling.
if (!is_subclass_of($original->getStorageClass(), '\Drupal\Core\Entity\Sql\SqlEntityStorageInterface')) {
$this->onEntityTypeCreate($entity_type);
}
}
/**
* {@inheritdoc}
*/
public function onEntityTypeDelete(EntityTypeInterface $entity_type) {
$schema_handler = $this->database->schema();
$schema = $this->getEntitySchema($entity_type, TRUE);
foreach ($schema as $table_name => $table_schema) {
if ($schema_handler->tableExists($table_name)) {
$schema_handler->dropTable($table_name);
}
}
}
/**
......
......@@ -8,12 +8,11 @@
namespace Drupal\Core\Entity\Sql;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\Schema\EntitySchemaProviderInterface;
/**
* A common interface for SQL-based entity storage implementations.
*/
interface SqlEntityStorageInterface extends EntityStorageInterface, EntitySchemaProviderInterface {
interface SqlEntityStorageInterface extends EntityStorageInterface {
/**
* Gets a table mapping for the entity's SQL tables.
......
......@@ -12,7 +12,6 @@
use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Utility\String;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Entity\Schema\EntitySchemaProviderInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
......@@ -845,19 +844,14 @@ public function install(array $module_list, $enable_dependencies = TRUE) {
}
drupal_set_installed_schema_version($module, $version);
// Install any entity schemas belonging to the module.
// Notify the entity manager that this module's entity types are new,
// so that it can notify all interested handlers. For example, a
// SQL-based storage handler can use this as an opportunity to create
// the necessary database tables.
$entity_manager = \Drupal::entityManager();
$schema = \Drupal::database()->schema();
foreach ($entity_manager->getDefinitions() as $entity_type) {
if ($entity_type->getProvider() == $module) {
$storage = $entity_manager->getStorage($entity_type->id());
if ($storage instanceof EntitySchemaProviderInterface) {
foreach ($storage->getSchema() as $table_name => $table_schema) {
if (!$schema->tableExists($table_name)) {
$schema->createTable($table_name, $table_schema);
}
}
}
$entity_manager->onEntityTypeCreate($entity_type);
}
}
......@@ -965,19 +959,13 @@ public function uninstall(array $module_list, $uninstall_dependents = TRUE) {
// Remove all configuration belonging to the module.
\Drupal::service('config.manager')->uninstall('module', $module);
// Remove any entity schemas belonging to the module.
$schema = \Drupal::database()->schema();
// Notify the entity manager that this module's entity types are being
// deleted, so that it can notify all interested handlers. For example,
// a SQL-based storage handler can use this as an opportunity to drop
// the corresponding database tables.
foreach ($entity_manager->getDefinitions() as $entity_type) {
if ($entity_type->getProvider() == $module) {
$storage = $entity_manager->getStorage($entity_type->id());
if ($storage instanceof EntitySchemaProviderInterface) {
foreach ($storage->getSchema() as $table_name => $table_schema) {
if ($schema->tableExists($table_name)) {
$schema->dropTable($table_name);
}
}
}
$entity_manager->onEntityTypeDelete($entity_type);
}
}
......
......@@ -9,16 +9,15 @@
* Implements hook_install().
*/
function contact_storage_test_install() {
// ModuleHandler won't create the schema automatically because Message entity
// belongs to contact.module.
// @todo Remove this when https://www.drupal.org/node/1498720 is in.
$entity_manager = \Drupal::entityManager();
$schema = \Drupal::database()->schema();
$entity_type = $entity_manager->getDefinition('contact_message');
$storage = $entity_manager->getStorage($entity_type->id());
foreach ($storage->getSchema() as $table_name => $table_schema) {
if (!$schema->tableExists($table_name)) {
$schema->createTable($table_name, $table_schema);
}
}
// Recreate the original entity type definition, in order to notify the
// manager of what changed. The change of storage backend will trigger
// schema installation.
// @see contact_storage_test_entity_type_alter()
$original = clone $entity_type;
$original->setStorageClass('Drupal\Core\Entity\ContentEntityNullStorage');
$entity_manager->onEntityTypeUpdate($entity_type, $original);
}
......@@ -11,10 +11,10 @@
use Drupal\Core\Database\Database;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DrupalKernel;
use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
use Drupal\Core\KeyValueStore\KeyValueMemoryFactory;
use Drupal\Core\Language\Language;
use Drupal\Core\Site\Settings;
use Drupal\Core\Entity\Schema\EntitySchemaProviderInterface;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpFoundation\Request;
......@@ -377,36 +377,39 @@ protected function installSchema($module, $tables) {
/**
* Installs the tables for a specific entity type.
* Installs the storage schema for a specific entity type.
*
* @param string $entity_type_id
* The ID of the entity type.
*
* @throws \RuntimeException
* Thrown when the entity type does not support automatic schema installation.
*/
protected function installEntitySchema($entity_type_id) {
/** @var \Drupal\Core\Entity\EntityManagerInterface $entity_manager */
$entity_manager = $this->container->get('entity.manager');
/** @var \Drupal\Core\Database\Schema $schema_handler */
$schema_handler = $this->container->get('database')->schema();
$entity_type = $entity_manager->getDefinition($entity_type_id);
$entity_manager->onEntityTypeCreate($entity_type);
// For test runs, the most common storage backend is a SQL database. For
// this case, ensure the tables got created.
$storage = $entity_manager->getStorage($entity_type_id);
if ($storage instanceof EntitySchemaProviderInterface) {
$schema = $storage->getSchema();
foreach ($schema as $table_name => $table_schema) {
$schema_handler->createTable($table_name, $table_schema);
if ($storage instanceof SqlEntityStorageInterface) {
$tables = $storage->getTableMapping()->getTableNames();
$db_schema = $this->container->get('database')->schema();
$all_tables_exist = TRUE;
foreach ($tables as $table) {
if (!$db_schema->tableExists($table)) {
$this->fail(String::format('Installed entity type table for the %entity_type entity type: %table', array(
'%entity_type' => $entity_type_id,
'%table' => $table,
)));
$all_tables_exist = FALSE;
}
}
if ($all_tables_exist) {
$this->pass(String::format('Installed entity type tables for the %entity_type entity type: %tables', array(
'%entity_type' => $entity_type_id,
'%tables' => '{' . implode('}, {', $tables) . '}',
)));
}
$this->pass(String::format('Installed entity type tables for the %entity_type entity type: %tables', array(
'%entity_type' => $entity_type_id,
'%tables' => '{' . implode('}, {', array_keys($schema)) . '}',
)));
}
else {
throw new \RuntimeException(String::format('Entity type %entity_type does not support automatic schema installation.', array(
'%entity-type' => $entity_type_id,
)));
}
}
......
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Field\TestBaseFieldDefinitionInterface.
*/
namespace Drupal\Tests\Core\Field;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
/**
* Defines a test interface to mock entity base field definitions.
*/
interface TestBaseFieldDefinitionInterface extends FieldDefinitionInterface, FieldStorageDefinitionInterface {
}
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