Change record status: 
Project: 
Introduced in branch: 
8.0.x
Introduced in version: 
8.0.0-BETA12
Description: 
  1. Added #lazy_builder callbacks, which are given only arguments that may contain only primitive types (string, int, bool, float, NULL), not objects.
  2. Added #create_placeholder, which allows a render array to be forced to be placeholdered.
    In a follow-up issue, automatic placeholdering will be introduced. (Basically: if max-age = 0 or high-cardinality cache contexts are present for the subtree, set #create_placeholder = TRUE.)
  3. Removed #post_render_cache (the above is its successor).
  4. Removed RendererInterface::generateCachePlaceholder() and drupal_render_cache_generate_placeholder() because they are now obsolete.

A new general recommendation

  • A #lazy_builder can build a render array from scratch. It is given only the arguments that you provide it, and that context may contain only primitive types (string, bool, int, float, NULL).
  • When specifying #cache, provide a #lazy_builder to build out the entire render array in case of a cache miss.
  • Together, the two above points allow Drupal to optimize: it is able to determine based on how dynamic the contents of the #builder callback are, whether the #lazy_builder (and thus the associated render array) should be rendered during the initial page load, or via BigPipe or ESI.
  • Arguments should only be passed from an object that is already provided to a parent element. Otherwise, if the render array is standalone (not a child of any parent element) then the arguments should be retrieved inside the callback itself.

Concrete changes

Before
            $callback = 'comment.post_render_cache:renderForm';
            $context = array(
              'entity_type' => $entity->getEntityTypeId(),
              'entity_id' => $entity->id(),
              'field_name' => $field_name,
              'comment_type' => $this->getFieldSetting('comment_type'),
            );
            $placeholder = drupal_render_cache_generate_placeholder($callback, $context);
            $output['comment_form'] = array(
              '#post_render_cache' => array(
                $callback => array(
                  $context,
                ),
              ),
              '#markup' => $placeholder,
            );
  public function renderForm(array $element, array $context) {
    $values = array(
      'entity_type' => $context['entity_type'],
      'entity_id' => $context['entity_id'],
      'field_name' => $context['field_name'],
      'comment_type' => $context['comment_type'],
      'pid' => NULL,
    );
    $comment = $this->entityManager->getStorage('comment')->create($values);
    $form = $this->entityFormBuilder->getForm($comment);
    $markup = $this->renderer->render($form);

    $callback = 'comment.post_render_cache:renderForm';
    $placeholder = $this->generatePlaceholder($callback, $context);
    $element['#markup'] = str_replace($placeholder, $markup, $element['#markup']);
    $element = $this->renderer->mergeBubbleableMetadata($element, $form);

    return $element;
  }
After
            $output['comment_form'] = [
              '#lazy_builder' => ['comment.lazy_builders:renderForm', [
                $entity->getEntityTypeId(),
                $entity->id(),
                $field_name,
                $this->getFieldSetting('comment_type'),
              ]],
              '#create_placeholder' => TRUE,
            ];
  public function renderForm($commented_entity_type_id, $commented_entity_id, $field_name, $comment_type_id) {
    $values = array(
      'entity_type' => $commented_entity_type_id,
      'entity_id' => $commented_entity_id,
      'field_name' => $field_name,
      'comment_type' => $comment_type_id,
      'pid' => NULL,
    );
    $comment = $this->entityManager->getStorage('comment')->create($values);
    return $this->entityFormBuilder->getForm($comment);
  }
Impacts: 
Module developers
Updates Done (doc team, etc.)
Online documentation: 
Not done
Theming guide: 
Not done
Module developer documentation: 
Not done
Examples project: 
Not done
Coder Review: 
Not done
Coder Upgrade: 
Not done
Other: 
Other updates done

Comments

Anonymous’s picture

Why do I need to specifically declare #create_placeholder ?

Anonymous’s picture

Also, why can't we provide whatever value we want but only strings, integers, null...? Can't the the arguments be serialized before rendering and unserialized before passing to the builder? If I have a configuration that has many values, has arrays or unknown structures(arrays) then I am passing it as serialized string. I don't see why this could not be taken care of in the core.

Wim Leers’s picture

Please read the issue that introduced it. Not going to repeat an entire discussion in comments here.

Wim Leers’s picture

You don't, see https://www.drupal.org/node/2499157. #create_placeholder = TRUE is for manual placeholdering.