Commit 149c25eb authored by alexpott's avatar alexpott

Issue #2474121 by Wim Leers: CacheableMetadata should get BubbleableMetadata's...

Issue #2474121 by Wim Leers: CacheableMetadata should get BubbleableMetadata's merge/applyTo/createFromRenderArray/createFromObject methods
parent e594f222
......@@ -129,4 +129,73 @@ public function setCacheMaxAge($max_age) {
return $this;
}
/**
* Merges the values of another CacheableMetadata object with this one.
*
* @param \Drupal\Core\Cache\CacheableMetadata $other
* The other CacheableMetadata object.
*
* @return static
* A new CacheableMetadata object, with the merged data.
*/
public function merge(CacheableMetadata $other) {
$result = new static();
$result->contexts = Cache::mergeContexts($this->contexts, $other->contexts);
$result->tags = Cache::mergeTags($this->tags, $other->tags);
$result->maxAge = Cache::mergeMaxAges($this->maxAge, $other->maxAge);
return $result;
}
/**
* Applies the values of this CacheableMetadata object to a render array.
*
* @param array &$build
* A render array.
*/
public function applyTo(array &$build) {
$build['#cache']['contexts'] = $this->contexts;
$build['#cache']['tags'] = $this->tags;
$build['#cache']['max-age'] = $this->maxAge;
}
/**
* Creates a CacheableMetadata object with values taken from a render array.
*
* @param array $build
* A render array.
*
* @return static
*/
public static function createFromRenderArray(array $build) {
$meta = new static();
$meta->contexts = (isset($build['#cache']['contexts'])) ? $build['#cache']['contexts'] : [];
$meta->tags = (isset($build['#cache']['tags'])) ? $build['#cache']['tags'] : [];
$meta->maxAge = (isset($build['#cache']['max-age'])) ? $build['#cache']['max-age'] : Cache::PERMANENT;
return $meta;
}
/**
* Creates a CacheableMetadata object from a depended object.
*
* @param \Drupal\Core\Cache\CacheableDependencyInterface|mixed $object
* The object whose cacheability metadata to retrieve.
*
* @return static
*/
public static function createFromObject($object) {
if ($object instanceof CacheableDependencyInterface) {
$meta = new static();
$meta->contexts = $object->getCacheContexts();
$meta->tags = $object->getCacheTags();
$meta->maxAge = $object->getCacheMaxAge();
return $meta;
}
// Objects that don't implement CacheableDependencyInterface must be assumed
// to be uncacheable, so set max-age 0.
$meta = new static();
$meta->maxAge = 0;
return $meta;
}
}
......@@ -7,12 +7,12 @@
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Field\EntityReferenceFieldItemListInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\TypedData\TranslatableInterface;
/**
......@@ -58,7 +58,7 @@ protected function getEntitiesToView(EntityReferenceFieldItemListInterface $item
$access = $this->checkAccess($entity);
// Add the access result's cacheability, ::view() needs it.
$item->_accessCacheability = BubbleableMetadata::createFromObject($access);
$item->_accessCacheability = CacheableMetadata::createFromObject($access);
if ($access->isAllowed()) {
// Add the referring item, in case the formatter needs it.
$entity->_referringItem = $items[$delta];
......@@ -79,7 +79,7 @@ protected function getEntitiesToView(EntityReferenceFieldItemListInterface $item
public function view(FieldItemListInterface $items) {
$elements = parent::view($items);
$field_level_access_cacheability = new BubbleableMetadata();
$field_level_access_cacheability = new CacheableMetadata();
// Try to map the cacheability of the access result that was set at
// _accessCacheability in getEntitiesToView() to the corresponding render
......@@ -90,7 +90,7 @@ public function view(FieldItemListInterface $items) {
// prepareView().
if (!empty($item->_accessCacheability)) {
if (isset($elements[$delta])) {
BubbleableMetadata::createFromRenderArray($elements[$delta])
CacheableMetadata::createFromRenderArray($elements[$delta])
->merge($item->_accessCacheability)
->applyTo($elements[$delta]);
}
......
......@@ -8,8 +8,6 @@
namespace Drupal\Core\Render;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheableDependencyInterface;
use Drupal\Core\Cache\CacheableMetadata;
/**
......@@ -19,27 +17,6 @@
*/
class BubbleableMetadata extends CacheableMetadata {
/**
* Cache contexts.
*
* @var string[]
*/
protected $contexts = [];
/**
* Cache tags.
*
* @var string[]
*/
protected $tags = [];
/**
* Cache max-age.
*
* @var int
*/
protected $maxAge = Cache::PERMANENT;
/**
* Attached assets.
*
......@@ -57,8 +34,9 @@ class BubbleableMetadata extends CacheableMetadata {
/**
* Merges the values of another bubbleable metadata object with this one.
*
* @param \Drupal\Core\Render\BubbleableMetadata $other
* @param \Drupal\Core\Cache\CacheableMetadata $other
* The other bubbleable metadata object.
*
* @return static
* A new bubbleable metadata object, with the merged data.
*
......@@ -67,11 +45,8 @@ class BubbleableMetadata extends CacheableMetadata {
* drupal_merge_attached() no longer is a procedural function and remove
* the '@codeCoverageIgnore' annotation.
*/
public function merge(BubbleableMetadata $other) {
$result = new BubbleableMetadata();
$result->contexts = Cache::mergeContexts($this->contexts, $other->contexts);
$result->tags = Cache::mergeTags($this->tags, $other->tags);
$result->maxAge = Cache::mergeMaxAges($this->maxAge, $other->maxAge);
public function merge(CacheableMetadata $other) {
$result = parent::merge($other);
$result->attached = \Drupal::service('renderer')->mergeAttachments($this->attached, $other->attached);
$result->postRenderCache = NestedArray::mergeDeep($this->postRenderCache, $other->postRenderCache);
return $result;
......@@ -84,9 +59,7 @@ public function merge(BubbleableMetadata $other) {
* A render array.
*/
public function applyTo(array &$build) {
$build['#cache']['contexts'] = $this->contexts;
$build['#cache']['tags'] = $this->tags;
$build['#cache']['max-age'] = $this->maxAge;
parent::applyTo($build);
$build['#attached'] = $this->attached;
$build['#post_render_cache'] = $this->postRenderCache;
}
......@@ -100,39 +73,12 @@ public function applyTo(array &$build) {
* @return static
*/
public static function createFromRenderArray(array $build) {
$meta = new static();
$meta->contexts = (isset($build['#cache']['contexts'])) ? $build['#cache']['contexts'] : [];
$meta->tags = (isset($build['#cache']['tags'])) ? $build['#cache']['tags'] : [];
$meta->maxAge = (isset($build['#cache']['max-age'])) ? $build['#cache']['max-age'] : Cache::PERMANENT;
$meta = parent::createFromRenderArray($build);
$meta->attached = (isset($build['#attached'])) ? $build['#attached'] : [];
$meta->postRenderCache = (isset($build['#post_render_cache'])) ? $build['#post_render_cache'] : [];
return $meta;
}
/**
* Creates a bubbleable metadata object from a depended object.
*
* @param \Drupal\Core\Cache\CacheableDependencyInterface|mixed $object
* The object whose cacheability metadata to retrieve.
*
* @return static
*/
public static function createFromObject($object) {
if ($object instanceof CacheableDependencyInterface) {
$meta = new static();
$meta->contexts = $object->getCacheContexts();
$meta->tags = $object->getCacheTags();
$meta->maxAge = $object->getCacheMaxAge();
return $meta;
}
// Objects that don't implement CacheableDependencyInterface must be assumed
// to be uncacheable, so set max-age 0.
$meta = new static();
$meta->maxAge = 0;
return $meta;
}
/**
* Gets assets.
*
......
......@@ -11,6 +11,7 @@
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheableDependencyInterface;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\CacheContextsManager;
use Drupal\Core\Cache\CacheFactoryInterface;
use Drupal\Core\Controller\ControllerResolverInterface;
......@@ -800,8 +801,8 @@ public function mergeBubbleableMetadata(array $a, array $b) {
* {@inheritdoc}
*/
public function addDependency(array &$elements, $dependency) {
$meta_a = BubbleableMetadata::createFromRenderArray($elements);
$meta_b = BubbleableMetadata::createFromObject($dependency);
$meta_a = CacheableMetadata::createFromRenderArray($elements);
$meta_b = CacheableMetadata::createFromObject($dependency);
$meta_a->merge($meta_b)->applyTo($elements);
}
......
......@@ -8,9 +8,9 @@
namespace Drupal\field\Tests\EntityReference;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\entity_reference\Tests\EntityReferenceTestTrait;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\filter\Entity\FilterFormat;
use Drupal\system\Tests\Entity\EntityUnitTestBase;
......@@ -236,11 +236,9 @@ public function testLabelFormatter() {
],
'tags' => $this->referencedEntity->getCacheTags(),
),
'#attached' => [],
'#post_render_cache' => [],
);
$this->assertEqual(drupal_render($build[0]), drupal_render($expected_item_1), sprintf('The markup returned by the %s formatter is correct for an item with a saved entity.', $formatter));
$this->assertEqual(BubbleableMetadata::createFromRenderArray($build[0]), BubbleableMetadata::createFromRenderArray($expected_item_1));
$this->assertEqual(CacheableMetadata::createFromRenderArray($build[0]), CacheableMetadata::createFromRenderArray($expected_item_1));
// The second referenced entity is "autocreated", therefore not saved and
// lacking any URL info.
......@@ -253,8 +251,6 @@ public function testLabelFormatter() {
'tags' => $this->unsavedReferencedEntity->getCacheTags(),
'max-age' => Cache::PERMANENT,
),
'#attached' => [],
'#post_render_cache' => [],
);
$this->assertEqual($build[1], $expected_item_2, sprintf('The render array returned by the %s formatter is correct for an item with a unsaved entity.', $formatter));
......
......@@ -9,6 +9,7 @@
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Xss as CoreXss;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
......@@ -18,7 +19,6 @@
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Render\Element;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Session\AccountInterface;
......@@ -742,8 +742,8 @@ public function getItems(ResultRow $values) {
// Merge the cacheability metadata of the top-level render array into
// each child because they will most likely be rendered individually.
if (isset($render_array['#cache'])) {
BubbleableMetadata::createFromRenderArray($render_array)
->merge(BubbleableMetadata::createFromRenderArray($items[$delta]['rendered']))
CacheableMetadata::createFromRenderArray($render_array)
->merge(CacheableMetadata::createFromRenderArray($items[$delta]['rendered']))
->applyTo($items[$delta]['rendered']);
}
// Add the raw field items (for use in tokens).
......
......@@ -7,7 +7,9 @@
namespace Drupal\Tests\Core\Cache;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Tests\Core\Render\TestCacheableDependency;
use Drupal\Tests\UnitTestCase;
use Drupal\Core\Render\Element;
......@@ -69,4 +71,76 @@ public function providerSetCacheMaxAge() {
[8.0, TRUE]
];
}
/**
* @covers ::createFromRenderArray
* @dataProvider providerTestCreateFromRenderArray
*/
public function testCreateFromRenderArray(array $render_array, CacheableMetadata $expected) {
$this->assertEquals($expected, CacheableMetadata::createFromRenderArray($render_array));
}
/**
* Provides test data for createFromRenderArray().
*
* @return array
*/
public function providerTestCreateFromRenderArray() {
$data = [];
$empty_metadata = new CacheableMetadata();
$nonempty_metadata = new CacheableMetadata();
$nonempty_metadata->setCacheContexts(['qux'])
->setCacheTags(['foo:bar']);
$empty_render_array = [];
$nonempty_render_array = [
'#cache' => [
'contexts' => ['qux'],
'tags' => ['foo:bar'],
'max-age' => Cache::PERMANENT,
],
];
$data[] = [$empty_render_array, $empty_metadata];
$data[] = [$nonempty_render_array, $nonempty_metadata];
return $data;
}
/**
* @covers ::createFromObject
* @dataProvider providerTestCreateFromObject
*/
public function testCreateFromObject($object, CacheableMetadata $expected) {
$this->assertEquals($expected, CacheableMetadata::createFromObject($object));
}
/**
* Provides test data for createFromObject().
*
* @return array
*/
public function providerTestCreateFromObject() {
$data = [];
$empty_metadata = new CacheableMetadata();
$nonempty_metadata = new CacheableMetadata();
$nonempty_metadata->setCacheContexts(['qux'])
->setCacheTags(['foo:bar'])
->setCacheMaxAge(600);
$uncacheable_metadata = new CacheableMetadata();
$uncacheable_metadata->setCacheMaxAge(0);
$empty_cacheable_object = new TestCacheableDependency([], [], Cache::PERMANENT);
$nonempty_cacheable_object = new TestCacheableDependency(['qux'], ['foo:bar'], 600);
$uncacheable_object = new \stdClass();
$data[] = [$empty_cacheable_object, $empty_metadata];
$data[] = [$nonempty_cacheable_object, $nonempty_metadata];
$data[] = [$uncacheable_object, $uncacheable_metadata];
return $data;
}
}
......@@ -132,39 +132,4 @@ public function providerTestCreateFromRenderArray() {
return $data;
}
/**
* @covers ::createFromObject
* @dataProvider providerTestCreateFromObject
*/
public function testCreateFromObject($object, BubbleableMetadata $expected) {
$this->assertEquals($expected, BubbleableMetadata::createFromObject($object));
}
/**
* Provides test data for createFromObject().
*
* @return array
*/
public function providerTestCreateFromObject() {
$data = [];
$empty_metadata = new BubbleableMetadata();
$nonempty_metadata = new BubbleableMetadata();
$nonempty_metadata->setCacheContexts(['qux'])
->setCacheTags(['foo:bar'])
->setCacheMaxAge(600);
$uncacheable_metadata = new BubbleableMetadata();
$uncacheable_metadata->setCacheMaxAge(0);
$empty_cacheable_object = new TestCacheableDependency([], [], Cache::PERMANENT);
$nonempty_cacheable_object = new TestCacheableDependency(['qux'], ['foo:bar'], 600);
$uncacheable_object = new \stdClass();
$data[] = [$empty_cacheable_object, $empty_metadata];
$data[] = [$nonempty_cacheable_object, $nonempty_metadata];
$data[] = [$uncacheable_object, $uncacheable_metadata];
return $data;
}
}
......@@ -643,8 +643,6 @@ public function providerTestAddDependency() {
'tags' => [],
'max-age' => Cache::PERMANENT,
],
'#attached' => [],
'#post_render_cache' => [],
],
],
// Empty render array, some cacheability.
......@@ -657,8 +655,6 @@ public function providerTestAddDependency() {
'tags' => ['foo'],
'max-age' => Cache::PERMANENT,
],
'#attached' => [],
'#post_render_cache' => [],
],
],
// Cacheable render array, some cacheability.
......@@ -677,8 +673,6 @@ public function providerTestAddDependency() {
'tags' => ['bar', 'foo'],
'max-age' => 600,
],
'#attached' => [],
'#post_render_cache' => [],
],
],
// Cacheable render array, no cacheability.
......@@ -697,8 +691,6 @@ public function providerTestAddDependency() {
'tags' => ['bar'],
'max-age' => 0,
],
'#attached' => [],
'#post_render_cache' => [],
],
],
];
......
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