Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
Using #lazy_builder and ['#cache']['keys'] together on the page level render array causes a
DomainException: When a #lazy_builder callback is specified, no properties can exist; all properties must be generated by the #lazy_builder callback. You specified the following properties: #cache_properties. in Drupal\Core\Render\Renderer->doRender() (line 331 of core/lib/Drupal/Core/Render/Renderer.php).
To reproduce:
- 1. Create a module "mymodule". The mymodule.routing.yml file should have the following contents:
mycache.content: path: '/mycache' defaults: _controller: '\Drupal\mycache\Controller\MyCacheController::content' _title: 'My Cache' requirements: _permission: 'access content'
- 2. The src/Controller/MyCacheController.php file has:
<?php /** * @file * Contains \Drupal\mycache\Controller\MyCacheController. * * @see https://www.drupal.org/developing/api/8/render/arrays/cacheability. */ namespace Drupal\mycache\Controller; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Cache\Cache; class MyCacheController extends ControllerBase { public function content() { $renderer = \Drupal::service('renderer'); $current_user = \Drupal::currentUser(); $build = [ '#lazy_builder' => [ 'Drupal\mycache\Controller\MyCacheController:mycache_helo', [$current_user->getAccountName()], ], '#create_placeholder' => TRUE, '#cache' => [ 'keys' => ['mycache'], 'contexts' => ['user'], ], ]; return $build; } public function mycache_helo($name) { return [ '#markup' => t('Hi @name', ['@name' => $name]), ]; } }
- Enable the module and go to /mycache.
- The above error is logged in dblog.
This issue happens only if [#cache]['keys'] is present. It is caused by Drupal\Core\Render\MainContent\HtmlRenderer::prepare() executing
$main_content['#cache_properties'][] = '#title';
and Drupal\Core\Render\Renderer::doRender() throwing
throw new \DomainException(sprintf('When a #lazy_builder callback is specified, no properties can exist; all properties must be generated by the #lazy_builder callback. You specified the following properties: %s.', implode(', ', $unsupported_keys)));
Comment | File | Size | Author |
---|---|---|---|
#15 | 2752889-cache_properties-14.patch | 811 bytes | Aron Novak |
#12 | mymodule.zip | 2.68 KB | JeroenT |
Comments
Comment #2
czigor CreditAttribution: czigor at Liip for FREITAG lab. AG commentedIt seems that page content (the render array returned by the controller) should not be tried to be loaded via a #lazy_builder. The workaround I used was to move the whole array one level lower (into a top-level render array property).
Comment #11
JeroenTI tried to reproduce this issue. But I couldn't.
Are there any clear steps or some file I can use I can use to reproduce this error? Or even better, a failing test.
Comment #12
JeroenTOk, So I was able to reproduce this error on Drupal 9.3 with the module I uploaded.
This no longer generates an exception, but is now a warning in the watchdog:
The stack trace:
Comment #13
JeroenTComment #15
Aron NovakIt will need tests, but let's see if this change, what somehow solves the error, cause any test breakage or not.
Comment #17
cmlaraI'm trying to understand what is this issue is asserting is wrong? As I understand #lazy_builder's are expected to be highly variable so it makes sense that providing keys when declaring the placeholder is forbidden as that context data is assumed to not be available at that time because only the #lazy_builder can know what it will work with, for example if it pulls a node it needs to get all the node's current key tags.
Generally #lazy_builder's place a unique string in the rendered page which is parsed by the render. This string links back to the #lazy_builder (with necessary serialized data).
It is my understanding the results of the #lazy_builder callback are stored in the DPC including any cache keys returned by the #lazy_builder allowing fast retrieval where possible and purging when data is adjusted. This is much like the results of a block being stored in the DPC, the #lazy_builder is essentially a distinct element in the page.
Is there something I am missing? In my opinion #2752889-2: #lazy_builder and ['#cache']['keys'] generates warnin assert(): When a #lazy_builder callback is specified, no properties can exist; all properties must be generated by the #lazy_builder callback. You specified the following properties: #cache_properties. appears to be the correct way to handler setting cache tags with #lazy_builder's making this issue appear to me to be Works as Designed.