summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLee Rowlands2017-11-08 12:01:36 +1000
committerLee Rowlands2017-11-08 12:01:36 +1000
commit52a598e2bf739b39709bd1fd77237e99e8572017 (patch)
treeacc26b24f7d6812e144cc0438c7cf8688ea322bd
parent33818be2d810be751b11c81bdafcd300f15b055a (diff)
Issue #2910211 by tedbow, Wim Leers: Allow computed exposed properties in ComplexData to support cacheability
-rw-r--r--core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonInternalPropertyNormalizerTest.php15
-rw-r--r--core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php49
-rw-r--r--core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestJsonInternalPropertyNormalizerTest.php15
-rw-r--r--core/modules/serialization/src/Normalizer/NormalizerBase.php16
-rw-r--r--core/modules/serialization/src/Normalizer/TypedDataNormalizer.php1
-rw-r--r--core/modules/system/tests/modules/entity_test/src/TypedData/ComputedString.php25
6 files changed, 110 insertions, 11 deletions
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonInternalPropertyNormalizerTest.php b/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonInternalPropertyNormalizerTest.php
index 8ee3215..819a125 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonInternalPropertyNormalizerTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/EntityTest/EntityTestHalJsonInternalPropertyNormalizerTest.php
@@ -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 @@ class EntityTestHalJsonInternalPropertyNormalizerTest extends EntityTestHalJsonA
];
}
+ /**
+ * {@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']);
+ }
+
}
diff --git a/core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php b/core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php
index ef33817..babb238 100644
--- a/core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php
+++ b/core/modules/rest/src/EventSubscriber/ResourceResponseSubscriber.php
@@ -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;
@@ -21,6 +22,15 @@ use Symfony\Component\Serializer\SerializerInterface;
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.
*
* @var \Symfony\Component\Serializer\SerializerInterface
@@ -128,11 +138,19 @@ class ResourceResponseSubscriber implements EventSubscriberInterface {
/**
* 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 @@ class ResourceResponseSubscriber implements EventSubscriberInterface {
// 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);
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestJsonInternalPropertyNormalizerTest.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestJsonInternalPropertyNormalizerTest.php
index c369e17..1944718 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestJsonInternalPropertyNormalizerTest.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestJsonInternalPropertyNormalizerTest.php
@@ -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 @@ class EntityTestJsonInternalPropertyNormalizerTest extends EntityTestResourceTes
];
}
+ /**
+ * {@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']);
+ }
+
}
diff --git a/core/modules/serialization/src/Normalizer/NormalizerBase.php b/core/modules/serialization/src/Normalizer/NormalizerBase.php
index 5e829f6..958aaf2 100644
--- a/core/modules/serialization/src/Normalizer/NormalizerBase.php
+++ b/core/modules/serialization/src/Normalizer/NormalizerBase.php
@@ -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 @@ abstract class NormalizerBase extends SerializerAwareNormalizer implements Norma
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);
+ }
+ }
+
}
diff --git a/core/modules/serialization/src/Normalizer/TypedDataNormalizer.php b/core/modules/serialization/src/Normalizer/TypedDataNormalizer.php
index 60ce7d0..958b987 100644
--- a/core/modules/serialization/src/Normalizer/TypedDataNormalizer.php
+++ b/core/modules/serialization/src/Normalizer/TypedDataNormalizer.php
@@ -18,6 +18,7 @@ class TypedDataNormalizer extends NormalizerBase {
* {@inheritdoc}
*/
public function normalize($object, $format = NULL, array $context = []) {
+ $this->addCacheableDependency($context, $object);
return $object->getValue();
}
diff --git a/core/modules/system/tests/modules/entity_test/src/TypedData/ComputedString.php b/core/modules/system/tests/modules/entity_test/src/TypedData/ComputedString.php
index 121699c..a817526 100644
--- a/core/modules/system/tests/modules/entity_test/src/TypedData/ComputedString.php
+++ b/core/modules/system/tests/modules/entity_test/src/TypedData/ComputedString.php
@@ -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 @@ class ComputedString extends TypedData {
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;
+ }
+
}