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
- Create a render array with a lazy builder
- In the lazy builder set the render array's #type property
- 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
| Comment | File | Size | Author |
|---|
Issue fork drupal-2609250
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:
- 2609250-lazy-builder-broken
changes, plain diff MR !11250
Comments
Comment #2
eric_a commentedSomething like this.
Comment #3
eric_a commentedComment #4
eric_a commentedComment #5
eric_a commentedI 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.
Comment #6
eric_a commentedBut then again, a
#pre_rendercallback would normally add new types in child elements only. With a#lazy_buildercallback it's different...Comment #7
eric_a commentedBack to Needs review because of #6.
Comment #8
eric_a commentedComment #9
eric_a commentedComment #11
xjmIs 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?
Comment #12
eric_a commentedNot that I'm aware of.
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.
Comment #13
eric_a commentedComment #14
eric_a commentedFixed ordering in the snippet.
Comment #15
eric_a commentedAnd fixed the text that goes with it. It works if the #lazy_builder callback returns a child element.
Comment #16
wim leersExactly.
Indeed.
But we should make a decision here, and then help the developer by throwing an assertion.
Comment #17
eric_a commentedHere'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)".
Comment #19
eric_a commentedI'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.)
Comment #21
lauriiiDiscussed 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.
Comment #22
lauriiiComment #27
upchuk commentedHey 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.
Comment #29
lukasss commentedComment #31
bbu23Thanks 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.
Comment #37
quietone commentedGot this as a random bug at DrupalSouth sprint. This is still valid and needs a reroll and tests.
Comment #38
ravi.shankar commentedAdded reroll of patch #19 on Drupal 10.1.x.
Comment #39
larowlanAn alternative approach here would just be to call this on the $new_element
Comment #40
larowlanComment #41
joachim commented#39 That sounds useful but I'm concerned it's a bit hacky to call getInfo() from a new place.
Comment #42
larowlanIt's roughly 10 lines below where the renderer already calls it (off the top of my head when I hit this issue yesterday)
Comment #43
andypost+1 to #39
Comment #45
kristiaanvandeneyndeComment #47
kristiaanvandeneyndeNeeds 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.
Comment #48
kristiaanvandeneyndeComment #49
kristiaanvandeneyndeWhy is phpstan choking on this on an unrelated variable that it should know how to inspect.
Comment #50
kristiaanvandeneyndeMeh added it to the baseline. The alternative was asinine:
To hell with that :)
Comment #51
smustgrave commentedWonder 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.
Comment #52
kristiaanvandeneyndeComment #53
kristiaanvandeneyndeWill try to find time next week to test this. If someone wants to have a crack at this in the meantime, go ahead.
Comment #54
kristiaanvandeneyndeAdded tests
Comment #55
kristiaanvandeneyndeAll green and the "test-only changes" job shows the failure we want to see.
Comment #56
joelpittetThat looks great, thanks for the red/green tests. I scoured over the code and didn’t spot anything concerning.
Comment #57
catchThe issue summary still has:
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?
Comment #58
kristiaanvandeneyndeNot yet, I can probably create on later this week. if others want to beat me to it, go ahead.
Yes.
Comment #59
kristiaanvandeneyndeAdded follow-up here: #3517121: Decide if we need to load #type defaults for #pre_render callbacks
So setting back to RTBC.
Comment #62
catchRan 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!