class ResourceObjectNormalizationCacher

Same name in other branches
  1. 9 core/modules/jsonapi/src/EventSubscriber/ResourceObjectNormalizationCacher.php \Drupal\jsonapi\EventSubscriber\ResourceObjectNormalizationCacher
  2. 10 core/modules/jsonapi/src/EventSubscriber/ResourceObjectNormalizationCacher.php \Drupal\jsonapi\EventSubscriber\ResourceObjectNormalizationCacher
  3. 11.x core/modules/jsonapi/src/EventSubscriber/ResourceObjectNormalizationCacher.php \Drupal\jsonapi\EventSubscriber\ResourceObjectNormalizationCacher

Caches entity normalizations after the response has been sent.

@internal

@todo Refactor once https://www.drupal.org/node/2551419 lands.

Hierarchy

Expanded class hierarchy of ResourceObjectNormalizationCacher

See also

\Drupal\jsonapi\Normalizer\ResourceObjectNormalizer::getNormalization()

2 files declare their use of ResourceObjectNormalizationCacher
ResourceObjectNormalizer.php in core/modules/jsonapi/src/Normalizer/ResourceObjectNormalizer.php
ResourceObjectNormalizerCacherTest.php in core/modules/jsonapi/tests/src/Kernel/EventSubscriber/ResourceObjectNormalizerCacherTest.php
1 string reference to 'ResourceObjectNormalizationCacher'
jsonapi.services.yml in core/modules/jsonapi/jsonapi.services.yml
core/modules/jsonapi/jsonapi.services.yml
1 service uses ResourceObjectNormalizationCacher
jsonapi.normalization_cacher in core/modules/jsonapi/jsonapi.services.yml
Drupal\jsonapi\EventSubscriber\ResourceObjectNormalizationCacher

File

core/modules/jsonapi/src/EventSubscriber/ResourceObjectNormalizationCacher.php, line 19

Namespace

Drupal\jsonapi\EventSubscriber
View source
class ResourceObjectNormalizationCacher implements EventSubscriberInterface {
    
    /**
     * Key for the base subset.
     *
     * The base subset contains the parts of the normalization that are always
     * present. The presence or absence of these are not affected by the requested
     * sparse field sets. This typically includes the resource type name, and the
     * resource ID.
     */
    const RESOURCE_CACHE_SUBSET_BASE = 'base';
    
    /**
     * Key for the fields subset.
     *
     * The fields subset contains the parts of the normalization that can appear
     * in a normalization based on the selected field set. This subset is
     * incrementally built across different requests for the same resource object.
     * A given field is normalized and put into the cache whenever there is a
     * cache miss for that field.
     */
    const RESOURCE_CACHE_SUBSET_FIELDS = 'fields';
    
    /**
     * The render cache.
     *
     * @var \Drupal\Core\Render\RenderCacheInterface
     */
    protected $renderCache;
    
    /**
     * The things to cache after the response has been sent.
     *
     * @var array
     */
    protected $toCache = [];
    
    /**
     * Sets the render cache service.
     *
     * @param \Drupal\Core\Render\RenderCacheInterface $render_cache
     *   The render cache.
     */
    public function setRenderCache(RenderCacheInterface $render_cache) {
        $this->renderCache = $render_cache;
    }
    
    /**
     * Reads an entity normalization from cache.
     *
     * The returned normalization may only be a partial normalization because it
     * was previously normalized with a sparse fieldset.
     *
     * @param \Drupal\jsonapi\JsonApiResource\ResourceObject $object
     *   The resource object for which to generate a cache item.
     *
     * @return array|false
     *   The cached normalization parts, or FALSE if not yet cached.
     *
     * @see \Drupal\dynamic_page_cache\EventSubscriber\DynamicPageCacheSubscriber::renderArrayToResponse()
     */
    public function get(ResourceObject $object) {
        $cached = $this->renderCache
            ->get(static::generateLookupRenderArray($object));
        return $cached ? $cached['#data'] : FALSE;
    }
    
    /**
     * Adds a normalization to be cached after the response has been sent.
     *
     * @param \Drupal\jsonapi\JsonApiResource\ResourceObject $object
     *   The resource object for which to generate a cache item.
     * @param array $normalization_parts
     *   The normalization parts to cache.
     */
    public function saveOnTerminate(ResourceObject $object, array $normalization_parts) {
        assert(array_keys($normalization_parts) === [
            static::RESOURCE_CACHE_SUBSET_BASE,
            static::RESOURCE_CACHE_SUBSET_FIELDS,
        ]);
        $resource_type = $object->getResourceType();
        $key = $resource_type->getTypeName() . ':' . $object->getId();
        $this->toCache[$key] = [
            $object,
            $normalization_parts,
        ];
    }
    
