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: ...@@ -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'] 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: entity.manager:
class: Drupal\Core\Entity\EntityManager 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 parent: container.trait
tags: tags:
- { name: plugin_manager_cache_clear } - { name: plugin_manager_cache_clear }
...@@ -442,11 +442,6 @@ services: ...@@ -442,11 +442,6 @@ services:
arguments: ['@entity.manager'] arguments: ['@entity.manager']
tags: tags:
- { name: event_subscriber } - { 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: entity.definition_update_manager:
class: Drupal\Core\Entity\EntityDefinitionUpdateManager class: Drupal\Core\Entity\EntityDefinitionUpdateManager
arguments: ['@entity.manager'] arguments: ['@entity.manager']
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
namespace Drupal\Core\Entity; namespace Drupal\Core\Entity;
use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldDefinitionListenerInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Field\FieldStorageDefinitionListenerInterface; use Drupal\Core\Field\FieldStorageDefinitionListenerInterface;
...@@ -20,7 +21,7 @@ ...@@ -20,7 +21,7 @@
* *
* For example, configurable fields defined and exposed by field.module. * 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. * Determines if the storage contains any data.
...@@ -30,37 +31,6 @@ interface DynamicallyFieldableEntityStorageInterface extends FieldableEntityStor ...@@ -30,37 +31,6 @@ interface DynamicallyFieldableEntityStorageInterface extends FieldableEntityStor
*/ */
public function hasData(); 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. * Purges a batch of field data.
* *
......
...@@ -9,12 +9,13 @@ ...@@ -9,12 +9,13 @@
use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface; use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
use Drupal\Component\Plugin\PluginManagerInterface; use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Core\Field\FieldDefinitionListenerInterface;
use Drupal\Core\Field\FieldStorageDefinitionListenerInterface; use Drupal\Core\Field\FieldStorageDefinitionListenerInterface;
/** /**
* Provides an interface for entity type managers. * 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. * 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) { ...@@ -160,7 +160,7 @@ public function preSave(EntityStorageInterface $storage) {
if ($this->isNew()) { if ($this->isNew()) {
// Notify the entity storage. // Notify the entity storage.
$entity_manager->getStorage($this->entity_type)->onFieldDefinitionCreate($this); $entity_manager->onFieldDefinitionCreate($this);
} }
else { else {
// Some updates are always disallowed. // Some updates are always disallowed.
...@@ -174,7 +174,7 @@ public function preSave(EntityStorageInterface $storage) { ...@@ -174,7 +174,7 @@ public function preSave(EntityStorageInterface $storage) {
throw new FieldException("Cannot change an existing field's storage."); throw new FieldException("Cannot change an existing field's storage.");
} }
// Notify the entity storage. // Notify the entity storage.
$entity_manager->getStorage($this->entity_type)->onFieldDefinitionUpdate($this, $this->original); $entity_manager->onFieldDefinitionUpdate($this, $this->original);
} }
parent::preSave($storage); parent::preSave($storage);
...@@ -221,7 +221,7 @@ public static function postDelete(EntityStorageInterface $storage, array $fields ...@@ -221,7 +221,7 @@ public static function postDelete(EntityStorageInterface $storage, array $fields
// Notify the entity storage. // Notify the entity storage.
foreach ($fields as $field) { foreach ($fields as $field) {
if (!$field->deleted) { if (!$field->deleted) {
\Drupal::entityManager()->getStorage($field->entity_type)->onFieldDefinitionDelete($field); \Drupal::entityManager()->onFieldDefinitionDelete($field);
} }
} }
......
...@@ -66,6 +66,11 @@ public function testCustomBundleFieldUsage() { ...@@ -66,6 +66,11 @@ public function testCustomBundleFieldUsage() {
'type' => 'custom', 'type' => 'custom',
]); ]);
$this->assertTrue($entity->hasField('custom_bundle_field')); $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->custom_bundle_field->value = 'swanky';
$entity->save(); $entity->save();
$storage->resetCache(); $storage->resetCache();
...@@ -102,6 +107,10 @@ public function testCustomBundleFieldUsage() { ...@@ -102,6 +107,10 @@ public function testCustomBundleFieldUsage() {
->execute(); ->execute();
$this->assertEqual(1, $result->fetchField(), 'Field data has been deleted'); $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 // @todo Test field purge and table deletion once supported. See
// https://www.drupal.org/node/2282119. // https://www.drupal.org/node/2282119.
// $this->assertFalse($this->database->schema()->tableExists($table), 'Custom field table was deleted'); // $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 ...@@ -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(). * Implements hook_entity_bundle_delete().
*/ */
...@@ -78,7 +93,6 @@ function entity_schema_test_entity_bundle_delete($entity_type_id, $bundle) { ...@@ -78,7 +93,6 @@ function entity_schema_test_entity_bundle_delete($entity_type_id, $bundle) {
->setTargetEntityTypeId($entity_type_id) ->setTargetEntityTypeId($entity_type_id)
->setTargetBundle($bundle); ->setTargetBundle($bundle);
// Notify the entity storage that our field is gone. // Notify the entity storage that our field is gone.
\Drupal::entityManager()->getStorage($entity_type_id) \Drupal::entityManager()->onFieldDefinitionDelete($field_definitions['custom_bundle_field']);
->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