Problem/Motivation

When Drupal\Core\Render\Renderer::doRender() processes #lazy_builder callbacks the #type defaults are already loaded, so any element with a #type property will not be rendered properly.

A #pre_render callback often adds new types in child elements only, when it becomes possible to have the type defaults loaded. With a #lazy_builder callback it's not common to return a child element.

Steps to reproduce

  1. Create a render array with a lazy builder
  2. In the lazy builder set the render array's #type property
  3. See that the defaults for said #type are not merged in by the Renderer and that the rendering breaks

Proposed resolution

Set the defaults on a lazily built render array also.

Remaining tasks

Decide what to do with pre_render (and post_render) as theoretically those could also set or change the type.This probably deserves a follow-up as it has far more edge cases than lazy builders because the array specifying a lazy builder cannot have a #type set.

User interface changes

N/A

Introduced terminology

N/A

API changes

N/A

Data model changes

N/A

Release notes snippet

N/A

Issue fork drupal-2609250

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

Eric_A created an issue. See original summary.

eric_a’s picture

Status: Active » Needs review
Issue tags: +Needs tests
StatusFileSize
new954 bytes

Something like this.

eric_a’s picture

Title: Renderer does not allow for lazy builders to return types (defaults not loaded) » Lazy builder broken (#type defaults not loaded)
eric_a’s picture

Issue tags: +D8 cacheability
eric_a’s picture

Status: Needs review » Closed (works as designed)

I got confused. The observed behavior is the same as with the old, familiar #pre_render: to have element type defaults applied automatically, having child elements rendered is the only option.

Closing this one.

eric_a’s picture

But then again, a #pre_render callback would normally add new types in child elements only. With a #lazy_builder callback it's different...

eric_a’s picture

Status: Closed (works as designed) » Needs review

Back to Needs review because of #6.

eric_a’s picture

Issue summary: View changes
eric_a’s picture

Issue summary: View changes

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

xjm’s picture

Is there any example in core that can reproduce this bug, or is it possible to provide a short example snippet and steps to reproduce and test how it is not rendered properly?

eric_a’s picture

Is there any example in core that can reproduce this bug

Not that I'm aware of.

is it possible to provide a short example snippet and steps to reproduce and test how it is not rendered properly?

$element = [
  '#lazy_builder' => [
    function($uri) {
      return [
        '#type' => 'more_link',
        '#url' => Url::fromUri($uri),
      ];
    },
    ['https://www.drupal.org'],
  ],
];

This element will not render as a more link. It would if the #lazy_builder callback returned a child element.
I'm not too sure this is a true bug. Our default renderer implementation shows the same behavior with #pre_render for ages. It's just not documented behavior.

eric_a’s picture

Issue summary: View changes
eric_a’s picture

Fixed ordering in the snippet.

eric_a’s picture

And fixed the text that goes with it. It works if the #lazy_builder callback returns a child element.

wim leers’s picture

It would if the #lazy_builder callback returned a child element.

Exactly.

I'm not too sure this is a true bug. Our default renderer implementation shows the same behavior with #pre_render for ages. It's just not documented behavior.

Indeed.

But we should make a decision here, and then help the developer by throwing an assertion.

eric_a’s picture

StatusFileSize
new1.97 KB

Here's a patch with the assert() approach, both for #lazy_builder and #pre_render.
If we leave the actual processing as is and confine ourselves to adding more assertions to Drupal\Core\Render, then the issue category should probably change from "Bug report" to something else and "D8 cacheability" should be removed in favor of "DX (Developer Experience)".

Status: Needs review » Needs work

The last submitted patch, 17: lazy-builder-type-2609250-17.patch, failed testing.

eric_a’s picture

Status: Needs work » Needs review
StatusFileSize
new1.11 KB

I've removed the #pre_render part assert(). I think it's impossible in the #pre_render part to determine fully within the assert() if a #type property was returned (wether one already existed or not, with the same or a different value.)

Status: Needs review » Needs work

The last submitted patch, 19: lazy-builder-type-2609250-19.patch, failed testing.

lauriii’s picture

Discussed with @alexpott, @xjm, @Cottser and @joelpittet. We agreed that this should remain as a major bug because this is very bad DX. It is very difficult to debug why render elements are not rendered properly on the lazy builder.

lauriii’s picture

Issue tags: +Triaged core major

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

upchuk’s picture

Hey there,

Last movement on this issue was quite a long time ago so it's worth looking at it again. I updated the docs on auto-placeholdering to mention this fact. I just lost 40 minutes figuring out why the heck my renderable was returning empty.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

lukasss’s picture

Version: 8.6.x-dev » 8.9.x-dev

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

bbu23’s picture

Thanks Upchuk for updating the documentation. I was having the same problem: wondering why my lazy builder was not working. I was checking core, but didn't notice the difference.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

quietone’s picture

Got this as a random bug at DrupalSouth sprint. This is still valid and needs a reroll and tests.

ravi.shankar’s picture

Issue tags: -Needs reroll
StatusFileSize
new923 bytes
new1.66 KB

Added reroll of patch #19 on Drupal 10.1.x.

larowlan’s picture

An alternative approach here would just be to call this on the $new_element

if (isset($elements['#type'])) {
  $element += $this->elementInfo->getInfo($elements['#type'])
}

larowlan’s picture

joachim’s picture

#39 That sounds useful but I'm concerned it's a bit hacky to call getInfo() from a new place.

larowlan’s picture

It's roughly 10 lines below where the renderer already calls it (off the top of my head when I hit this issue yesterday)

andypost’s picture

+1 to #39

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

kristiaanvandeneynde’s picture

Assigned: Unassigned » kristiaanvandeneynde

kristiaanvandeneynde’s picture

Needs tests and we need to update https://www.drupal.org/docs/drupal-apis/render-api/auto-placeholdering#s...

Also we should check if we want to load defaults for pre_render and post_render. But there we need to also check if defaults were already loaded and someone changed the type.

kristiaanvandeneynde’s picture

Assigned: kristiaanvandeneynde » Unassigned
Status: Needs work » Needs review
kristiaanvandeneynde’s picture

Why is phpstan choking on this on an unrelated variable that it should know how to inspect.

kristiaanvandeneynde’s picture

Meh added it to the baseline. The alternative was asinine:

    if ($this->rendererConfig['debug'] === TRUE) {
      $render_start = microtime(TRUE);
    }

    // Had to become (also further down):
    $debug_mode = $this->rendererConfig['debug'] === TRUE;
    if ($debug_mode) {
      $render_start = microtime(TRUE);
    }

To hell with that :)

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs issue summary update