    /**
     * Writes normalizations of entities to cache, if any were created.
     *
     * @param \Symfony\Component\HttpKernel\Event\PostResponseEvent $event
     *   The Event to process.
     */
    public function onTerminate(PostResponseEvent $event) {
        foreach ($this->toCache as $value) {
            list($object, $normalization_parts) = $value;
            $this->set($object, $normalization_parts);
        }
    }
    
    /**
     * Writes a normalization to cache.
     *
     * @param \Drupal\jsonapi\JsonApiResource\ResourceObject $object
     *   The resource object for which to generate a cache item.
     * @param array $normalization_parts
     *   The normalization parts to cache.
     *
     * @see \Drupal\dynamic_page_cache\EventSubscriber\DynamicPageCacheSubscriber::responseToRenderArray()
     * @todo Refactor/remove once https://www.drupal.org/node/2551419 lands.
     */
    protected function set(ResourceObject $object, array $normalization_parts) {
        $base = static::generateLookupRenderArray($object);
        $data_as_render_array = $base + [
            // The data we actually care about.
'#data' => $normalization_parts,
            // Tell RenderCache to cache the #data property: the data we actually care
            // about.
'#cache_properties' => [
                '#data',
            ],
            // These exist only to fulfill the requirements of the RenderCache, which
            // is designed to work with render arrays only. We don't care about these.
'#markup' => '',
            '#attached' => '',
        ];
        // Merge the entity's cacheability metadata with that of the normalization
        // parts, so that RenderCache can take care of cache redirects for us.
        CacheableMetadata::createFromObject($object)->merge(static::mergeCacheableDependencies($normalization_parts[static::RESOURCE_CACHE_SUBSET_BASE]))
            ->merge(static::mergeCacheableDependencies($normalization_parts[static::RESOURCE_CACHE_SUBSET_FIELDS]))
            ->applyTo($data_as_render_array);
        $this->renderCache
            ->set($data_as_render_array, $base);
    }
    
    /**
     * Generates a lookup render array for a normalization.
     *
     * @param \Drupal\jsonapi\JsonApiResource\ResourceObject $object
     *   The resource object for which to generate a cache item.
     *
     * @return array
     *   A render array for use with the RenderCache service.
     *
     * @see \Drupal\dynamic_page_cache\EventSubscriber\DynamicPageCacheSubscriber::$dynamicPageCacheRedirectRenderArray
     */
    protected static function generateLookupRenderArray(ResourceObject $object) {
        return [
            '#cache' => [
                'keys' => [
                    $object->getResourceType()
                        ->getTypeName(),
                    $object->getId(),
                ],
                'bin' => 'jsonapi_normalizations',
            ],
        ];
    }
    
    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents() {
        $events[KernelEvents::TERMINATE][] = [
            'onTerminate',
        ];
        return $events;
    }
    
    /**
     * Determines the joint cacheability of all provided dependencies.
     *
     * @param \Drupal\Core\Cache\CacheableDependencyInterface|object[] $dependencies
     *   The dependencies.
     *
     * @return \Drupal\Core\Cache\CacheableMetadata
     *   The cacheability of all dependencies.
     *
     * @see \Drupal\Core\Cache\RefinableCacheableDependencyInterface::addCacheableDependency()
     */
    protected static function mergeCacheableDependencies(array $dependencies) {
        $merged_cacheability = new CacheableMetadata();
        array_walk($dependencies, function ($dependency) use ($merged_cacheability) {
            $merged_cacheability->addCacheableDependency($dependency);
        });
        return $merged_cacheability;
    }

}

Members

Title Sort descending Modifiers Object type Summary
ResourceObjectNormalizationCacher::$renderCache protected property The render cache.
ResourceObjectNormalizationCacher::$toCache protected property The things to cache after the response has been sent.
ResourceObjectNormalizationCacher::generateLookupRenderArray protected static function Generates a lookup render array for a normalization.
ResourceObjectNormalizationCacher::get public function Reads an entity normalization from cache.
ResourceObjectNormalizationCacher::getSubscribedEvents public static function
ResourceObjectNormalizationCacher::mergeCacheableDependencies protected static function Determines the joint cacheability of all provided dependencies.
ResourceObjectNormalizationCacher::onTerminate public function Writes normalizations of entities to cache, if any were created.
ResourceObjectNormalizationCacher::RESOURCE_CACHE_SUBSET_BASE constant Key for the base subset.
ResourceObjectNormalizationCacher::RESOURCE_CACHE_SUBSET_FIELDS constant Key for the fields subset.
ResourceObjectNormalizationCacher::saveOnTerminate public function Adds a normalization to be cached after the response has been sent.
ResourceObjectNormalizationCacher::set protected function Writes a normalization to cache.
ResourceObjectNormalizationCacher::setRenderCache public function Sets the render cache service.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.