We are recommending not to call drupal_render() in preprocess, now formally documented here: http://drupal.org/node/1920746
Per #1898432-20: node.module - Convert PHPTemplate templates to Twig, the same should be true of drupal_render_children().
drupal_render_children should be implicit when something is printed using twig, so that we don't have to do it in preprocess.
Comment | File | Size | Author |
---|---|---|---|
#71 | views_ef_fieldset6.jpg | 65.97 KB | Pol |
#70 | interdiff.txt | 710 bytes | rteijeiro |
#70 | drupal_render_should-1920886-70.patch | 1.36 KB | rteijeiro |
#68 | drupal_render_should-1920886-68.patch | 1.33 KB | lauriii |
#64 | drupal_render_should-1920886-64.patch | 1.41 KB | lauriii |
Comments
Comment #1
star-szrTagging and re-titling.
Comment #2
Fabianx CreditAttribution: Fabianx commentedThis is problematic, because $form had a #theme, which would lead to an infinite loop.
What we can do is have a function like:
drupal_lazy_render_children()
which just does.
That way render will still correctly render the children, but won't run into the infinite loop.
Another possibility is to introduce type hints for twig, like:
'#render => TRUE
#render_children => TRUE
'
as part of the render array().
That can be used for optimization of the code internally.
Comment #3
jenlamptonI'm running into this same problem in color.module conversion to Twig.
The problem is if we leave $variables['children'] = drupal_render_children($form) in the preprocess (temporarily) then the entire form gets rendered twice, since drupal_render_children($form) at this point contains the whole form (since nothing else has been rendered yet).
As a temporary workaround for this core patch, I'm going to make a copy of $form and call it $children, and remove the corresponding element from $children every time the element in $form is preprocessed into it's own variable. Then $variables['children'] = drupal_render_children($children) returns the expected output.
Comment #4
star-szrAn example from #1898052: color.module - Convert theme_ functions to Twig that I'm working on:
Comment #5
jenlamptonThis temporary workaround is also causing some problems with hide(). If we preprocess everything into separate variables then we can't use hide in the template files. Maybe this is okay? (death to hide!) but we had originally thought we needed to keep it (death to preprocess!).
Maybe time to re-evaluate, and solve this one. Bumping up priority.
Comment #6
Fabianx CreditAttribution: Fabianx commented#5 This just means that functionality does not work that did not work before, so that is not a big deal.
We can remove drupal_render_children() once we have sorted out the infinite loop problem.
Because in general:
should work fine.
From a performance perspective:
drupal_render_children vs. drupal_render call -> no measurable difference, because isset() is so freaking fast ...
Comment #7
jenlamptonWe should be able to tell when Twig should render the renderable we give it, and when it should render only the children within. It only needs to render the children within when the 'render element' is specified in hook_theme - can we use that as our toggle?
Updating title to reflect the TODO action of this issue (please change again if necessary)
Comment #8
star-szrTitle is better I think, just fixing one thing. Thanks @jenlampton :)
Comment #9
Fabianx CreditAttribution: Fabianx commentedThe example exists here:
node-edit-form.html.twig
in http://drupal.org/node/1898432#comment-7162006
Comment #10
star-szrHere's one idea based on a discussion @Fabianx and I had yesterday. With this patch in place, node-edit-form.html.twig works fine, no preprocess needed and hide() can be used in the template.
Comment #12
shanethehat CreditAttribution: shanethehat commentedWhen this is resolved, seven.theme will need a cleanup as mentioned in #72 of #1898432: node.module - Convert PHPTemplate templates to Twig
Comment #13
jenlampton#10: 1920886-10.patch queued for re-testing.
Comment #15
Fabianx CreditAttribution: Fabianx commentedAfter long discussions, I think Twig should check via isset for this key manually and call drupal_render children instead.
The necessary key can be set when $form[$render_element] = $element is set in theme.inc.
( will add correct line later )
Comment #16
Fabianx CreditAttribution: Fabianx commentedAnd here is the patch for that:
* Gives a hint to rendering engines that this is coming from a self-render to prevent recursion.
* Changes twig engine to take this new hint into account.
a) Patch - DONE
Stil todo:
b) Test Coverage
c) Documenation - especially for valid render array keys
Have fun, Twig Patch Squad! :-)
Comment #17
webchickWow! Well that's far less problematic than I was picturing. :) Great work!
Comment #18
Fabianx CreditAttribution: Fabianx commentedWell, if you apply a working patch, then it is easier :-) ...
Correct patch attached! :-)
Comment #19
star-szrWe discussed adding this logic in drupal_render() itself, initial tests seem to show that will work as well.
I'll look at writing some tests for this. Thanks @Fabianx!
Comment #21
star-szrI'll just leave this here… :)
Comment #22
star-szr#18: core_0001-Issue-1920886-by-Cottser-Twig-should-check-to-see-if-v2.patch queued for re-testing.
Comment #23
star-szr(Sending for a re-test, ran some of the tests locally and they passed)
It looks like we'd be documenting this in http://api.drupal.org/api/drupal/developer%21topics%21forms_api_referenc... as an "internal property". I have no idea how one goes about changing that documentation :)
Comment #24
Fabianx CreditAttribution: Fabianx commentedThis will need tests.
Besides that: RTBC :-).
Comment #25
star-szrHoping to get something up this weekend :)
Comment #26
star-szrHere's a test for this. Forgoing a test-only patch because the pre-patch behaviour is an infinite loop. I tested locally and running the test results in an infinite loop so it is testing the behaviour.
I also created a postponed issue in the documentation project to document this new internal property: #1993198: Document new internal FAPI property: #render_children
Re-titling to indicate that this is a change for the theme system in general, not just Twig.
Comment #27
Fabianx CreditAttribution: Fabianx commented* Easy patch.
* Has nice test coverage.
* Well documented.
* isset() is very very fast
AAAAND fixes one big WTF! of Drupal 7.
=> Full win, RTBC
Comment #28
catchThere's a bunch of calls to this in preprocess in core: http://api.drupal.org/api/drupal/core%21includes%21common.inc/function/d...
Is this something that'll get handled in the twig conversions or do we need explicit follow-ups to go through those?
Patch looks good to me and a nice step along the way to removing preprocess bloat.
Comment #29
star-szr@catch - we can definitely remove the calls from the .tpl.php preprocess functions right away (and in some cases remove preprocess functions entirely!). As for the theme_ functions I think it would make more sense to do that as follow-up.
Comment #30
star-szrI didn't think of this before but I think we can avoid the function call to drupal_render_children() altogether.
Comment #31
star-szrHere's another take on this, just has drupal_render() check for the #render_element key before any calls to theme(). Added test coverage for #theme_wrappers as well.
Comment #32
star-szrOf course the inline docs would need to be updated for #31. But other than that?
Comment #33
star-szrHere's a pass at the docs in line with #31.
Comment #34
star-szrComment #35
Fabianx CreditAttribution: Fabianx commentedI am very happy with #33.
Even nicer, has nice docs, tests still pass.
=> RTBC
Comment #36
Dries CreditAttribution: Dries commentedCommitted to 8.x.
This adds a little more complexity to drupal_render(), but that can be offset by removing drupal_render_children(), so let's open a follow up to remove that function.
Thanks!
Comment #37
moshe weitzman CreditAttribution: moshe weitzman commentedWe add drupal_render_children() in #355236: Refactor drupal_render theming - docs and everyone was happy at the time with its cleanliness. Now it looks like we are deprecating that. Can anyone explain why a that function was an improvement before and a hindrance now?
Comment #38
star-szrLooks like this did not get pushed yet?
Regarding #37, I definitely can't speak to the history of the linked issue, way before my time.
But @chx in #355236: Refactor drupal_render theming - docs:
I think that's a pretty good reason. drupal_render() before this commit still had the functionality of drupal_render_children() contained within it. I don't think we necessarily have to deprecate drupal_render_children(), but I'm not sure we need it for any reason.
Snippet from drupal_render():
Comment #39
star-szrBack to RTBC per #35 to try and get this pushed or re-committed.
Comment #40
alexpottCommitted 3b1f85a and pushed to 8.x. Thanks!
Comment #41
alexpottRe #37 this patch did not deprecate
drupal_render_children()
it just made it safe to call multiple time... it's deprecation or removal and be discussed in a followup if we want to pursue that....Comment #42
star-szrFollowup created: #2004402: Discuss deprecating or removing drupal_render_children()
Comment #43
star-szrTags.
Comment #45
Fabianx CreditAttribution: Fabianx commentedUnfortunately I have to re-open this,
The committed patch is wrong, as you can set '#prefix', '#suffix' or '#pre_render', '#post_render' and easily get double output. (like a double wrapper or such). (It did fix the recursion though)
The correct patch would be like:
And then just copying the code from below and inlining it for speed:
and removing all further isset(#render_children) from below.
It is crucial that it returns that early.
Comment #46
Fabianx CreditAttribution: Fabianx commentedClariying title to make task more clear.
Comment #47
Fabianx CreditAttribution: Fabianx commentedChanging title to the best title in this issue.
Comment #48
jenlamptonImplemented @Fabianx's suggestions from #45
Comment #50
jenlampton#48: drupal-fix_double_rendering_of_element_children-1920886-48.patch queued for re-testing.
Comment #52
Fabianx CreditAttribution: Fabianx commented#48: drupal-fix_double_rendering_of_element_children-1920886-48.patch queued for re-testing.
Comment #54
jenlamptonA few problems. Returning early is not an option when we are dealing with the
$page
array. I added an exception to prevent it from rendering & returning for the page, and I seem to have gotten my site back up :) Also, I was overdoing the removal ofisset('#render_children'
). Fixed now.Comment #56
thedavidmeister CreditAttribution: thedavidmeister commentedooh, this is interesting. Best of luck with this :)
Comment #57
Fabianx CreditAttribution: Fabianx commentedIts DrupalCon ...
Comment #58
star-szrThis might work a bit better - the main change is $element -> $elements.
Comment #59.0
(not verified) CreditAttribution: commentededit
Comment #60
markhalliwellAdding tags
Comment #61
star-szrThis comment doesn't make sense, we're actually doing the equivalent of drupal_render_children() inline. Thanks @Mark Carver on the Twig call.
Comment #64
lauriiiReroll
Comment #68
lauriiiLets see what the testbot says
Comment #70
rteijeiro CreditAttribution: rteijeiro commentedTrying to fix the broken test.
Comment #71
PolThe patch works successfully.
Without it I have thinks like the screenshot uploaded to this comment for the module: https://www.drupal.org/project/views_ef_fieldset
Can't wait to see this in.
Comment #72
Fabianx CreditAttribution: Fabianx as a volunteer commentedThat is unfortunately not the right logic.
Edit: After some sleep:
That is kinda the right logic as only #theme sets the #render_children property in core as only that needs to prevent recursion.
Theoretically this could also occur via #theme_wrappers though, so what about:
This should also remove the other cases of #render_children - unless #page needs it - which should be checked.
Bumping to critical as this completely breaks #post_render for e.g. views form_views_exposed_form_alter as found by @Pol.
There is no work-around possible, the #post_render is called twice and you cannot check that as you don't have a reference to the render array, so you cannot even know if it was rendered already.( Edit: Code can obviously check for '#render_children' presence themselves ... )
Comment #73
Fabianx CreditAttribution: Fabianx as a volunteer commentedDemoting back to Major, code can just check #render_children itself.
Really not optimal, but a work-around is possible.
Pushing to core committers for attention though if it should be release blocking ...
CNW, per #72.
Comment #74
joelpittetAdding tag for #73
Comment #75
xjmComment #79
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedClosing this as duplicate of #2346893: Duplicate AJAX wrapper around a file field, which has tests, etc.
Comment #80
xjmCorrecting historical tag.