Issue #2482295 by Berdir: Rebuilding field map with many bundles/fields is very slow
parent
b480c525ac
commit
0f881613ef
|
@ -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:
|
||||||
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 @@
|
||||||
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 @@ use Drupal\Core\Field\FieldStorageDefinitionListenerInterface;
|
||||||
*
|
*
|
||||||
* 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
|
||||||
*/
|
*/
|
||||||
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.
|
||||||
*
|
*
|
||||||
|
|
|
@ -18,10 +18,12 @@ use Drupal\Core\Entity\Exception\AmbiguousEntityClassException;
|
||||||
use Drupal\Core\Entity\Exception\NoCorrespondingEntityClassException;
|
use Drupal\Core\Entity\Exception\NoCorrespondingEntityClassException;
|
||||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||||
use Drupal\Core\Field\BaseFieldDefinition;
|
use Drupal\Core\Field\BaseFieldDefinition;
|
||||||
|
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||||
use Drupal\Core\Field\FieldStorageDefinitionEvent;
|
use Drupal\Core\Field\FieldStorageDefinitionEvent;
|
||||||
use Drupal\Core\Field\FieldStorageDefinitionEvents;
|
use Drupal\Core\Field\FieldStorageDefinitionEvents;
|
||||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||||
use Drupal\Core\Field\FieldStorageDefinitionListenerInterface;
|
use Drupal\Core\Field\FieldStorageDefinitionListenerInterface;
|
||||||
|
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
|
||||||
use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
|
use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
|
||||||
use Drupal\Core\Language\LanguageInterface;
|
use Drupal\Core\Language\LanguageInterface;
|
||||||
use Drupal\Core\Language\LanguageManagerInterface;
|
use Drupal\Core\Language\LanguageManagerInterface;
|
||||||
|
@ -121,11 +123,11 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
|
||||||
protected $languageManager;
|
protected $languageManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The keyvalue collection for tracking installed definitions.
|
* The keyvalue factory.
|
||||||
*
|
*
|
||||||
* @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface
|
* @var \Drupal\Core\KeyValueStore\KeyValueFactoryInterface
|
||||||
*/
|
*/
|
||||||
protected $installedDefinitions;
|
protected $keyValueFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The event dispatcher.
|
* The event dispatcher.
|
||||||
|
@ -193,12 +195,12 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
|
||||||
* The class resolver.
|
* The class resolver.
|
||||||
* @param \Drupal\Core\TypedData\TypedDataManager $typed_data_manager
|
* @param \Drupal\Core\TypedData\TypedDataManager $typed_data_manager
|
||||||
* The typed data manager.
|
* The typed data manager.
|
||||||
* @param \Drupal\Core\KeyValueStore\KeyValueStoreInterface $installed_definitions
|
* @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value_factory
|
||||||
* The keyvalue collection for tracking installed definitions.
|
* The keyvalue factory.
|
||||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
|
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
|
||||||
* The event dispatcher.
|
* The event dispatcher.
|
||||||
*/
|
*/
|
||||||
public function __construct(\Traversable $namespaces, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, TranslationInterface $translation_manager, ClassResolverInterface $class_resolver, TypedDataManager $typed_data_manager, KeyValueStoreInterface $installed_definitions, EventDispatcherInterface $event_dispatcher) {
|
public function __construct(\Traversable $namespaces, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, TranslationInterface $translation_manager, ClassResolverInterface $class_resolver, TypedDataManager $typed_data_manager, KeyValueFactoryInterface $key_value_factory, EventDispatcherInterface $event_dispatcher) {
|
||||||
parent::__construct('Entity', $namespaces, $module_handler, 'Drupal\Core\Entity\EntityInterface');
|
parent::__construct('Entity', $namespaces, $module_handler, 'Drupal\Core\Entity\EntityInterface');
|
||||||
|
|
||||||
$this->setCacheBackend($cache, 'entity_type', array('entity_types'));
|
$this->setCacheBackend($cache, 'entity_type', array('entity_types'));
|
||||||
|
@ -209,7 +211,7 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
|
||||||
$this->translationManager = $translation_manager;
|
$this->translationManager = $translation_manager;
|
||||||
$this->classResolver = $class_resolver;
|
$this->classResolver = $class_resolver;
|
||||||
$this->typedDataManager = $typed_data_manager;
|
$this->typedDataManager = $typed_data_manager;
|
||||||
$this->installedDefinitions = $installed_definitions;
|
$this->keyValueFactory = $key_value_factory;
|
||||||
$this->eventDispatcher = $event_dispatcher;
|
$this->eventDispatcher = $event_dispatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -642,19 +644,43 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
|
||||||
$this->fieldMap = $cache->data;
|
$this->fieldMap = $cache->data;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Rebuild the definitions and put it into the cache.
|
// The field map is built in two steps. First, add all base fields, by
|
||||||
|
// looping over all fieldable entity types. They always exist for all
|
||||||
|
// bundles, and we do not expect to have so many different entity
|
||||||
|
// types for this to become a bottleneck.
|
||||||
foreach ($this->getDefinitions() as $entity_type_id => $entity_type) {
|
foreach ($this->getDefinitions() as $entity_type_id => $entity_type) {
|
||||||
if ($entity_type->isSubclassOf('\Drupal\Core\Entity\FieldableEntityInterface')) {
|
if ($entity_type->isSubclassOf('\Drupal\Core\Entity\FieldableEntityInterface')) {
|
||||||
foreach ($this->getBundleInfo($entity_type_id) as $bundle => $bundle_info) {
|
$bundles = array_keys($this->getBundleInfo($entity_type_id));
|
||||||
foreach ($this->getFieldDefinitions($entity_type_id, $bundle) as $field_name => $field_definition) {
|
foreach ($this->getBaseFieldDefinitions($entity_type_id) as $field_name => $base_field_definition) {
|
||||||
$this->fieldMap[$entity_type_id][$field_name]['type'] = $field_definition->getType();
|
$this->fieldMap[$entity_type_id][$field_name] = [
|
||||||
$this->fieldMap[$entity_type_id][$field_name]['bundles'][] = $bundle;
|
'type' => $base_field_definition->getType(),
|
||||||
}
|
'bundles' => array_combine($bundles, $bundles),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->cacheSet($cid, $this->fieldMap, Cache::PERMANENT, array('entity_types', 'entity_field_info'));
|
// In the second step, the per-bundle fields are added, based on the
|
||||||
|
// persistent bundle field map stored in a key value collection. This
|
||||||
|
// data is managed in the EntityManager::onFieldDefinitionCreate()
|
||||||
|
// and EntityManager::onFieldDefinitionDelete() methods. Rebuilding this
|
||||||
|
// information in the same way as base fields would not scale, as the
|
||||||
|
// time to query would grow exponentially with more fields and bundles.
|
||||||
|
// A cache would be deleted during cache clears, which is the only time
|
||||||
|
// it is needed, so a key value collection is used.
|
||||||
|
$bundle_field_maps = $this->keyValueFactory->get('entity.definitions.bundle_field_map')->getAll();
|
||||||
|
foreach ($bundle_field_maps as $entity_type_id => $bundle_field_map) {
|
||||||
|
foreach ($bundle_field_map as $field_name => $map_entry) {
|
||||||
|
if (!isset($this->fieldMap[$entity_type_id][$field_name])) {
|
||||||
|
$this->fieldMap[$entity_type_id][$field_name] = $map_entry;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->fieldMap[$entity_type_id][$field_name]['bundles'] += $map_entry['bundles'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cacheSet($cid, $this->fieldMap, Cache::PERMANENT, array('entity_types'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $this->fieldMap;
|
return $this->fieldMap;
|
||||||
|
@ -679,6 +705,89 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
|
||||||
return $this->fieldMapByFieldType[$field_type];
|
return $this->fieldMapByFieldType[$field_type];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onFieldDefinitionCreate(FieldDefinitionInterface $field_definition) {
|
||||||
|
$entity_type_id = $field_definition->getTargetEntityTypeId();
|
||||||
|
$bundle = $field_definition->getTargetBundle();
|
||||||
|
$field_name = $field_definition->getName();
|
||||||
|
|
||||||
|
// Notify the storage about the new field.
|
||||||
|
$this->getStorage($entity_type_id)->onFieldDefinitionCreate($field_definition);
|
||||||
|
|
||||||
|
// Update the bundle field map key value collection, add the new field.
|
||||||
|
$bundle_field_map = $this->keyValueFactory->get('entity.definitions.bundle_field_map')->get($entity_type_id);
|
||||||
|
if (!isset($bundle_field_map[$field_name])) {
|
||||||
|
// This field did not exist yet, initialize it with the type and empty
|
||||||
|
// bundle list.
|
||||||
|
$bundle_field_map[$field_name] = [
|
||||||
|
'type' => $field_definition->getType(),
|
||||||
|
'bundles' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$bundle_field_map[$field_name]['bundles'][$bundle] = $bundle;
|
||||||
|
$this->keyValueFactory->get('entity.definitions.bundle_field_map')->set($entity_type_id, $bundle_field_map);
|
||||||
|
|
||||||
|
// Delete the cache entry.
|
||||||
|
$this->cacheBackend->delete('entity_field_map');
|
||||||
|
|
||||||
|
// If the field map is initialized, update it as well, so that calls to it
|
||||||
|
// do not have to rebuild it again.
|
||||||
|
if ($this->fieldMap) {
|
||||||
|
if (!isset($this->fieldMap[$entity_type_id][$field_name])) {
|
||||||
|
// This field did not exist yet, initialize it with the type and empty
|
||||||
|
// bundle list.
|
||||||
|
$this->fieldMap[$entity_type_id][$field_name] = [
|
||||||
|
'type' => $field_definition->getType(),
|
||||||
|
'bundles' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$this->fieldMap[$entity_type_id][$field_name]['bundles'][$bundle] = $bundle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onFieldDefinitionUpdate(FieldDefinitionInterface $field_definition, FieldDefinitionInterface $original) {
|
||||||
|
// Notify the storage about the updated field.
|
||||||
|
$this->getStorage($field_definition->getTargetEntityTypeId())->onFieldDefinitionUpdate($field_definition, $original);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function onFieldDefinitionDelete(FieldDefinitionInterface $field_definition) {
|
||||||
|
$entity_type_id = $field_definition->getTargetEntityTypeId();
|
||||||
|
$bundle = $field_definition->getTargetBundle();
|
||||||
|
$field_name = $field_definition->getName();
|
||||||
|
|
||||||
|
// Notify the storage about the field deletion.
|
||||||
|
$this->getStorage($entity_type_id)->onFieldDefinitionDelete($field_definition);
|
||||||
|
|
||||||
|
// Unset the bundle from the bundle field map key value collection.
|
||||||
|
$bundle_field_map = $this->keyValueFactory->get('entity.definitions.bundle_field_map')->get($entity_type_id);
|
||||||
|
unset($bundle_field_map[$field_name]['bundles'][$bundle]);
|
||||||
|
if (empty($bundle_field_map[$field_name]['bundles'])) {
|
||||||
|
// If there are no bundles left, remove the field from the map.
|
||||||
|
unset($bundle_field_map[$field_name]);
|
||||||
|
}
|
||||||
|
$this->keyValueFactory->get('entity.definitions.bundle_field_map')->set($entity_type_id, $bundle_field_map);
|
||||||
|
|
||||||
|
// Delete the cache entry.
|
||||||
|
$this->cacheBackend->delete('entity_field_map');
|
||||||
|
|
||||||
|
// If the field map is initialized, update it as well, so that calls to it
|
||||||
|
// do not have to rebuild it again.
|
||||||
|
if ($this->fieldMap) {
|
||||||
|
unset($this->fieldMap[$entity_type_id][$field_name]['bundles'][$bundle]);
|
||||||
|
if (empty($this->fieldMap[$entity_type_id][$field_name]['bundles'])) {
|
||||||
|
unset($this->fieldMap[$entity_type_id][$field_name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds field storage definitions for an entity type.
|
* Builds field storage definitions for an entity type.
|
||||||
*
|
*
|
||||||
|
@ -1243,7 +1352,7 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function getLastInstalledDefinition($entity_type_id) {
|
public function getLastInstalledDefinition($entity_type_id) {
|
||||||
return $this->installedDefinitions->get($entity_type_id . '.entity_type');
|
return $this->keyValueFactory->get('entity.definitions.installed')->get($entity_type_id . '.entity_type');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1267,7 +1376,7 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
|
||||||
*/
|
*/
|
||||||
protected function setLastInstalledDefinition(EntityTypeInterface $entity_type) {
|
protected function setLastInstalledDefinition(EntityTypeInterface $entity_type) {
|
||||||
$entity_type_id = $entity_type->id();
|
$entity_type_id = $entity_type->id();
|
||||||
$this->installedDefinitions->set($entity_type_id . '.entity_type', $entity_type);
|
$this->keyValueFactory->get('entity.definitions.installed')->set($entity_type_id . '.entity_type', $entity_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1277,18 +1386,18 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
|
||||||
* The entity type definition identifier.
|
* The entity type definition identifier.
|
||||||
*/
|
*/
|
||||||
protected function deleteLastInstalledDefinition($entity_type_id) {
|
protected function deleteLastInstalledDefinition($entity_type_id) {
|
||||||
$this->installedDefinitions->delete($entity_type_id . '.entity_type');
|
$this->keyValueFactory->get('entity.definitions.installed')->delete($entity_type_id . '.entity_type');
|
||||||
// Clean up field storage definitions as well. Even if the entity type
|
// Clean up field storage definitions as well. Even if the entity type
|
||||||
// isn't currently fieldable, there might be legacy definitions or an
|
// isn't currently fieldable, there might be legacy definitions or an
|
||||||
// empty array stored from when it was.
|
// empty array stored from when it was.
|
||||||
$this->installedDefinitions->delete($entity_type_id . '.field_storage_definitions');
|
$this->keyValueFactory->get('entity.definitions.installed')->delete($entity_type_id . '.field_storage_definitions');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function getLastInstalledFieldStorageDefinitions($entity_type_id) {
|
public function getLastInstalledFieldStorageDefinitions($entity_type_id) {
|
||||||
return $this->installedDefinitions->get($entity_type_id . '.field_storage_definitions', array());
|
return $this->keyValueFactory->get('entity.definitions.installed')->get($entity_type_id . '.field_storage_definitions', array());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1300,7 +1409,7 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
|
||||||
* An array of field storage definitions.
|
* An array of field storage definitions.
|
||||||
*/
|
*/
|
||||||
protected function setLastInstalledFieldStorageDefinitions($entity_type_id, array $storage_definitions) {
|
protected function setLastInstalledFieldStorageDefinitions($entity_type_id, array $storage_definitions) {
|
||||||
$this->installedDefinitions->set($entity_type_id . '.field_storage_definitions', $storage_definitions);
|
$this->keyValueFactory->get('entity.definitions.installed')->set($entity_type_id . '.field_storage_definitions', $storage_definitions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -9,12 +9,13 @@ namespace Drupal\Core\Entity;
|
||||||
|
|
||||||
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.
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?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 @@ class FieldConfig extends FieldConfigBase implements FieldConfigInterface {
|
||||||
|
|
||||||
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 @@ class FieldConfig extends FieldConfigBase implements FieldConfigInterface {
|
||||||
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 @@ class FieldConfig extends FieldConfigBase implements FieldConfigInterface {
|
||||||
// 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 @@ class EntityBundleFieldTest extends EntityUnitTestBase {
|
||||||
'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 @@ class EntityBundleFieldTest extends EntityUnitTestBase {
|
||||||
->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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
->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']);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,11 +114,11 @@ class EntityManagerTest extends UnitTestCase {
|
||||||
protected $typedDataManager;
|
protected $typedDataManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The keyvalue collection for tracking installed definitions.
|
* The keyvalue factory.
|
||||||
*
|
*
|
||||||
* @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface|\PHPUnit_Framework_MockObject_MockObject
|
* @var \Drupal\Core\KeyValueStore\KeyValueFactoryInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||||
*/
|
*/
|
||||||
protected $installedDefinitions;
|
protected $keyValueFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The event dispatcher.
|
* The event dispatcher.
|
||||||
|
@ -183,7 +183,7 @@ class EntityManagerTest extends UnitTestCase {
|
||||||
|
|
||||||
$this->eventDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
|
$this->eventDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
|
||||||
|
|
||||||
$this->installedDefinitions = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface');
|
$this->keyValueFactory = $this->getMock('Drupal\Core\KeyValueStore\KeyValueFactoryInterface');
|
||||||
|
|
||||||
$this->container = $this->getContainerWithCacheTagsInvalidator($this->cacheTagsInvalidator);
|
$this->container = $this->getContainerWithCacheTagsInvalidator($this->cacheTagsInvalidator);
|
||||||
$this->container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
|
$this->container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
|
||||||
|
@ -239,7 +239,7 @@ class EntityManagerTest extends UnitTestCase {
|
||||||
->method('getDefinitions')
|
->method('getDefinitions')
|
||||||
->will($this->returnValue($definitions));
|
->will($this->returnValue($definitions));
|
||||||
|
|
||||||
$this->entityManager = new TestEntityManager(new \ArrayObject(), $this->moduleHandler, $this->cacheBackend, $this->languageManager, $this->translationManager, $this->getClassResolverStub(), $this->typedDataManager, $this->installedDefinitions, $this->eventDispatcher);
|
$this->entityManager = new TestEntityManager(new \ArrayObject(), $this->moduleHandler, $this->cacheBackend, $this->languageManager, $this->translationManager, $this->getClassResolverStub(), $this->typedDataManager, $this->keyValueFactory, $this->eventDispatcher);
|
||||||
$this->entityManager->setContainer($this->container);
|
$this->entityManager->setContainer($this->container);
|
||||||
$this->entityManager->setDiscovery($this->discovery);
|
$this->entityManager->setDiscovery($this->discovery);
|
||||||
}
|
}
|
||||||
|
@ -1250,7 +1250,7 @@ class EntityManagerTest extends UnitTestCase {
|
||||||
$id_definition = $this->getMockBuilder('Drupal\Core\Field\BaseFieldDefinition')
|
$id_definition = $this->getMockBuilder('Drupal\Core\Field\BaseFieldDefinition')
|
||||||
->disableOriginalConstructor()
|
->disableOriginalConstructor()
|
||||||
->getMock();
|
->getMock();
|
||||||
$id_definition->expects($this->exactly(2))
|
$id_definition->expects($this->once())
|
||||||
->method('getType')
|
->method('getType')
|
||||||
->will($this->returnValue('integer'));
|
->will($this->returnValue('integer'));
|
||||||
$base_field_definitions = array(
|
$base_field_definitions = array(
|
||||||
|
@ -1258,21 +1258,20 @@ class EntityManagerTest extends UnitTestCase {
|
||||||
);
|
);
|
||||||
$entity_class::$baseFieldDefinitions = $base_field_definitions;
|
$entity_class::$baseFieldDefinitions = $base_field_definitions;
|
||||||
|
|
||||||
// Set up a by bundle field definition that only exists on one bundle.
|
// Set up the stored bundle field map.
|
||||||
$bundle_definition = $this->getMockBuilder('Drupal\Core\Field\BaseFieldDefinition')
|
$key_value_store = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface');
|
||||||
->disableOriginalConstructor()
|
$this->keyValueFactory->expects($this->once())
|
||||||
->getMock();
|
->method('get')
|
||||||
$bundle_definition->expects($this->once())
|
->with('entity.definitions.bundle_field_map')
|
||||||
->method('getType')
|
->willReturn($key_value_store);
|
||||||
->will($this->returnValue('string'));
|
$key_value_store->expects($this->once())
|
||||||
$entity_class::$bundleFieldDefinitions = array(
|
->method('getAll')
|
||||||
'test_entity_type' => array(
|
->willReturn(['test_entity_type' => [
|
||||||
'first_bundle' => array(),
|
'by_bundle' => [
|
||||||
'second_bundle' => array(
|
'type' => 'string',
|
||||||
'by_bundle' => $bundle_definition,
|
'bundles' => ['second_bundle' => 'second_bundle'],
|
||||||
),
|
],
|
||||||
),
|
]]);
|
||||||
);
|
|
||||||
|
|
||||||
// Set up a non-content entity type.
|
// Set up a non-content entity type.
|
||||||
$non_content_entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
|
$non_content_entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
|
||||||
|
@ -1303,11 +1302,11 @@ class EntityManagerTest extends UnitTestCase {
|
||||||
'test_entity_type' => array(
|
'test_entity_type' => array(
|
||||||
'id' => array(
|
'id' => array(
|
||||||
'type' => 'integer',
|
'type' => 'integer',
|
||||||
'bundles' => array('first_bundle', 'second_bundle'),
|
'bundles' => array('first_bundle' => 'first_bundle', 'second_bundle' => 'second_bundle'),
|
||||||
),
|
),
|
||||||
'by_bundle' => array(
|
'by_bundle' => array(
|
||||||
'type' => 'string',
|
'type' => 'string',
|
||||||
'bundles' => array('second_bundle'),
|
'bundles' => array('second_bundle' => 'second_bundle'),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -1322,11 +1321,11 @@ class EntityManagerTest extends UnitTestCase {
|
||||||
'test_entity_type' => array(
|
'test_entity_type' => array(
|
||||||
'id' => array(
|
'id' => array(
|
||||||
'type' => 'integer',
|
'type' => 'integer',
|
||||||
'bundles' => array('first_bundle', 'second_bundle'),
|
'bundles' => array('first_bundle' => 'first_bundle', 'second_bundle' => 'second_bundle'),
|
||||||
),
|
),
|
||||||
'by_bundle' => array(
|
'by_bundle' => array(
|
||||||
'type' => 'string',
|
'type' => 'string',
|
||||||
'bundles' => array('second_bundle'),
|
'bundles' => array('second_bundle' => 'second_bundle'),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -1393,7 +1392,7 @@ class EntityManagerTest extends UnitTestCase {
|
||||||
$id_definition = $this->getMockBuilder('Drupal\Core\Field\BaseFieldDefinition')
|
$id_definition = $this->getMockBuilder('Drupal\Core\Field\BaseFieldDefinition')
|
||||||
->disableOriginalConstructor()
|
->disableOriginalConstructor()
|
||||||
->getMock();
|
->getMock();
|
||||||
$id_definition->expects($this->exactly(2))
|
$id_definition->expects($this->once())
|
||||||
->method('getType')
|
->method('getType')
|
||||||
->will($this->returnValue('integer'));
|
->will($this->returnValue('integer'));
|
||||||
$base_field_definitions = array(
|
$base_field_definitions = array(
|
||||||
|
@ -1401,21 +1400,20 @@ class EntityManagerTest extends UnitTestCase {
|
||||||
);
|
);
|
||||||
$entity_class::$baseFieldDefinitions = $base_field_definitions;
|
$entity_class::$baseFieldDefinitions = $base_field_definitions;
|
||||||
|
|
||||||
// Set up a by bundle field definition that only exists on one bundle.
|
// Set up the stored bundle field map.
|
||||||
$bundle_definition = $this->getMockBuilder('Drupal\Core\Field\BaseFieldDefinition')
|
$key_value_store = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface');
|
||||||
->disableOriginalConstructor()
|
$this->keyValueFactory->expects($this->once())
|
||||||
->getMock();
|
->method('get')
|
||||||
$bundle_definition->expects($this->once())
|
->with('entity.definitions.bundle_field_map')
|
||||||
->method('getType')
|
->willReturn($key_value_store);
|
||||||
->will($this->returnValue('string'));
|
$key_value_store->expects($this->once())
|
||||||
$entity_class::$bundleFieldDefinitions = array(
|
->method('getAll')
|
||||||
'test_entity_type' => array(
|
->willReturn(['test_entity_type' => [
|
||||||
'first_bundle' => array(),
|
'by_bundle' => [
|
||||||
'second_bundle' => array(
|
'type' => 'string',
|
||||||
'by_bundle' => $bundle_definition,
|
'bundles' => ['second_bundle' => 'second_bundle'],
|
||||||
),
|
],
|
||||||
),
|
]]);
|
||||||
);
|
|
||||||
|
|
||||||
// Mock the base field definition override.
|
// Mock the base field definition override.
|
||||||
$override_entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
|
$override_entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
|
||||||
|
@ -1446,6 +1444,271 @@ class EntityManagerTest extends UnitTestCase {
|
||||||
$this->assertArrayNotHasKey('id', $stringFields['test_entity_type']);
|
$this->assertArrayNotHasKey('id', $stringFields['test_entity_type']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::onFieldDefinitionCreate
|
||||||
|
*/
|
||||||
|
public function testonFieldDefinitionCreateNewField() {
|
||||||
|
$field_definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
|
||||||
|
$field_definition->expects($this->atLeastOnce())
|
||||||
|
->method('getTargetEntityTypeId')
|
||||||
|
->willReturn('test_entity_type');
|
||||||
|
$field_definition->expects($this->atLeastOnce())
|
||||||
|
->method('getTargetBundle')
|
||||||
|
->willReturn('test_bundle');
|
||||||
|
$field_definition->expects($this->atLeastOnce())
|
||||||
|
->method('getName')
|
||||||
|
->willReturn('test_field');
|
||||||
|
$field_definition->expects($this->atLeastOnce())
|
||||||
|
->method('getType')
|
||||||
|
->willReturn('test_type');
|
||||||
|
|
||||||
|
$storage = $this->getMock('Drupal\Core\Entity\DynamicallyFieldableEntityStorageInterface');
|
||||||
|
|
||||||
|
$class = get_class($storage);
|
||||||
|
$entity = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
|
||||||
|
$entity->expects($this->once())
|
||||||
|
->method('getHandlerClass')
|
||||||
|
->with('storage')
|
||||||
|
->will($this->returnValue($class));
|
||||||
|
$this->setUpEntityManager(array('test_entity_type' => $entity));
|
||||||
|
|
||||||
|
// The entity manager will instantiate a new object with the given class
|
||||||
|
// name. Define the mock expectations on that.
|
||||||
|
$storage = $this->entityManager->getStorage('test_entity_type');
|
||||||
|
$storage->expects($this->once())
|
||||||
|
->method('onFieldDefinitionCreate')
|
||||||
|
->with($field_definition);
|
||||||
|
|
||||||
|
// Set up the stored bundle field map.
|
||||||
|
$key_value_store = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface');
|
||||||
|
$this->keyValueFactory->expects($this->exactly(2))
|
||||||
|
->method('get')
|
||||||
|
->with('entity.definitions.bundle_field_map')
|
||||||
|
->willReturn($key_value_store);
|
||||||
|
$key_value_store->expects($this->once())
|
||||||
|
->method('get')
|
||||||
|
->with('test_entity_type')
|
||||||
|
->willReturn([]);
|
||||||
|
$key_value_store->expects($this->once())
|
||||||
|
->method('set')
|
||||||
|
->with('test_entity_type', [
|
||||||
|
'test_field' => [
|
||||||
|
'type' => 'test_type',
|
||||||
|
'bundles' => ['test_bundle' => 'test_bundle'],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->entityManager->onFieldDefinitionCreate($field_definition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::onFieldDefinitionCreate
|
||||||
|
*/
|
||||||
|
public function testonFieldDefinitionCreateExistingField() {
|
||||||
|
$field_definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
|
||||||
|
$field_definition->expects($this->atLeastOnce())
|
||||||
|
->method('getTargetEntityTypeId')
|
||||||
|
->willReturn('test_entity_type');
|
||||||
|
$field_definition->expects($this->atLeastOnce())
|
||||||
|
->method('getTargetBundle')
|
||||||
|
->willReturn('test_bundle');
|
||||||
|
$field_definition->expects($this->atLeastOnce())
|
||||||
|
->method('getName')
|
||||||
|
->willReturn('test_field');
|
||||||
|
|
||||||
|
$storage = $this->getMock('Drupal\Core\Entity\DynamicallyFieldableEntityStorageInterface');
|
||||||
|
$class = get_class($storage);
|
||||||
|
$entity = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
|
||||||
|
$entity->expects($this->once())
|
||||||
|
->method('getHandlerClass')
|
||||||
|
->with('storage')
|
||||||
|
->will($this->returnValue($class));
|
||||||
|
$this->setUpEntityManager(array('test_entity_type' => $entity));
|
||||||
|
|
||||||
|
// The entity manager will instantiate a new object with the given class
|
||||||
|
// name. Define the mock expectations on that.
|
||||||
|
$storage = $this->entityManager->getStorage('test_entity_type');
|
||||||
|
$storage->expects($this->once())
|
||||||
|
->method('onFieldDefinitionCreate')
|
||||||
|
->with($field_definition);
|
||||||
|
|
||||||
|
// Set up the stored bundle field map.
|
||||||
|
$key_value_store = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface');
|
||||||
|
$this->keyValueFactory->expects($this->exactly(2))
|
||||||
|
->method('get')
|
||||||
|
->with('entity.definitions.bundle_field_map')
|
||||||
|
->willReturn($key_value_store);
|
||||||
|
$key_value_store->expects($this->once())
|
||||||
|
->method('get')
|
||||||
|
->with('test_entity_type')
|
||||||
|
->willReturn([
|
||||||
|
'test_field' => [
|
||||||
|
'type' => 'test_type',
|
||||||
|
'bundles' => ['existing_bundle' => 'existing_bundle'],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$key_value_store->expects($this->once())
|
||||||
|
->method('set')
|
||||||
|
->with('test_entity_type', [
|
||||||
|
'test_field' => [
|
||||||
|
'type' => 'test_type',
|
||||||
|
'bundles' => ['existing_bundle' => 'existing_bundle', 'test_bundle' => 'test_bundle'],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->entityManager->onFieldDefinitionCreate($field_definition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::onFieldDefinitionUpdate
|
||||||
|
*/
|
||||||
|
public function testonFieldDefinitionUpdate() {
|
||||||
|
$field_definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
|
||||||
|
$field_definition->expects($this->atLeastOnce())
|
||||||
|
->method('getTargetEntityTypeId')
|
||||||
|
->willReturn('test_entity_type');
|
||||||
|
|
||||||
|
$storage = $this->getMock('Drupal\Core\Entity\DynamicallyFieldableEntityStorageInterface');
|
||||||
|
|
||||||
|
$class = get_class($storage);
|
||||||
|
$entity = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
|
||||||
|
$entity->expects($this->once())
|
||||||
|
->method('getHandlerClass')
|
||||||
|
->with('storage')
|
||||||
|
->will($this->returnValue($class));
|
||||||
|
$this->setUpEntityManager(array('test_entity_type' => $entity));
|
||||||
|
|
||||||
|
// The entity manager will instantiate a new object with the given class
|
||||||
|
// name. Define the mock expectations on that.
|
||||||
|
$storage = $this->entityManager->getStorage('test_entity_type');
|
||||||
|
$storage->expects($this->once())
|
||||||
|
->method('onFieldDefinitionUpdate')
|
||||||
|
->with($field_definition);
|
||||||
|
|
||||||
|
$this->entityManager->onFieldDefinitionUpdate($field_definition, $field_definition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::onFieldDefinitionDelete
|
||||||
|
*/
|
||||||
|
public function testonFieldDefinitionDeleteMultipleBundles() {
|
||||||
|
$field_definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
|
||||||
|
$field_definition->expects($this->atLeastOnce())
|
||||||
|
->method('getTargetEntityTypeId')
|
||||||
|
->willReturn('test_entity_type');
|
||||||
|
$field_definition->expects($this->atLeastOnce())
|
||||||
|
->method('getTargetBundle')
|
||||||
|
->willReturn('test_bundle');
|
||||||
|
$field_definition->expects($this->atLeastOnce())
|
||||||
|
->method('getName')
|
||||||
|
->willReturn('test_field');
|
||||||
|
|
||||||
|
$storage = $this->getMock('Drupal\Core\Entity\DynamicallyFieldableEntityStorageInterface');
|
||||||
|
$class = get_class($storage);
|
||||||
|
$entity = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
|
||||||
|
$entity->expects($this->once())
|
||||||
|
->method('getHandlerClass')
|
||||||
|
->with('storage')
|
||||||
|
->will($this->returnValue($class));
|
||||||
|
$this->setUpEntityManager(array('test_entity_type' => $entity));
|
||||||
|
|
||||||
|
// The entity manager will instantiate a new object with the given class
|
||||||
|
// name. Define the mock expectations on that.
|
||||||
|
$storage = $this->entityManager->getStorage('test_entity_type');
|
||||||
|
$storage->expects($this->once())
|
||||||
|
->method('onFieldDefinitionDelete')
|
||||||
|
->with($field_definition);
|
||||||
|
|
||||||
|
// Set up the stored bundle field map.
|
||||||
|
$key_value_store = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface');
|
||||||
|
$this->keyValueFactory->expects($this->exactly(2))
|
||||||
|
->method('get')
|
||||||
|
->with('entity.definitions.bundle_field_map')
|
||||||
|
->willReturn($key_value_store);
|
||||||
|
$key_value_store->expects($this->once())
|
||||||
|
->method('get')
|
||||||
|
->with('test_entity_type')
|
||||||
|
->willReturn([
|
||||||
|
'test_field' => [
|
||||||
|
'type' => 'test_type',
|
||||||
|
'bundles' => ['test_bundle' => 'test_bundle'],
|
||||||
|
],
|
||||||
|
'second_field' => [
|
||||||
|
'type' => 'test_type',
|
||||||
|
'bundles' => ['test_bundle' => 'test_bundle'],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$key_value_store->expects($this->once())
|
||||||
|
->method('set')
|
||||||
|
->with('test_entity_type', [
|
||||||
|
'second_field' => [
|
||||||
|
'type' => 'test_type',
|
||||||
|
'bundles' => ['test_bundle' => 'test_bundle'],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->entityManager->onFieldDefinitionDelete($field_definition);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::onFieldDefinitionDelete
|
||||||
|
*/
|
||||||
|
public function testonFieldDefinitionDeleteSingleBundles() {
|
||||||
|
$field_definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
|
||||||
|
$field_definition->expects($this->atLeastOnce())
|
||||||
|
->method('getTargetEntityTypeId')
|
||||||
|
->willReturn('test_entity_type');
|
||||||
|
$field_definition->expects($this->atLeastOnce())
|
||||||
|
->method('getTargetBundle')
|
||||||
|
->willReturn('test_bundle');
|
||||||
|
$field_definition->expects($this->atLeastOnce())
|
||||||
|
->method('getName')
|
||||||
|
->willReturn('test_field');
|
||||||
|
|
||||||
|
$storage = $this->getMock('Drupal\Core\Entity\DynamicallyFieldableEntityStorageInterface');
|
||||||
|
$class = get_class($storage);
|
||||||
|
$entity = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
|
||||||
|
$entity->expects($this->once())
|
||||||
|
->method('getHandlerClass')
|
||||||
|
->with('storage')
|
||||||
|
->will($this->returnValue($class));
|
||||||
|
$this->setUpEntityManager(array('test_entity_type' => $entity));
|
||||||
|
|
||||||
|
// The entity manager will instantiate a new object with the given class
|
||||||
|
// name. Define the mock expectations on that.
|
||||||
|
$storage = $this->entityManager->getStorage('test_entity_type');
|
||||||
|
$storage->expects($this->once())
|
||||||
|
->method('onFieldDefinitionDelete')
|
||||||
|
->with($field_definition);
|
||||||
|
|
||||||
|
// Set up the stored bundle field map.
|
||||||
|
$key_value_store = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface');
|
||||||
|
$this->keyValueFactory->expects($this->exactly(2))
|
||||||
|
->method('get')
|
||||||
|
->with('entity.definitions.bundle_field_map')
|
||||||
|
->willReturn($key_value_store);
|
||||||
|
$key_value_store->expects($this->once())
|
||||||
|
->method('get')
|
||||||
|
->with('test_entity_type')
|
||||||
|
->willReturn([
|
||||||
|
'test_field' => [
|
||||||
|
'type' => 'test_type',
|
||||||
|
'bundles' => ['test_bundle' => 'test_bundle', 'second_bundle' => 'second_bundle'],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$key_value_store->expects($this->once())
|
||||||
|
->method('set')
|
||||||
|
->with('test_entity_type', [
|
||||||
|
'test_field' => [
|
||||||
|
'type' => 'test_type',
|
||||||
|
'bundles' => ['second_bundle' => 'second_bundle'],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->entityManager->onFieldDefinitionDelete($field_definition);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers ::getEntityTypeFromClass
|
* @covers ::getEntityTypeFromClass
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue