Commit 0f881613 authored by catch's avatar catch

Issue #2482295 by Berdir: Rebuilding field map with many bundles/fields is very slow

parent b480c525
......@@ -433,7 +433,7 @@ services:
arguments: ['@app.root', '@config.factory', '@module_handler', '@state', '@info_parser', '@logger.channel.default', '@asset.css.collection_optimizer', '@config.installer', '@config.manager', '@router.builder']
entity.manager:
class: Drupal\Core\Entity\EntityManager
arguments: ['@container.namespaces', '@module_handler', '@cache.discovery', '@language_manager', '@string_translation', '@class_resolver', '@typed_data_manager', '@entity.definitions.installed', '@event_dispatcher']
arguments: ['@container.namespaces', '@module_handler', '@cache.discovery', '@language_manager', '@string_translation', '@class_resolver', '@typed_data_manager', '@keyvalue', '@event_dispatcher']
parent: container.trait
tags:
- { name: plugin_manager_cache_clear }
......@@ -442,11 +442,6 @@ services:
arguments: ['@entity.manager']
tags:
- { name: event_subscriber }
entity.definitions.installed:
class: Drupal\Core\KeyValueStore\KeyValueStoreInterface
factory_method: get
factory_service: keyvalue
arguments: ['entity.definitions.installed']
entity.definition_update_manager:
class: Drupal\Core\Entity\EntityDefinitionUpdateManager
arguments: ['@entity.manager']
......
......@@ -8,6 +8,7 @@
namespace Drupal\Core\Entity;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldDefinitionListenerInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Field\FieldStorageDefinitionListenerInterface;
......@@ -20,7 +21,7 @@
*
* For example, configurable fields defined and exposed by field.module.
*/
interface DynamicallyFieldableEntityStorageInterface extends FieldableEntityStorageInterface, FieldStorageDefinitionListenerInterface {
interface DynamicallyFieldableEntityStorageInterface extends FieldableEntityStorageInterface, FieldStorageDefinitionListenerInterface, FieldDefinitionListenerInterface {
/**
* Determines if the storage contains any data.
......@@ -30,37 +31,6 @@ interface DynamicallyFieldableEntityStorageInterface extends FieldableEntityStor
*/
public function hasData();
/**
* Reacts to the creation of a field.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition created.
*/
public function onFieldDefinitionCreate(FieldDefinitionInterface $field_definition);
/**
* Reacts to the update of a field.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition being updated.
* @param \Drupal\Core\Field\FieldDefinitionInterface $original
* The original field definition; i.e., the definition before the update.
*/
public function onFieldDefinitionUpdate(FieldDefinitionInterface $field_definition, FieldDefinitionInterface $original);
/**
* Reacts to the deletion of a field.
*
* Stored values should not be wiped at once, but marked as 'deleted' so that
* they can go through a proper purge process later on.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition being deleted.
*
* @see purgeFieldData()
*/
public function onFieldDefinitionDelete(FieldDefinitionInterface $field_definition);
/**
* Purges a batch of field data.
*
......
......@@ -9,12 +9,13 @@
use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Core\Field\FieldDefinitionListenerInterface;
use Drupal\Core\Field\FieldStorageDefinitionListenerInterface;
/**
* Provides an interface for entity type managers.
*/
interface EntityManagerInterface extends PluginManagerInterface, EntityTypeListenerInterface, EntityBundleListenerInterface, FieldStorageDefinitionListenerInterface, CachedDiscoveryInterface {
interface EntityManagerInterface extends PluginManagerInterface, EntityTypeListenerInterface, EntityBundleListenerInterface, FieldStorageDefinitionListenerInterface, FieldDefinitionListenerInterface, CachedDiscoveryInterface {
/**
* Builds a list of entity type labels suitable for a Form API options list.
......
<?php
/**
* @file
* Contains \Drupal\Core\Field\FieldDefinitionListenerInterface.
*/
namespace Drupal\Core\Field;
/**
* Defines an interface for reacting to field creation, deletion, and updates.
*/
interface FieldDefinitionListenerInterface {
/**
* Reacts to the creation of a field.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition created.
*/
public function onFieldDefinitionCreate(FieldDefinitionInterface $field_definition);
/**
* Reacts to the update of a field.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition being updated.
* @param \Drupal\Core\Field\FieldDefinitionInterface $original
* The original field definition; i.e., the definition before the update.
*/
public function onFieldDefinitionUpdate(FieldDefinitionInterface $field_definition, FieldDefinitionInterface $original);
/**
* Reacts to the deletion of a field.
*
* Stored values should not be wiped at once, but marked as 'deleted' so that
* they can go through a proper purge process later on.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition being deleted.
*/
public function onFieldDefinitionDelete(FieldDefinitionInterface $field_definition);
}
......@@ -160,7 +160,7 @@ public function preSave(EntityStorageInterface $storage) {
if ($this->isNew()) {
// Notify the entity storage.
$entity_manager->getStorage($this->entity_type)->onFieldDefinitionCreate($this);
$entity_manager->onFieldDefinitionCreate($this);
}
else {
// Some updates are always disallowed.
......@@ -174,7 +174,7 @@ public function preSave(EntityStorageInterface $storage) {
throw new FieldException("Cannot change an existing field's storage.");
}
// Notify the entity storage.
$entity_manager->getStorage($this->entity_type)->onFieldDefinitionUpdate($this, $this->original);
$entity_manager->onFieldDefinitionUpdate($this, $this->original);
}
parent::preSave($storage);
......@@ -221,7 +221,7 @@ public static function postDelete(EntityStorageInterface $storage, array $fields
// Notify the entity storage.
foreach ($fields as $field) {
if (!$field->deleted) {
\Drupal::entityManager()->getStorage($field->entity_type)->onFieldDefinitionDelete($field);
\Drupal::entityManager()->onFieldDefinitionDelete($field);
}
}
......
......@@ -66,6 +66,11 @@ public function testCustomBundleFieldUsage() {
'type' => 'custom',
]);
$this->assertTrue($entity->hasField('custom_bundle_field'));
// Ensure that the field exists in the field map.
$field_map = \Drupal::entityManager()->getFieldMap();
$this->assertEqual($field_map['entity_test']['custom_bundle_field'], ['type' => 'string', 'bundles' => ['custom' => 'custom']]);
$entity->custom_bundle_field->value = 'swanky';
$entity->save();
$storage->resetCache();
......@@ -102,6 +107,10 @@ public function testCustomBundleFieldUsage() {
->execute();
$this->assertEqual(1, $result->fetchField(), 'Field data has been deleted');
// Ensure that the field no longer exists in the field map.
$field_map = \Drupal::entityManager()->getFieldMap();
$this->assertFalse(isset($field_map['entity_test']['custom_bundle_field']));
// @todo Test field purge and table deletion once supported. See
// https://www.drupal.org/node/2282119.
// $this->assertFalse($this->database->schema()->tableExists($table), 'Custom field table was deleted');
......
......@@ -67,6 +67,21 @@ function entity_schema_test_entity_bundle_field_info(EntityTypeInterface $entity
}
}
/**
* Implements hook_entity_bundle_create().
*/
function entity_schema_test_entity_bundle_create($entity_type_id, $bundle) {
if ($entity_type_id == 'entity_test' && $bundle == 'custom') {
$entity_type = \Drupal::entityManager()->getDefinition($entity_type_id);
$field_definitions = entity_schema_test_entity_bundle_field_info($entity_type, $bundle);
$field_definitions['custom_bundle_field']
->setTargetEntityTypeId($entity_type_id)
->setTargetBundle($bundle);
// Notify the entity storage that we just created a new field.
\Drupal::entityManager()->onFieldDefinitionCreate($field_definitions['custom_bundle_field']);
}
}
/**
* Implements hook_entity_bundle_delete().
*/
......@@ -78,7 +93,6 @@ function entity_schema_test_entity_bundle_delete($entity_type_id, $bundle) {
->setTargetEntityTypeId($entity_type_id)
->setTargetBundle($bundle);
// Notify the entity storage that our field is gone.
\Drupal::entityManager()->getStorage($entity_type_id)
->onFieldDefinitionDelete($field_definitions['custom_bundle_field']);
\Drupal::entityManager()->onFieldDefinitionDelete($field_definitions['custom_bundle_field']);
}
}
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