官术网_书友最值得收藏!

  • Drupal 9 Module Development
  • Daniel Sipos Antonio De Marco
  • 1759字
  • 2021-06-11 18:36:05

Theme hooks

Since we have covered some of the principles behind the Drupal theme system—most notably, the separation of concerns—let's go a bit deeper and take a look at how they are actually put into practice. This all starts with the theme hooks. Yes, Drupal loves to call things hooks.

Theme hooks define how a specific piece of data should be rendered. They are registered with the theme system by modules (and themes) using hook_theme(). In doing so, they get a name, a list of variables they output (the data that needs to be wrapped with markup), and other options.

The modules and themes that register theme hooks also need to provide an implementation (one that will be used by default).

As an example, let's take a look at two common ways of registering a theme hook that we'll often find. For this, we will use Drupal core examples that already exist:

function hook_theme($existing, $type, $theme, $path) {

  return [

    'item_list' => array(

      'variables' => array('items' => array(), 'title' =>        '', 'list_type' => 'ul', 'wrapper_attributes' =>        array(), 'attributes' => array(), 'empty' => NULL,        'context' => array()),

    ),

    'select' => array(

      'render element' => 'element',

    ),

  ];

}

In the above hook_theme() example, I included two theme hooks from Drupal core. One is based on variables, whereas the other is based on a render element. There are, of course, many more options that can be defined here, and I strongly encourage you to read the Drupal.org API documentation page for this hook.

However, right off the bat you can see how easy it is to register a theme hook. In the first case, we have item_list, which, by default (if not otherwise specified), will map to the item-list.html.twig file for outputting the variables. In its definition we find the variables it uses, with some handy defaults in case they are not passed in from the client. The second theme hook is select, which doesn't use variables but a render element (which we will discuss soon). Also, its template file is easy to determine based on the name: select.html.twig. I encourage you to check out both of these template files in the core code (provided by the System module).

In addition to the actual implementation, the modules and themes that register a theme hook can also provide a default template preprocessor. The responsibility of this is to "preprocess" (that is, prepare) data before being sent to the template. For example, if a theme hook receives an entity (a complex data object) as its only variable, a preprocessor can be used to break that entity into tiny pieces that are needed to be output in the template (such as title and description).

Template preprocessors are simple procedural functions that follow a naming pattern and are called by the theme system before the template is rendered. As I mentioned earlier, the modules and themes that register a theme hook can also provide a default preprocessor. So, for a theme hook named component_box, the default preprocessor function would look like this:

function template_preprocess_component_box(&$variables) {

  // Prepare variables.

}

The function name starts with the word template to denote that it is the original preprocessor for this theme hook, then follows the conventional preprocess word, and ends with the name of the theme hook. The argument is always an array passed as a reference and contains some info regarding that theme hook, and more importantly, the data variables that were defined with the theme hook and passed to it from the calling code. That is what we are usually working with in this function. Also, since it's passed by a reference, we don't return anything in this function, but we always manipulate the values directly in the $variables array. In the end, the template file can print out variables named after the keys in this array. The values will be, of course, the values that map to those keys.

Another module (or theme) can override this preprocessor function by implementing its own. However, in its naming, it needs to replace the word template with the module name (to avoid collisions). If one such override exists, both preprocessors will be called in a specific order. The first is always the default one, followed by the ones defined by modules, and then the ones defined by themes. This is another great extension point of Drupal because altering data or options found inside the preprocessor can go a long way in customizing the existing functionality to your liking.

As an alternative to following the previous naming convention, you also have the option to specify the preprocessor function names in the hook_theme() definition when you register it. However, I recommend that you stick to the default naming convention because it's much easier to spot what the purpose of the function is. As you become more advanced, you'll, in turn, appreciate being able to quickly understand these convention functions at a quick glance.

I mentioned a bit earlier that modules and themes can also override theme hooks defined by other modules and themes. There are two ways to do this.

