Skip to content
Snippets Groups Projects
Verified Commit 52a598e2 authored by Lee Rowlands's avatar Lee Rowlands
Browse files

Issue #2910211 by tedbow, Wim Leers: Allow computed exposed properties in...

Issue #2910211 by tedbow, Wim Leers: Allow computed exposed properties in ComplexData to support cacheability
parent 33818be2
No related branches found
No related tags found
2 merge requests!7452Issue #1797438. HTML5 validation is preventing form submit and not fully...,!789Issue #3210310: Adjust Database API to remove deprecated Drupal 9 code in Drupal 10
......@@ -2,6 +2,7 @@
namespace Drupal\Tests\hal\Functional\EntityResource\EntityTest;
use Drupal\Core\Cache\Cache;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\Tests\hal\Functional\EntityResource\HalEntityNormalizationTrait;
......@@ -82,4 +83,18 @@ protected function getNormalizedPostEntity() {
];
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheContexts() {
return Cache::mergeContexts(parent::getExpectedCacheContexts(), ['request_format']);
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheTags() {
return Cache::mergeTags(parent::getExpectedCacheTags(), ['you_are_it', 'no_tag_backs']);
}
}
......@@ -2,6 +2,7 @@
namespace Drupal\rest\EventSubscriber;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\CacheableResponse;
use Drupal\Core\Cache\CacheableResponseInterface;
use Drupal\Core\Render\RenderContext;
......@@ -20,6 +21,15 @@
*/
class ResourceResponseSubscriber implements EventSubscriberInterface {
/**
* Name of key for bubbling cacheability metadata via serialization context.
*
* @see \Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize()
* @see \Symfony\Component\Serializer\SerializerInterface::serialize()
* @see \Drupal\rest\EventSubscriber\ResourceResponseSubscriber::renderResponseBody()
*/
const SERIALIZATION_CONTEXT_CACHEABILITY = 'cacheability';
/**
* The serializer.
*
......@@ -128,11 +138,19 @@ public function getResponseFormat(RouteMatchInterface $route_match, Request $req
/**
* Renders a resource response body.
*
* Serialization can invoke rendering (e.g., generating URLs), but the
* serialization API does not provide a mechanism to collect the
* bubbleable metadata associated with that (e.g., language and other
* contexts), so instead, allow those to "leak" and collect them here in
* a render context.
* During serialization, encoders and normalizers are able to explicitly
* bubble cacheability metadata via the 'cacheability' key-value pair in the
* received context. This bubbled cacheability metadata will be applied to the
* the response.
*
* In versions of Drupal prior to 8.5, implicit bubbling of cacheability
* metadata was allowed because there was no explicit cacheability metadata
* bubbling API. To maintain backwards compatibility, we continue to support
* this, but support for this will be dropped in Drupal 9.0.0. This is
* especially useful when interacting with APIs that implicitly invoke
* rendering (for example: generating URLs): this allows those to "leak", and
* we collect their bubbled cacheability metadata automatically in a render
* context.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
......@@ -152,14 +170,25 @@ protected function renderResponseBody(Request $request, ResourceResponseInterfac
// If there is data to send, serialize and set it as the response body.
if ($data !== NULL) {
$serialization_context = [
'request' => $request,
static::SERIALIZATION_CONTEXT_CACHEABILITY => new CacheableMetadata(),
];
// @deprecated In Drupal 8.5.0, will be removed before Drupal 9.0.0. Use
// explicit cacheability metadata bubbling instead. (The wrapping call to
// executeInRenderContext() will be removed before Drupal 9.0.0.)
$context = new RenderContext();
$output = $this->renderer
->executeInRenderContext($context, function () use ($serializer, $data, $format) {
return $serializer->serialize($data, $format);
->executeInRenderContext($context, function () use ($serializer, $data, $format, $serialization_context) {
return $serializer->serialize($data, $format, $serialization_context);
});
if ($response instanceof CacheableResponseInterface && !$context->isEmpty()) {
$response->addCacheableDependency($context->pop());
if ($response instanceof CacheableResponseInterface) {
if (!$context->isEmpty()) {
@trigger_error('Implicit cacheability metadata bubbling (onto the global render context) in normalizers is deprecated since Drupal 8.5.0 and will be removed in Drupal 9.0.0. Use the "cacheability" serialization context instead, for explicit cacheability metadata bubbling. See https://www.drupal.org/node/2918937', E_USER_DEPRECATED);
$response->addCacheableDependency($context->pop());
}
$response->addCacheableDependency($serialization_context[static::SERIALIZATION_CONTEXT_CACHEABILITY]);
}
$response->setContent($output);
......
......@@ -2,6 +2,7 @@
namespace Drupal\Tests\rest\Functional\EntityResource\EntityTest;
use Drupal\Core\Cache\Cache;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
......@@ -84,4 +85,18 @@ protected function getNormalizedPostEntity() {
];
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheContexts() {
return Cache::mergeContexts(parent::getExpectedCacheContexts(), ['request_format']);
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheTags() {
return Cache::mergeTags(parent::getExpectedCacheTags(), ['you_are_it', 'no_tag_backs']);
}
}
......@@ -2,6 +2,8 @@
namespace Drupal\serialization\Normalizer;
use Drupal\Core\Cache\CacheableDependencyInterface;
use Drupal\rest\EventSubscriber\ResourceResponseSubscriber;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Normalizer\SerializerAwareNormalizer;
......@@ -81,4 +83,18 @@ protected function checkFormat($format = NULL) {
return in_array($format, (array) $this->format, TRUE);
}
/**
* Adds cacheability if applicable.
*
* @param array $context
* Context options for the normalizer.
* @param $data
* The data that might have cacheability information.
*/
protected function addCacheableDependency(array $context, $data) {
if ($data instanceof CacheableDependencyInterface && isset($context[ResourceResponseSubscriber::SERIALIZATION_CONTEXT_CACHEABILITY])) {
$context[ResourceResponseSubscriber::SERIALIZATION_CONTEXT_CACHEABILITY]->addCacheableDependency($data);
}
}
}
......@@ -18,6 +18,7 @@ class TypedDataNormalizer extends NormalizerBase {
* {@inheritdoc}
*/
public function normalize($object, $format = NULL, array $context = []) {
$this->addCacheableDependency($context, $object);
return $object->getValue();
}
......
......@@ -2,12 +2,14 @@
namespace Drupal\entity_test\TypedData;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheableDependencyInterface;
use Drupal\Core\TypedData\TypedData;
/**
* A computed property for test strings.
*/
class ComputedString extends TypedData {
class ComputedString extends TypedData implements CacheableDependencyInterface {
/**
* {@inheritdoc}
......@@ -27,4 +29,25 @@ public function getCastedValue() {
return $this->getString();
}
/**
* {@inheritdoc}
*/
public function getCacheTags() {
return ['you_are_it', 'no_tag_backs'];
}
/**
* {@inheritdoc}
*/
public function getCacheContexts() {
return ['request_format'];
}
/**
* {@inheritdoc}
*/
public function getCacheMaxAge() {
return Cache::PERMANENT;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment