Problem/Motivation

IIRC entity_embed chose to use #post_render_cache to be able to support entity access checking correctly. At the time, this was the only solution possible to only show embedded entities if the current user could access them. It does come at the cost of significantly worse cacheability though!

Since then, #2429257: Bubble cache contexts has landed. This makes it possible to bubble cache contexts. Access results specify cache contexts. This is how we fixed #2099137: Entity/field access and node grants not taken into account with core cache contexts, which is basically the same problem.

Proposed resolution

Make entity_embed bubble the cacheability metadata associated with an entity's access result.

Basically:

$result = new FilterProcessResult($text);

// [Do the work of rendering the entity and its access checking.]
$access = …
$entity = …

// Make the FilterProcessResult have the cacheability metadata of the entity access result and the rendered entity itself.
$result = $result->merge($access)->merge($entity);

$result->setProcessedText(str_replace(…));

Remaining tasks

Discuss.

User interface changes

None.

API changes

The "context alter" hook would go away.

Comments

wim leers’s picture

Happy to provide a patch if desired.

wim leers’s picture

Title: Reconsider use of #post_render_cache » Reconsider use of #post_render_cache now that we have cache context bubbling
dave reid’s picture

Excellent Wim, I'll take a look at implementing this.

wim leers’s picture

wim leers’s picture

Category: Task » Bug report

#2478483: Introduce placeholders (#lazy_builder) to replace #post_render_cache landed, #post_render_cache is gone, so by now this is a bug; it'll prevent Entity Embed in its current state from working.

dave reid’s picture

Title: Reconsider use of #post_render_cache now that we have cache context bubbling » Replace #post_render_cache with #lazy_builder and use cache context bubbling
Assigned: Unassigned » dave reid

Will be working on this this weekend.

dave reid’s picture

Relevant change notice: https://www.drupal.org/node/2498803

wim leers’s picture

Cool, ping me if you have questions :)

slashrsm’s picture

Issue tags: +Media Initiative, +sprint
dave reid’s picture

@Wim Leers: So with this new code, we don't actually need placeholders at all? We should just render directly in the filter itself?

wim leers’s picture

Correct.

dave reid’s picture

@Wim Leers: I'm having problems implementing this part:

// Make the FilterProcessResult have the cacheability metadata of the entity access result and the rendered entity itself.
$result = $result->merge($access)->merge($entity);

Neither using the entity object, nor the AccessResultInterface result from $entity->access() seems to be usable to provide to merge(). Any ideas on where we should look for that?

dave reid’s picture

Hrm, looks like I should be doing something like this?

$result->merge(BubbleableMetadata::createFromObject($entity))->merge(BubbleableMetadata::createFromObject($access));
dave reid’s picture

Blargh, that doesn't work at all.

dave reid’s picture

When I attempt to do $result = $result->merge(...) I always get:

Warning: Missing argument 1 for Drupal\filter\FilterProcessResult::__construct(), called in /home/davereid/Sync/projects/drupal8/core/lib/Drupal/Core/Cache/CacheableMetadata.php on line 142 and defined in Drupal\filter\FilterProcessResult->__construct() (line 86 of core/modules/filter/src/FilterProcessResult.php).
Notice: Undefined variable: processed_text in Drupal\filter\FilterProcessResult->__construct() (line 87 of core/modules/filter/src/FilterProcessResult.php).

wim leers’s picture

So, FilterProcessResult extends BubbleableMetadata. BubbleableMetadata::merge() typehints to CacheableMetadata, which is the base class of BubbleableMetadata. So you can give it either CacheableMetadata or BubbleableMetadata objects.

This is therefore what you want:

$result = $result->merge(CacheableMetadata::createFromObject($access))
  ->merge(CacheableMetadata::createFromObject($entity));

Yes, this is painful. This is why for regular rendering code, we have RendererInterface::addCacheableDependency(). We could consider adding that to BubbleableMetadata/CacheableMetadata as well. Then it'd become simply:

$result = $result->addCacheableDependency($access)
  ->addCacheableDependency($entity);
wim leers’s picture

(We cross-posted.)

Warning: Missing argument 1 for Drupal\filter\FilterProcessResult::__construct(), called in /home/davereid/Sync/projects/drupal8/core/lib/Drupal/Core/Cache/CacheableMetadata.php on line 142 and defined in Drupal\filter\FilterProcessResult->__construct() (line 86 of core/modules/filter/src/FilterProcessResult.php).

Oh :(

That's a bug. This is because of:

  public function merge(CacheableMetadata $other) {
    $result = new static();

Which means that upon merging, a new FilterProcessResult object is constructed, but it's not passed any parameters, even though BubbleableMetadata expects that.

IOW: You've found a core bug.

dave reid’s picture

Shouldn't it be using $result = clone $this instead?

dave reid’s picture

wim leers’s picture

I think you did :D :D

Wanna file a core patch? :)

dave reid’s picture

wim leers’s picture

dave reid’s picture

Issue tags: -sprint +D8Media
dave reid’s picture

Status: Active » Fixed

Ok, calling this good for now.

wim leers’s picture

Can you link to the commit(s) that fixed it?

dave reid’s picture

dave reid’s picture

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.