hook_preprocess_views_view_grouping() was only invoked for nested (multi-level) grouping. For single-level grouping, StylePluginBase::renderGroupingSets() bypassed the views_view_grouping theme hook and delegated directly to renderRowGroup(), which uses the style's own theme (e.g. views_view_unformatted). The preprocess hook was therefore never triggered.
Two changes have been made to fix this:
-
StylePluginBase::renderGroupingSets()At the leaf level (rows are individual result rows, not nested groups), when grouping is configured, the method now builds a
#theme => views_view_groupingrender array instead of callingrenderRowGroup()directly. This ensureshook_preprocess_views_view_grouping()is invoked at every grouping level.Before:
// Leaf rows always went through renderRowGroup(), skipping views_view_grouping. $single_output = $this->renderRowGroup($set['rows']);After:
if (!empty($this->options['grouping'])) { $single_output = [ '#theme' => $theme_functions, '#view' => $this->view, '#grouping' => $this->options['grouping'][$level] ?? [], '#rows' => $set['rows'], ]; } else { $single_output = $this->renderRowGroup($set['rows']); } -
ViewsThemeHooks::preprocessViewsViewGrouping()The preprocess function now detects whether
$variables['rows']contains nested grouping sets (arrays with a'group'key) or already-rendered rows (leaf level), and handles each case:- Non-leaf level: calls
renderGroupingSets()recursively, as before. - Leaf level (including single-level grouping): wraps the rows in the style's own theme render array.
$variables['content']is now always an indexed array of render arrays at every level.Additionally,
$variables['title']is now wrapped in aMarkupobject to prevent Twig from double-escaping the already-HTML-escaped rendered field value used as the group title. - Non-leaf level: calls
Impact on site builders and module developers
This change affects you if you have:
- A custom
views-view-grouping.html.twigtemplate — it will now also be invoked for single-level grouping, where it was previously skipped. Review your template to ensure it handles both leaf and non-leaf levels correctly. - A
hook_preprocess_views_view_grouping()implementation — it will now fire for all grouping levels. The$variables['content']variable is always an indexed array of render arrays; if your hook assumed a different structure for single-level grouping, update it accordingly. - A
hook_preprocess_views_view_grouping()implementation that outputs$variables['title']— it is now aMarkupobject; do not re-escape it.