Skip to content
Snippets Groups Projects
Commit 22a2503d authored by catch's avatar catch
Browse files

Issue #3018287 by ndobromirov, Wim Leers, gabesullice, e0ipso:...

Issue #3018287 by ndobromirov, Wim Leers, gabesullice, e0ipso: ResourceTypeRepository computes ResourceType value objects on *every request*
parent fcf03f4b
No related branches found
No related tags found
No related merge requests found
......@@ -120,10 +120,20 @@ services:
arguments: ['jsonapi']
# Cache.
cache.jsonapi_resource_types:
cache.jsonapi_memory:
class: Drupal\Core\Cache\MemoryCache\MemoryCache
# We need this to add this to the Drupal's cache_tags.invalidator service.
# This way it can invalidate the data in here based on tags.
public: false
# A chained cache with an in-memory cache as the first layer and a database-
# backed cache as the fallback is used. The first layer (memory) is necessary
# because ResourceType value objects are retrieved many times during a
# request. The second layer (by default a database) is necessary to avoid
# recomputing the ResourceType value objects on every request.
cache.jsonapi_resource_types:
class: Drupal\Core\Cache\BackendChain
calls:
- [appendBackend, ['@cache.jsonapi_memory']]
- [appendBackend, ['@cache.default']]
tags: [{ name: cache.bin }]
cache.jsonapi_normalizations:
class: Drupal\Core\Cache\CacheBackendInterface
......
......@@ -61,18 +61,26 @@ class ResourceTypeRepository implements ResourceTypeRepositoryInterface {
protected $entityFieldManager;
/**
* The static cache backend.
* The cache backend.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $staticCache;
protected $cache;
/**
* Instance data cache.
* Cache tags used for caching the repository.
*
* @var array
* @var string[]
*/
protected $cache = [];
protected $cacheTags = [
'jsonapi_resource_types',
// Invalidate whenever field definitions are modified.
'entity_field_info',
// Invalidate whenever the set of bundles changes.
'entity_bundles',
// Invalidate whenever the set of entity types changes.
'entity_types',
];
/**
* Instantiates a ResourceTypeRepository object.
......@@ -83,33 +91,37 @@ class ResourceTypeRepository implements ResourceTypeRepositoryInterface {
* The entity type bundle info service.
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
* The entity field manager.
* @param \Drupal\Core\Cache\CacheBackendInterface $static_cache
* The static cache backend.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
* The cache backend.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_bundle_info, EntityFieldManagerInterface $entity_field_manager, CacheBackendInterface $static_cache) {
public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_bundle_info, EntityFieldManagerInterface $entity_field_manager, CacheBackendInterface $cache) {
$this->entityTypeManager = $entity_type_manager;
$this->entityTypeBundleInfo = $entity_bundle_info;
$this->entityFieldManager = $entity_field_manager;
$this->staticCache = $static_cache;
$this->cache = $cache;
}
/**
* {@inheritdoc}
*/
public function all() {
$cached = $this->staticCache->get('jsonapi.resource_types', FALSE);
$cached = $this->cache->get('jsonapi.resource_types', FALSE);
if ($cached === FALSE) {
$resource_types = [];
foreach ($this->entityTypeManager->getDefinitions() as $entity_type) {
$resource_types = array_merge($resource_types, array_map(function ($bundle) use ($entity_type) {
return $this->createResourceType($entity_type, $bundle);
}, array_keys($this->entityTypeBundleInfo->getBundleInfo($entity_type->id()))));
$bundles = array_keys($this->entityTypeBundleInfo->getBundleInfo($entity_type->id()));
$resource_types = array_reduce($bundles, function ($resource_types, $bundle) use ($entity_type) {
$resource_type = $this->createResourceType($entity_type, $bundle);
return array_merge($resource_types, [
$resource_type->getTypeName() => $resource_type,
]);
}, $resource_types);
}
foreach ($resource_types as $resource_type) {
$relatable_resource_types = $this->calculateRelatableResourceTypes($resource_type, $resource_types);
$resource_type->setRelatableResourceTypes($relatable_resource_types);
}
$this->staticCache->set('jsonapi.resource_types', $resource_types, Cache::PERMANENT, ['jsonapi_resource_types']);
$this->cache->set('jsonapi.resource_types', $resource_types, Cache::PERMANENT, $this->cacheTags);
}
return $cached ? $cached->data : $resource_types;
}
......@@ -148,31 +160,15 @@ public function get($entity_type_id, $bundle) {
throw new PreconditionFailedHttpException('Server error. The current route is malformed.');
}
$cid = "jsonapi:resource_type:$entity_type_id:$bundle";
if (!array_key_exists($cid, $this->cache)) {
$result = NULL;
foreach ($this->all() as $resource) {
if ($resource->getEntityTypeId() == $entity_type_id && $resource->getBundle() == $bundle) {
$result = $resource;
break;
}
}
$this->cache[$cid] = $result;
}
return $this->cache[$cid];
return $this->getByTypeName("$entity_type_id--$bundle");
}
/**
* {@inheritdoc}
*/
public function getByTypeName($type_name) {
foreach ($this->all() as $resource) {
if ($resource->getTypeName() == $type_name) {
return $resource;
}
}
return NULL;
$resource_types = $this->all();
return isset($resource_types[$type_name]) ? $resource_types[$type_name] : NULL;
}
/**
......@@ -395,11 +391,8 @@ protected function getRelatableResourceTypesFromFieldDefinition(FieldDefinitionI
: $this->getAllBundlesForEntityType($entity_type_id);
return array_map(function ($target_bundle) use ($entity_type_id, $resource_types) {
foreach ($resource_types as $resource_type) {
if ($resource_type->getEntityTypeId() === $entity_type_id && $resource_type->getBundle() === $target_bundle) {
return $resource_type;
}
}
$type_name = "$entity_type_id--$target_bundle";
return isset($resource_types[$type_name]) ? $resource_types[$type_name] : NULL;
}, $target_bundles);
}
......@@ -414,11 +407,18 @@ protected function getRelatableResourceTypesFromFieldDefinition(FieldDefinitionI
* otherwise.
*/
protected function isReferenceFieldDefinition(FieldDefinitionInterface $field_definition) {
static $field_type_is_reference = [];
if (isset($field_type_is_reference[$field_definition->getType()])) {
return $field_type_is_reference[$field_definition->getType()];
}
/* @var \Drupal\Core\Field\TypedData\FieldItemDataDefinition $item_definition */
$item_definition = $field_definition->getItemDefinition();
$main_property = $item_definition->getMainPropertyName();
$property_definition = $item_definition->getPropertyDefinition($main_property);
return $property_definition instanceof DataReferenceTargetDefinition;
return $field_type_is_reference[$field_definition->getType()] = $property_definition instanceof DataReferenceTargetDefinition;
}
/**
......
......@@ -3,8 +3,8 @@
namespace Drupal\Tests\jsonapi\Kernel\ResourceType;
use Drupal\jsonapi\ResourceType\ResourceType;
use Drupal\KernelTests\KernelTestBase;
use Drupal\node\Entity\NodeType;
use Drupal\Tests\jsonapi\Kernel\JsonapiKernelTestBase;
/**
* @coversDefaultClass \Drupal\jsonapi\ResourceType\ResourceTypeRepository
......@@ -12,14 +12,14 @@
*
* @internal
*/
class ResourceTypeRepositoryTest extends KernelTestBase {
class ResourceTypeRepositoryTest extends JsonapiKernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'field',
'node',
'jsonapi',
'serialization',
'system',
'user',
......@@ -96,4 +96,15 @@ public function getProvider() {
];
}
/**
* Ensures that the ResourceTypeRepository's cache does not become stale.
*/
public function testCaching() {
$this->assertEmpty($this->resourceTypeRepository->get('node', 'article')->getRelatableResourceTypesByField('field_relationship'));
$this->createEntityReferenceField('node', 'article', 'field_relationship', 'Related entity', 'node');
$this->assertCount(2, $this->resourceTypeRepository->get('node', 'article')->getRelatableResourceTypesByField('field_relationship'));
NodeType::create(['type' => 'camelids'])->save();
$this->assertCount(3, $this->resourceTypeRepository->get('node', 'article')->getRelatableResourceTypesByField('field_relationship'));
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment