Updated: Comment #N

Problem/Motivation

In D7 you had access to $variables['theme_hook_suggestions'] in preprocess functions. This was good because you could see the available theme hook suggestions, but you could also change the current suggestion at any point during the preprocess stage by altering this variable and/or $variables['theme_hook_suggestion']. This ability to change the theme hook suggestions via preprocess made checking the value of these variables in a preprocess function unreliable if you wanted to perform specific alterations for a certain theme hook suggestion or set of theme hook suggestions.

In Drupal 8 we've removed 'theme_hook_suggestions' and 'theme_hook_suggestion' from preprocess. Altering the theme hook suggestions happens in the theme suggestion stage before any preprocess hooks are invoked - see #1751194: Introduce hook_theme_suggestions[_HOOK]() and hook_theme_suggestions[_HOOK]_alter() and its change record.

We still want to find a way to solve the use case brought up in #939462: Specific preprocess functions for theme hook suggestions are not invoked and pave the way for template consolidation (#1804614: [meta] Consolidate theme functions and properly use theme suggestions in core).

Proposed resolution

Heavily inspired by @effulgentsia's post at #939462-6: Specific preprocess functions for theme hook suggestions are not invoked

Pass the complete array of theme hook suggestions to preprocess functions so that variable preprocessing can be done conditionally based on theme suggestions. Also, when an array of hooks is given, treat them as suggestions and pass them through accordingly in _theme().

Now that suggestions are immutable during the preprocess stage, we can check the second parameter passed to preprocess functions, $hook, to see which template is being used. What preprocess is missing is the complete array of theme hook suggestions to cover more use cases.

My suggestion at this time is to add this array to the $info array (the third parameter that is passed to preprocess functions). To my knowledge $info is currently made up of information coming from the theme registry and we would be adding non-theme registry data to this array, but I find this preferable to adding a fourth parameter to preprocess functions for this use case and it maintains backwards compatibility by not changing the function signature of preprocess.

See #1885714-16: Remove theme_install_page() for a basic proof of concept of how this could work, or read on for a more detailed explanation.

Preprocess functions can examine the theme hook suggestion data and have complete flexibility as to how they want to act. When working with theme suggestions these are the 2 most prominent ways that this data would likely be used (note that either of these can be used by both modules and themes):

1. Logic based on a suggestion actually being used (e.g. node--article.html.twig template exists in the currently active theme).

function MYTHEME_preprocess_node(&$variables, $hook) {
  if ($hook == 'node__article') {
    $variables['article_template'] = TRUE;
  }
}

2. Logic based on a suggestion simply being in the list of $suggestions, whether or not node--article.html.twig exists within the theme or not.

function MYTHEME_preprocess_node(&$variables, $hook, $info) {
  // We would add ‘theme suggestions’ to the $info array in theme().
  if (in_array('node__article', $info['suggestions'])) {
    $variables['article_suggestion'] = TRUE;
  }
}

If there is a large amount of preprocessing needed for a certain theme suggestion, that logic can always be moved out into a "helper" function and this function can even be named after the theme hook suggestion but it will not be invoked automatically. For example:

function MYTHEME_preprocess_node(&$variables, $hook, $info) {
  // We would add ‘theme suggestions’ to the $info array in theme().
  if (in_array('node__article', $info['suggestions'])) {
    MYTHEME_preprocess_node__article($variables);
  }
}

function MYTHEME_preprocess_node__article(&$variables) {
  ...
}

Remaining tasks

  • Write patch
  • Write tests
  • Review patch

User interface changes

n/a

API changes

An API addition of being able to access the full suggestion data in preprocess functions.

Files: 
CommentFileSizeAuthor
#4 2201781-4.patch1.3 KBCottser
FAILED: [[SimpleTest]]: [PHP 5.4 MySQL] 64,710 pass(es), 2 fail(s), and 0 exception(s). View

Comments

Cottser’s picture

Issue summary: View changes

Adding a note and link to @effulgentsia's great write-up at #939462-6: Specific preprocess functions for theme hook suggestions are not invoked to the issue summary.

Cottser’s picture

Issue summary: View changes

Boldifying and shifting a paragraph.

Cottser’s picture

Issue summary: View changes

Some more slight clarifications, I think I'm done futzing for tonight.

Cottser’s picture

Status: Active » Needs review
FileSize
1.3 KB
FAILED: [[SimpleTest]]: [PHP 5.4 MySQL] 64,710 pass(es), 2 fail(s), and 0 exception(s). View

Rough proof of concept just to put something up. twig_debug is a bit broken.

Status: Needs review » Needs work

The last submitted patch, 4: 2201781-4.patch, failed testing.

dawehner’s picture

Can we mark one of the two issues as duplicate? Postponed seems to be just wrong.

Cottser’s picture

Cottser’s picture

Hoping to get back to this (mostly just test coverage) in the near future.

markcarver’s picture

Status: Needs work » Closed (duplicate)
Related issues: +#2223885: Detect standalone hook_[pre]process_THEME_HOOK__SUGGESTION implementations

Sorry @Cottser. I've finally been able to review all these issues. I think that a few things were overlooked or misunderstood.

I interpreted the most important part of @effulgentsia's comment as:

So, my recommendation is to mark this issue "won't fix" or kick it to D8, and to require the less attractive, but more robust, pattern to be used for suggestion-specific preprocessing in D7. Alternatively, a contrib module like ctools can implement hook_theme_registry_alter() to make suggestion-specific preprocess functions work in D7 (with all the limitations they have in D6, or maybe even fixing some of them).

But, that's my recommendation only. Please share your thoughts. If my arguments for why suggestion-specific preprocess functions are undesirable in D7 core aren't persuasive, and if the community really wants this D6->D7 regression fixed, I'll support a D7 core patch to fix it.

This suggests that "this is indeed a bug and should be bumped to 8.x to get fixed, but here's a possible solution for 7.x."

Furthermore, @dvessel's review of his comment in #939462-21: Specific preprocess functions for theme hook suggestions are not invoked essentially disagreed that his approach is not acceptable (and I must also disagree with this solution). There have been many patches continuing the progression of that issue which has clearly indicated it's importance. Furthermore, the related issue I'm attaching now explains why using the base preprocess function and "check for theme suggestions" is not a good approach:

  1. Omitted because it's reasoning belongs to 7.x
  2. Duplicated code/functionality - For a simple theme, this may seem like a viable solution. However base themes, such as this one (Bootstrap), and complex [sub-]themes that implement many, many theme hook suggestions throughout their code so theme hooks can be targeted for easier themeing are required to implement this "detection" in each and every single base hook [pre]process function. This is absolutely ridiculous and a nightmare to maintain in the long run.

I'm marking this as a duplicate and returning focus to #2035055: Introduce hook_theme_prepare[_alter]() and remove hook_preprocess_HOOK() (to include adding suggestions to the $info array, for context).

forestmars’s picture

Issue summary: View changes