The most common one is for a theme to override the theme hook. This is because of the rationale I was talking about earlier—a module defines a default implementation for its data, but a theme can then take over its presentation with ease. Also, the way themes can override a theme hook is by simply creating a new Twig file with the same name as the original and placing it somewhere in its templates folder. If that theme is enabled, it will be used instead. A less common but definitely valid use case is for a module to override a theme hook defined by another module. For example, this might be because you need to change how data is rendered by a popular contributed module. To achieve this, you will need to implement hook_theme_registry_alter() and change the template file used by the existing theme hook. It's also worth adding that you can change the entire theme hook definition using this hook if you want, not just the template. Moreover, since we mentioned this hook, note that theme hooks, upon definition, are stored and cached in a theme registry for optimized performance, and that registry is what we are altering with this hook. This also means that we regularly need to clear the cache when we make changes to the theme registry.

All this is good and fine, but the business logic still has to interact with the theme system to tell it which particular theme hook to use. And we do so by using something called render arrays, which contain the theme hook information, the variables, and any other metadata on how that component needs to be rendered. We will also talk about render arrays in this chapter.

Theme hook suggestions

A great thing about theme hooks is that they are reusable. However, one problem you'll encounter is that theme hook templates lose context when a theme hook is reused. For example, the item_list theme hook, whose definition we saw in the previous section, has no idea what list it is theming. And this makes it difficult to style differently depending on what that content is. Fortunately, we can provide context to the theme system by using a theme hook pattern instead of the original theme hook name, and this pattern looks something like this:

base_theme_hook__some_context

The parts of the pattern are separated with a double underscore and together they are called a theme hook suggestion. But how does this work?

Client code (the render arrays, as we will soon see), when using a theme hook to render a piece of data, can append the context to the theme hook, turning it into a suggestion. The theme system will then check for the following:

  • If there is a template file that matches that suggestion (inside a theme), it uses it instead of the original theme hook template.
  • Alternatively, if there is a theme hook registered that has that actual name, it uses that instead.
  • Alternatively, it checks for the base theme hook and uses that instead (the fallback).

In this case, the caller (the render array) is responsible for "proposing" a suggestion. For example, consider the following render array:

return [

  '#theme' => 'item_list__my_list',

  '#items' => $items,

];

The base theme hook is item_list, which is rendered using the item-list.html.twig template file provided by Drupal core. If there is no item-list--my-list.html.twig template file in the theme, and there is no item_list__my_list theme hook registered, the default item_list theme hook will be used. Otherwise, we will follow the order that I mentioned before. A module can register that suggestion as a hook, which will be used instead. However, a theme can override that further by just creating the template file with that name.

And all this is done so that when rendering something with a reusable theme hook, we give the possibility to determine what exactly is being themed. However, the example we saw just now is static in the sense that we hardcoded my_list as the theme hook suggestion. We can do better than that.

A module that registers a theme hook can also provide a list of suggestions that should go with that theme hook automatically. It does so by implementing hook_theme_suggestions_HOOK(), where HOOK is the theme hook name. This hook is fired at runtime by the theme system, trying to determine how a certain render array needs to be rendered. It receives the same $variables array as an argument as the template preprocessors do. This means that we can make use of those variables and dynamically provide theme hook suggestions. We will see an example of this later in the chapter.

Moreover, as module developers, we can also provide a list of theme hook suggestions to theme hooks registered by other modules or Drupal core. We can do so by implementing hook_theme_suggestions_HOOK_alter(), where we receive the available suggestions for that theme hook in addition to the variables.

In summary, theme hook suggestions are a powerful way of adding some context to the generic theme hooks that are responsible for rendering multiple things.

主站蜘蛛池模板: 招远市| 邹平县| 陇西县| 肇东市| 康马县| 三河市| 临颍县| 新乐市| 湘潭市| 名山县| 买车| 新野县| 长汀县| 左贡县| 鹿泉市| 屏边| 吐鲁番市| 通化市| 武穴市| 当阳市| 桂林市| 昭平县| 莱芜市| 南陵县| 高邮市| 安达市| 樟树市| 绿春县| 光山县| 泰和县| 公主岭市| 四子王旗| 汉沽区| 玉林市| 长宁区| 东丰县| 乡城县| 邳州市| 区。| 徐闻县| 黄骅市|