Problem/Motivation
hook_preprocess_views_view_grouping() is never invoked for single-level grouping, regardless of the display type (page, block, etc.).
The root cause is in StylePluginBase::renderGroupingSets(). A conditional branch decides which theme hook to use for each grouping set:
- Branch A (
is_array($row) && isset($row['group'])): creates a#theme => views_view_groupingrender array. Only reached when the current set's rows are themselves nested grouping sets (i.e. with 2+ grouping levels, for non-leaf levels only). - Branch B (else): calls
renderRowGroup(), which uses the style's own theme (e.g.views_view_unformatted). Always taken for single-level grouping, and for the leaf level of any multi-level grouping.
Because views_view_grouping is never rendered in these cases, hook_preprocess_views_view_grouping() is never triggered.
With 2 grouping levels, the hook fires once (for level 0). Level-1 titles are accessible indirectly via $variables['content'][x]['#title'] within the level-0 hook call, but views_view_grouping is never properly invoked for the leaf level.
Steps to reproduce
- Create a view with any display (page or block).
- Configure the style (e.g. Unformatted list) with one grouping field.
- Implement
hook_preprocess_views_view_grouping()in a custom module. - The hook is never called.
Proposed resolution
Two changes are needed:
- In
StylePluginBase::renderGroupingSets(), when grouping is configured and branch B is taken (leaf rows), always build a#theme => views_view_groupingrender array instead of delegating directly torenderRowGroup().
Rows should be pre-rendered with the row plugin first, as currently done. - In
ViewsThemeHooks::preprocessViewsViewGrouping(), detect whether$variables['rows']contains nested grouping sets or already-rendered row render arrays:- nested sets → existing recursive
renderGroupingSets()call - pre-rendered rows → wrap them with
$style->themeFunctions()directly (no new public API needed)
- nested sets → existing recursive
A change record will be needed: for single-level grouping, the group title currently rendered by views_view_unformatted (inside an <h3>) moves to views_view_grouping (which currently outputs a bare {{ title }}).
Note: template_preprocess_views_view_grouping() is deprecated in Drupal 11.3.0 and removed in 12.0.0. The initial preprocess is now registered via the 'initial preprocess' key in hook_theme(), pointing to ViewsThemeHooks::preprocessViewsViewGrouping() — which is the method to update.
Remaining tasks
- [x] Write a patch
- [x] Write tests
- [ ] Review the MR
- [x] Write a change record : https://www.drupal.org/node/3592452
API changes
hook_preprocess_views_view_grouping() will now be invoked for all grouping levels, including single-level grouping and leaf levels of multi-level grouping.
BC breaks
For single-level grouping, the HTML output changes: the group title moves from views_view_unformatted to views_view_grouping. Sites overriding views-view-grouping.html.twig or implementing hook_preprocess_views_view_grouping() may need to update accordingly.
Issue fork drupal-2950737
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
Comment #2
volegerAlso, I tried implement preprocess hooks for other views theme function, but they are worked.
Comment #3
volegerComment #11
volegerAny thoughts?
Comment #16
macsim commentedBumping this cause I am having the same problem with a block display on a drupal 11 instance. The hook is correctly invoked for a page display though.
Comment #17
macsim commentedOk after digging into the code, the issue is not specific to the block display -
hook_preprocess_views_view_grouping()is never invoked for single-level grouping, regardless of the display type (page, block, etc.).The culprit is
StylePluginBase::renderGroupingSets(). It contains a branch that decides which theme hook to use:With a single grouping level,
renderGrouping()produces sets whoserowsareResultRowobjects.reset()returns an object, sois_array($row)isFALSE→ branch B is taken →renderRowGroup()is called → the style's own theme (e.g.views_view_unformatted) is used instead ofviews_view_grouping. The grouping title ends up as#titleon the wrong render array, andhook_preprocess_views_view_grouping()is never triggered.With 2 grouping levels, branch A is reached for level 0 (its rows are the level-1 sets, which are arrays with a
'group'key), soviews_view_groupingis invoked once. However, the initial preprocess (preprocessViewsViewGrouping) immediately callsrenderGroupingSets()on the level-1 sets, which fall through to branch B — so the hook never fires a second time for level 1. Level-1 titles are still accessible indirectly via$variables['content'][x]['#title']within the level-0 hook call, butviews_view_groupingis never properly invoked for the leaf level.This matches my use case: my page display has 2 grouping levels while my block only has one
Comment #18
macsim commentedComment #19
macsim commentedComment #21
macsim commentedTested on an unformatted list view with both a single grouping level and 2 grouping levels - works perfectly
Please review the MR and if someone could also test it on a table-style view it would also be great.
Comment #22
macsim commented