The docs state that theme hooks are "responsible" for rendering child elements in render arrays.
// Call the element's #theme function if it is set. Then any children of the
// element have to be rendered there. If the internal #render_children
// property is set, do not call the #theme function to prevent infinite
// recursion.
In some cases, the hooks kind of "cheat" by calling drupal_render() in their preprocess functions, hence the need for #render_children in drupal_render(), but at least the job gets done.
More worryingly, however, there are theme functions and templates that do *not* render the child elements at all, but *do* invent their own attributes that look suspiciously like awkward placeholders for where the rendered children should go.
Take #theme 'maintenance_page' for example, it has two variables defined in the theme registry, 'content' and 'show_messages'.
This means that if we build a renderable array like this:
$maintenance_page = array(
'#theme' => 'maintenance_page',
'#content' => 'foo',
'child' => array('#markup' => 'bar'),
);
print $maintenance_page;
We get back a maintenance page with the "content" of foo, but bar is not rendered. So, two concerns here:
- Why have we invented #content when the concept of "child elements" already exists in the render API?
- Why does this theme hook not handle rendering children, something the docs explicitly state theme hooks that are not #theme_wrappers are required to do?
There are *lots* of theme hooks in core that define a "content" variable or equivalent. I'm sure this is for historical reasons, but it now appears to be at best very "crufty" and at worse a bug.
To make matters worse, the way that many theme hooks are implemented actually makes it impossible for them to "be responsible for" rendering child elements as theme() strips the child elements out of the $variables array that is passed to the preprocess and theme function/templates unless 'render element' is set:
// If a renderable array is passed as $variables, then set $variables to
// the arguments expected by the theme function.
if (isset($variables['#theme']) || isset($variables['#theme_wrappers'])) {
$element = $variables;
$variables = array();
if (isset($info['variables'])) {
foreach (array_keys($info['variables']) as $name) {
if (isset($element["#$name"]) || array_key_exists("#$name", $element)) {
$variables[$name] = $element["#$name"];
}
}
}
else {
$variables[$info['render element']] = $element;
// Give a hint to render engines to prevent infinite recursion.
$variables[$info['render element']]['#render_children'] = TRUE;
}
}
Comments
Comment #1
Eric_A CreditAttribution: Eric_A commentedThe whole inline comment is about theme hooks that handle a render element as their only argument. Not about hooks that handle variables.
theme_maintenance()
handles variables, not a render element.Every theme hook just handles what it is designed to handle. Hooks that handle elements can handle child elements. Hooks that handle variables handle the variables they are designed to handle. Which can never be an element child. Because these hooks do not handle elements.
The theme() function transforms a render element to a variables array for hooks that handle variables by mapping element properties to variables. That's all. It cannot map a child element to a variable.
Comment #2
thedavidmeister CreditAttribution: thedavidmeister commented#1 - Yes, that seems like a fairly accurate assessment of the situation, but I can't tell if you're describing the nature of the bug further, or trying to explain that somehow this behaviour is a "good thing" or "working as designed".
Comment #3
thedavidmeister CreditAttribution: thedavidmeister commentedThe comment doesn't make this clear at all. Nor is there a clear explanation on how to support nested data structures in the render API where child elements are compatible with theme hooks that use variables instead of 'render element'.
Comment #4
sunComment #5
thedavidmeister CreditAttribution: thedavidmeister commentedSo, would a potential way forward here to merge the concept of
#theme_wrappers
and#theme
into a single thing?The proposal would be:
- One attribute
#theme
that behaves as#theme_wrappers
currently does- Remove any variables that currently exist in theme functions/templates for the sole purpose of working around the fact that children cannot be rendered, and replace them with the functionality that already exists for
#theme_wrappers
, i.e. concatenate/print$element['#children']
It's obvious to me what we gain by merging the concepts: A unified set of rules and expectations for every theme hook
It's not obvious what we lose by merging the concepts (other than a bit of time re-jigging some templates and documenting the API change), so I'd love to hear what other people think.
Comment #8
joelpittetI'd love to discuss this idea further, a snippet of one conversion would be great as a discussion point. Rendering the children in the template without them being flattened earlier than {{ children }} would be ideal or even
But the ship may have sailed for this in 8.x unless there is some additions we can make to make this easier a template?
Let's revisit in 9.x unless there is a proposal for BC in 8.x? Likely won't convert existing render element for sure but help new elements.
Comment #9
catchLet's keep this against 8.x until we've exhausted the potential for a bc layer.
Comment #23
darvanenThis ticket came up as a triage target for the Bug Smash Initiative (find us at #bugsmash on Drupal Slack).
I discussed with @dww. Following the (closed) parent issue link: #2004872: [meta] Theme system architecture changes yields a new direction:
So we think this one might be outdated.
I'm going to mark this PMNMI to give time for feedback. If there is no further activity for three months this ticket may be closed as outdated.
Comment #24
geek-merlin@darvanen #23: Thanks for re-triaging this and cross-linking the other issues!
The cross-linked issues are about the wide area of OO-modernizing and pluginifying the theme system, but unfortunately look quite stale with relevant discussion 5 years old.
This issue otoh is clearly about handling render array childen, which is orthogonal to the other issues, and as much a point as when it was created..
So no, imho not outdated nor lacking info.