Wonder if the IS could get some love. Using the standard template please

Good to see the tests still pass but believe will still need test coverage

Thanks.

kristiaanvandeneynde’s picture

Issue summary: View changes
Issue tags: -Needs issue summary update
kristiaanvandeneynde’s picture

Will try to find time next week to test this. If someone wants to have a crack at this in the meantime, go ahead.

kristiaanvandeneynde’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests

Added tests

kristiaanvandeneynde’s picture

All green and the "test-only changes" job shows the failure we want to see.

joelpittet’s picture

Status: Needs review » Reviewed & tested by the community

That looks great, thanks for the red/green tests. I scoured over the code and didn’t spot anything concerning.

catch’s picture

Status: Reviewed & tested by the community » Needs work

The issue summary still has:

Decide what to do with pre_render (and post_render) as theoretically those could also set or change the type.This probably deserves a follow-up as it has far more edge cases than lazy builders because the array specifying a lazy builder cannot have a #type set.

Does a folow-up exist?

Also not sure why this is adding a new entry to the phpstan baseline, is it somehow picking up a pre-existing issue it didn't pick up before?

kristiaanvandeneynde’s picture

Does a folow-up exist?

Not yet, I can probably create on later this week. if others want to beat me to it, go ahead.

Also not sure why this is adding a new entry to the phpstan baseline, is it somehow picking up a pre-existing issue it didn't pick up before?

Yes.

kristiaanvandeneynde’s picture

Status: Needs work » Reviewed & tested by the community

  • catch committed fba99e20 on 11.x
    Issue #2609250 by kristiaanvandeneynde, eric_a, ravi.shankar, larowlan,...

  • catch committed 2e93ec30 on 10.5.x
    Issue #2609250 by kristiaanvandeneynde, eric_a, ravi.shankar, larowlan,...
catch’s picture

Version: 11.x-dev » 10.5.x-dev
Status: Reviewed & tested by the community » Fixed

Ran into this the other day on a client project, and realised I needed to nest the render array after a few minutes headscratching, but didn't put two and two together that it was this specific bug until I came back here. This is annoying and we should fix it.

Committed/pushed to 11.x and cherry-picked to 10.5.x, thanks!

Status: Fixed » Closed (fixed)

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