Commit 2bf5dc11 authored by catch's avatar catch

Issue #2443409 by Berdir: Add a way to entity manager to get fresh entity and...

Issue #2443409 by Berdir: Add a way to entity manager to get fresh entity and field definitions without invalidating all caches
parent 71838159
......@@ -21,4 +21,17 @@ interface CachedDiscoveryInterface extends DiscoveryInterface {
*/
public function clearCachedDefinitions();
/**
* Disable the use of caches.
*
* Can be used to ensure that uncached plugin definitions are returned,
* without invalidating all cached information.
*
* This will also remove all local/static caches.
*
* @param bool $use_caches
* FALSE to not use any caches.
*/
public function useCaches($use_caches = FALSE);
}
......@@ -95,7 +95,13 @@ public function getChangeSummary() {
* {@inheritdoc}
*/
public function applyUpdates() {
foreach ($this->getChangeList() as $entity_type_id => $change_list) {
$change_list = $this->getChangeList();
if ($change_list) {
// getChangeList() only disables the cache and does not invalidate.
// In case there are changes, explictly invalidate caches.
$this->entityManager->clearCachedDefinitions();
}
foreach ($change_list as $entity_type_id => $change_list) {
// Process entity type definition changes before storage definitions ones
// this is necessary when you change an entity type from non-revisionable
// to revisionable and at the same time add revisionable fields to the
......@@ -153,7 +159,7 @@ public function applyUpdates() {
* - DEFINITION_DELETED
*/
protected function getChangeList() {
$this->entityManager->clearCachedDefinitions();
$this->entityManager->useCaches(FALSE);
$change_list = array();
foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) {
......@@ -208,6 +214,8 @@ protected function getChangeList() {
// @todo Support deleting entity definitions when we support base field
// purging. See https://www.drupal.org/node/2282119.
$this->entityManager->useCaches(TRUE);
return array_filter($change_list);
}
......
......@@ -372,13 +372,13 @@ public function getBaseFieldDefinitions($entity_type_id) {
if (!isset($this->baseFieldDefinitions[$entity_type_id])) {
// Not prepared, try to load from cache.
$cid = 'entity_base_field_definitions:' . $entity_type_id . ':' . $this->languageManager->getCurrentLanguage()->getId();
if ($cache = $this->cacheBackend->get($cid)) {
if ($cache = $this->cacheGet($cid)) {
$this->baseFieldDefinitions[$entity_type_id] = $cache->data;
}
else {
// Rebuild the definitions and put it into the cache.
$this->baseFieldDefinitions[$entity_type_id] = $this->buildBaseFieldDefinitions($entity_type_id);
$this->cacheBackend->set($cid, $this->baseFieldDefinitions[$entity_type_id], Cache::PERMANENT, array('entity_types', 'entity_field_info'));
$this->cacheSet($cid, $this->baseFieldDefinitions[$entity_type_id], Cache::PERMANENT, array('entity_types', 'entity_field_info'));
}
}
return $this->baseFieldDefinitions[$entity_type_id];
......@@ -470,13 +470,13 @@ public function getFieldDefinitions($entity_type_id, $bundle) {
$base_field_definitions = $this->getBaseFieldDefinitions($entity_type_id);
// Not prepared, try to load from cache.
$cid = 'entity_bundle_field_definitions:' . $entity_type_id . ':' . $bundle . ':' . $this->languageManager->getCurrentLanguage()->getId();
if ($cache = $this->cacheBackend->get($cid)) {
if ($cache = $this->cacheGet($cid)) {
$bundle_field_definitions = $cache->data;
}
else {
// Rebuild the definitions and put it into the cache.
$bundle_field_definitions = $this->buildBundleFieldDefinitions($entity_type_id, $bundle, $base_field_definitions);
$this->cacheBackend->set($cid, $bundle_field_definitions, Cache::PERMANENT, array('entity_types', 'entity_field_info'));
$this->cacheSet($cid, $bundle_field_definitions, Cache::PERMANENT, array('entity_types', 'entity_field_info'));
}
// Field definitions consist of the bundle specific overrides and the
// base fields, merge them together. Use array_replace() to replace base
......@@ -578,13 +578,13 @@ public function getFieldStorageDefinitions($entity_type_id) {
}
// Not prepared, try to load from cache.
$cid = 'entity_field_storage_definitions:' . $entity_type_id . ':' . $this->languageManager->getCurrentLanguage()->getId();
if ($cache = $this->cacheBackend->get($cid)) {
if ($cache = $this->cacheGet($cid)) {
$field_storage_definitions = $cache->data;
}
else {
// Rebuild the definitions and put it into the cache.
$field_storage_definitions = $this->buildFieldStorageDefinitions($entity_type_id);
$this->cacheBackend->set($cid, $field_storage_definitions, Cache::PERMANENT, array('entity_types', 'entity_field_info'));
$this->cacheSet($cid, $field_storage_definitions, Cache::PERMANENT, array('entity_types', 'entity_field_info'));
}
$this->fieldStorageDefinitions[$entity_type_id] += $field_storage_definitions;
}
......@@ -598,7 +598,7 @@ public function getFieldMap() {
if (!$this->fieldMap) {
// Not prepared, try to load from cache.
$cid = 'entity_field_map';
if ($cache = $this->cacheBackend->get($cid)) {
if ($cache = $this->cacheGet($cid)) {
$this->fieldMap = $cache->data;
}
else {
......@@ -614,7 +614,7 @@ public function getFieldMap() {
}
}
$this->cacheBackend->set($cid, $this->fieldMap, Cache::PERMANENT, array('entity_types', 'entity_field_info'));
$this->cacheSet($cid, $this->fieldMap, Cache::PERMANENT, array('entity_types', 'entity_field_info'));
}
}
return $this->fieldMap;
......@@ -717,7 +717,7 @@ public function getBundleInfo($entity_type) {
public function getAllBundleInfo() {
if (empty($this->bundleInfo)) {
$langcode = $this->languageManager->getCurrentLanguage()->getId();
if ($cache = $this->cacheBackend->get("entity_bundle_info:$langcode")) {
if ($cache = $this->cacheGet("entity_bundle_info:$langcode")) {
$this->bundleInfo = $cache->data;
}
else {
......@@ -738,7 +738,7 @@ public function getAllBundleInfo() {
}
}
$this->moduleHandler->alter('entity_bundle_info', $this->bundleInfo);
$this->cacheBackend->set("entity_bundle_info:$langcode", $this->bundleInfo, Cache::PERMANENT, array('entity_types', 'entity_bundles'));
$this->cacheSet("entity_bundle_info:$langcode", $this->bundleInfo, Cache::PERMANENT, array('entity_types', 'entity_bundles'));
}
}
......@@ -758,7 +758,7 @@ public function getExtraFields($entity_type_id, $bundle) {
// hook_entity_extra_field_info_alter() might contain t() calls, we cache
// per language.
$cache_id = 'entity_bundle_extra_fields:' . $entity_type_id . ':' . $bundle . ':' . $this->languageManager->getCurrentLanguage()->getId();
$cached = $this->cacheBackend->get($cache_id);
$cached = $this->cacheGet($cache_id);
if ($cached) {
$this->extraFields[$entity_type_id][$bundle] = $cached->data;
return $this->extraFields[$entity_type_id][$bundle];
......@@ -774,7 +774,7 @@ public function getExtraFields($entity_type_id, $bundle) {
// Store in the 'static' and persistent caches.
$this->extraFields[$entity_type_id][$bundle] = $info;
$this->cacheBackend->set($cache_id, $info, Cache::PERMANENT, array(
$this->cacheSet($cache_id, $info, Cache::PERMANENT, array(
'entity_field_info',
));
......@@ -886,7 +886,7 @@ protected function getAllDisplayModesByEntityType($display_type) {
$key = 'entity_' . $display_type . '_info';
$entity_type_id = 'entity_' . $display_type;
$langcode = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_INTERFACE)->getId();
if ($cache = $this->cacheBackend->get("$key:$langcode")) {
if ($cache = $this->cacheGet("$key:$langcode")) {
$this->displayModeInfo[$display_type] = $cache->data;
}
else {
......@@ -896,7 +896,7 @@ protected function getAllDisplayModesByEntityType($display_type) {
$this->displayModeInfo[$display_type][$display_mode_entity_type][$display_mode_name] = $display_mode->toArray();
}
$this->moduleHandler->alter($key, $this->displayModeInfo[$display_type]);
$this->cacheBackend->set("$key:$langcode", $this->displayModeInfo[$display_type], CacheBackendInterface::CACHE_PERMANENT, array('entity_types', 'entity_field_info'));
$this->cacheSet("$key:$langcode", $this->displayModeInfo[$display_type], CacheBackendInterface::CACHE_PERMANENT, array('entity_types', 'entity_field_info'));
}
}
......@@ -1206,6 +1206,19 @@ public function getLastInstalledDefinition($entity_type_id) {
return $this->installedDefinitions->get($entity_type_id . '.entity_type');
}
/**
* {@inheritdoc}
*/
public function useCaches($use_caches = FALSE) {
parent::useCaches($use_caches);
if (!$use_caches) {
$this->handlers = [];
$this->fieldDefinitions = [];
$this->baseFieldDefinitions = [];
$this->fieldStorageDefinitions = [];
}
}
/**
* Stores the entity type definition in the application state.
*
......
......@@ -7,13 +7,14 @@
namespace Drupal\Core\Entity;
use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Core\Field\FieldStorageDefinitionListenerInterface;
/**
* Provides an interface for entity type managers.
*/
interface EntityManagerInterface extends PluginManagerInterface, EntityTypeListenerInterface, EntityBundleListenerInterface, FieldStorageDefinitionListenerInterface {
interface EntityManagerInterface extends PluginManagerInterface, EntityTypeListenerInterface, EntityBundleListenerInterface, FieldStorageDefinitionListenerInterface, CachedDiscoveryInterface {
/**
* Builds a list of entity type labels suitable for a Form API options list.
......
......@@ -80,6 +80,13 @@ class DefaultPluginManager extends PluginManagerBase implements PluginManagerInt
*/
protected $defaults = array();
/**
* Flag whether persistent caches should be used.
*
* @var bool
*/
protected $useCaches = TRUE;
/**
* Creates the discovery object.
*
......@@ -180,7 +187,7 @@ public function clearCachedDefinitions() {
* and would actually be returned by the getDefinitions() method.
*/
protected function getCachedDefinitions() {
if (!isset($this->definitions) && $this->cacheBackend && $cache = $this->cacheBackend->get($this->cacheKey)) {
if (!isset($this->definitions) && $cache = $this->cacheGet($this->cacheKey)) {
$this->definitions = $cache->data;
}
return $this->definitions;
......@@ -193,12 +200,43 @@ protected function getCachedDefinitions() {
* List of definitions to store in cache.
*/
protected function setCachedDefinitions($definitions) {
if ($this->cacheBackend) {
$this->cacheBackend->set($this->cacheKey, $definitions, Cache::PERMANENT, $this->cacheTags);
}
$this->cacheSet($this->cacheKey, $definitions, Cache::PERMANENT, $this->cacheTags);
$this->definitions = $definitions;
}
/**
* {@inheritdoc}
*/
public function useCaches($use_caches = FALSE) {
$this->useCaches = $use_caches;
if (!$use_caches) {
$this->definitions = NULL;
}
}
/**
* Fetches from the cache backend, respecting the use caches flag.
*
* @see \Drupal\Core\Cache\CacheBackendInterface::get()
*/
protected function cacheGet($cid) {
if ($this->useCaches && $this->cacheBackend) {
return $this->cacheBackend->get($cid);
}
return FALSE;
}
/**
* Stores data in the persistent cache, respecting the use caches flag.
*
* @see \Drupal\Core\Cache\CacheBackendInterface::set()
*/
protected function cacheSet($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) {
if ($this->cacheBackend && $this->useCaches) {
$this->cacheBackend->set($cid, $data, $expire, $tags);
}
}
/**
* Performs extra processing on plugin definitions.
......
......@@ -44,7 +44,6 @@ protected function updateEntityTypeToRevisionable() {
$entity_type->set('entity_keys', $keys);
$this->state->set('entity_test_update.entity_type', $entity_type);
$this->entityManager->clearCachedDefinitions();
}
/**
......@@ -58,7 +57,6 @@ protected function updateEntityTypeToNotRevisionable() {
$entity_type->set('entity_keys', $keys);
$this->state->set('entity_test_update.entity_type', $entity_type);
$this->entityManager->clearCachedDefinitions();
}
/**
......@@ -75,7 +73,6 @@ protected function updateEntityTypeToTranslatable() {
}
$this->state->set('entity_test_update.entity_type', $entity_type);
$this->entityManager->clearCachedDefinitions();
}
/**
......@@ -92,7 +89,6 @@ protected function updateEntityTypeToNotTranslatable() {
}
$this->state->set('entity_test_update.entity_type', $entity_type);
$this->entityManager->clearCachedDefinitions();
}
/**
......@@ -106,7 +102,6 @@ protected function addBaseField($type = 'string') {
->setName('new_base_field')
->setLabel(t('A new base field'));
$this->state->set('entity_test_update.additional_base_field_definitions', $definitions);
$this->entityManager->clearCachedDefinitions();
}
/**
......@@ -121,7 +116,6 @@ protected function addRevisionableBaseField($type = 'string') {
->setLabel(t('A new revisionable base field'))
->setRevisionable(TRUE);
$this->state->set('entity_test_update.additional_base_field_definitions', $definitions);
$this->entityManager->clearCachedDefinitions();
}
/**
......@@ -136,7 +130,6 @@ protected function modifyBaseField() {
*/
protected function removeBaseField() {
$this->state->delete('entity_test_update.additional_base_field_definitions');
$this->entityManager->clearCachedDefinitions();
}
/**
......@@ -144,7 +137,6 @@ protected function removeBaseField() {
*/
protected function addBaseFieldIndex() {
$this->state->set('entity_test_update.additional_field_index.entity_test_update.new_base_field', TRUE);
$this->entityManager->clearCachedDefinitions();
}
/**
......@@ -152,7 +144,6 @@ protected function addBaseFieldIndex() {
*/
protected function removeBaseFieldIndex() {
$this->state->delete('entity_test_update.additional_field_index.entity_test_update.new_base_field');
$this->entityManager->clearCachedDefinitions();
}
/**
......@@ -168,7 +159,6 @@ protected function addBundleField($type = 'string') {
->setTargetEntityTypeId('entity_test_update');
$this->state->set('entity_test_update.additional_field_storage_definitions', $definitions);
$this->state->set('entity_test_update.additional_bundle_field_definitions.test_bundle', $definitions);
$this->entityManager->clearCachedDefinitions();
}
/**
......@@ -184,7 +174,6 @@ protected function modifyBundleField() {
protected function removeBundleField() {
$this->state->delete('entity_test_update.additional_field_storage_definitions');
$this->state->delete('entity_test_update.additional_bundle_field_definitions.test_bundle');
$this->entityManager->clearCachedDefinitions();
}
/**
......@@ -215,7 +204,6 @@ protected function renameBaseTable() {
$entity_type->set('base_table', 'entity_test_update_new');
$this->state->set('entity_test_update.entity_type', $entity_type);
$this->entityManager->clearCachedDefinitions();
}
/**
......@@ -227,7 +215,6 @@ protected function renameDataTable() {
$entity_type->set('data_table', 'entity_test_update_data_new');
$this->state->set('entity_test_update.entity_type', $entity_type);
$this->entityManager->clearCachedDefinitions();
}
/**
......@@ -239,7 +226,6 @@ protected function renameRevisionBaseTable() {
$entity_type->set('revision_table', 'entity_test_update_revision_new');
$this->state->set('entity_test_update.entity_type', $entity_type);
$this->entityManager->clearCachedDefinitions();
}
/**
......@@ -251,7 +237,6 @@ protected function renameRevisionDataTable() {
$entity_type->set('revision_data_table', 'entity_test_update_revision_data_new');
$this->state->set('entity_test_update.entity_type', $entity_type);
$this->entityManager->clearCachedDefinitions();
}
/**
......@@ -259,7 +244,6 @@ protected function renameRevisionDataTable() {
*/
protected function deleteEntityType() {
$this->state->set('entity_test_update.entity_type', 'null');
$this->entityManager->clearCachedDefinitions();
}
}
......@@ -208,6 +208,9 @@ public function testRevisionBaseTableRename() {
*/
public function testRevisionDataTableRename() {
$this->updateEntityTypeToRevisionable();
// Multiple changes, so we have to invalidate the caches, otherwise
// the second update will revert the first.
$this->entityManager->clearCachedDefinitions();
$this->updateEntityTypeToTranslatable();
$this->entityDefinitionUpdateManager->applyUpdates();
......@@ -414,6 +417,10 @@ public function testVariousTableUpdates() {
public function testVariousTableUpdatesForRevisionView() {
// base + revision <-> base + translation + revision
$this->updateEntityTypeToRevisionable();
// Multiple changes, so we have to invalidate the caches, otherwise
// the second update will revert the first.
$this->entityManager->clearCachedDefinitions();
list($view, $display) = $this->getUpdatedViewAndDisplay(TRUE);
$this->assertEqual('entity_test_update_revision', $view->get('base_table'));
......
......@@ -185,6 +185,30 @@ public function testDefaultPluginManagerWithFilledCache() {
$this->assertEquals($this->expectedDefinitions, $plugin_manager->getDefinitions());
}
/**
* Tests the plugin manager with caching disabled.
*/
public function testDefaultPluginManagerNoCache() {
$plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, NULL, NULL, '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
$cid = $this->randomMachineName();
$cache_backend = $this->getMockBuilder('Drupal\Core\Cache\MemoryBackend')
->disableOriginalConstructor()
->getMock();
$cache_backend
->expects($this->never())
->method('get');
$cache_backend
->expects($this->never())
->method('set');
$plugin_manager->setCacheBackend($cache_backend, $cid);
$plugin_manager->useCaches(FALSE);
$this->assertEquals($this->expectedDefinitions, $plugin_manager->getDefinitions());
$this->assertEquals($this->expectedDefinitions['banana'], $plugin_manager->getDefinition('banana'));
}
/**
* Tests the plugin manager cache clear with tags.
*/
......
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