Problem/Motivation
The current placeholder functionality is a lot of work to get done, while all you want to do is add a placeholder and call a function later.
Proposed resolution
For D7, I have implemented the API in the following way:
return RenderCacheControllerBase::getPlaceholder('comment_node_page_additions', array(
'%node' => $node->nid,
));
The interface looks like this:
interface RenderCacheControllerPlaceholderInterface {
public static function getPlaceholder($function, array $args = array());
public static function postRenderCacheCallback(array $element, array $context);
public static function loadPlaceholderFunctionArgs(array $context);
}
The implementation like this:
// -----------------------------------------------------------------------
// RenderCacheControllerPlaceholderInterface
public static function getPlaceholder($function, array $args = array()) {
$callback = 'RenderCacheControllerBase::postRenderCacheCallback';
$context = array(
'function' => $function,
'args' => $args,
);
$placeholder = drupal_render_cache_generate_placeholder($context['function'], $context);
return array(
'#post_render_cache' => array(
$callback => array(
$context,
),
),
'#markup' => $placeholder,
);
}
public static function postRenderCacheCallback(array $element, array $context) {
$placeholder = drupal_render_cache_generate_placeholder($context['function'], $context);
// Check if the placeholder is present at all.
if (strpos($element['#markup'], $placeholder) === FALSE) {
return $element;
}
$function = $context['function'];
$args = static::loadPlaceholderFunctionArgs($context);
$new_element = call_user_func_array($function, $args);
// @todo D8 - This needs to switch to some function, which ensures #attached, #cache tags and #post render cache can be updated
// from this.
$markup = RenderCacheControllerBase::drupalRender($new_element);
$element['#markup'] = str_replace($placeholder, $markup, $element['#markup']);
return $element;
}
public static function loadPlaceholderFunctionArgs(array $context) {
$args = array();
foreach ($context['args'] as $key => $arg) {
// In case a dynamic argument has been passed, load it with the loader.
if (preg_match('/^%(|' . DRUPAL_PHP_FUNCTION_PATTERN . ')$/', $key, $matches)) {
if (!empty($matches[1]) && function_exists($matches[1] . '_load')) {
$loader_function = $matches[1] . '_load';
$arg = $loader_function($arg);
}
}
$args[] = $arg;
}
return $args;
}
Note: This has an added menu_loader for convenience similar to how %node works in Drupal 7.
Update: http://cgit.drupalcode.org/render_cache/tree/includes/RenderCachePlaceho...
has the newest code.
It has 'multiple' support - so you can group multiple objects together so you get a function call like:
RenderCachePlaceholder::getPlaceholder('post_render_function', $object_1, TRUE);
RenderCachePlaceholder::getPlaceholder('post_render_function', $object_2, TRUE);
function post_render_function(array $args) {
$placeholders = array();
foreach ($args as $placeholder => $object) {
$placeholders[$placeholder] = array(
'#markup' => $object->id,
);
}
return $placeholders;
}
Remaining tasks
- Discuss implementation
- Find a class or service for it to add to
- Make a patch
API changes
API Addition:
- Added RenderCacheControllerBase::getPlaceholder() function.
Comments
Comment #1
Fabianx CreditAttribution: Fabianx commentedComment #2
Fabianx CreditAttribution: Fabianx commentedComment #3
Fabianx CreditAttribution: Fabianx commentedComment #4
Fabianx CreditAttribution: Fabianx commentedComment #5
Fabianx CreditAttribution: Fabianx commentedDawehner thinks this should be at least major.
Comment #6
idebr CreditAttribution: idebr commentedI think the correct issue status is 'Active', since there is no patch attached to this issue.
Comment #7
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedThis was fixed via #2478483: Introduce placeholders (#lazy_builder) to replace #post_render_cache. :-)