跳转到主要内容
nosuchplace 提交于 10 January 2017

Drupal 8的Twig文件中一般会在注释中说明传入的变量,比如某个主题的field.html.twig:

<?php
/ *
 * Available variables:
 * - attributes: HTML attributes for the containing element.
 * - label_hidden: Whether to show the field label or not.
 * - title_attributes: HTML attributes for the title.
 * - label: The label for the field.
 * - multiple: TRUE if a field can contain multiple items.
 * - items: List of all the field items. Each item contains:
 *   - attributes: List of HTML attributes for each item.
 *   - content: The field item's content.
 * - entity_type: The entity type to which the field belongs.
 * - field_name: The name of the field.
 * - field_type: The type of the field.
 * - label_display: The display settings for the label.
 *
 * @see template_preprocess_field()
 *
 * @ingroup themeable
 */
?>

但是可以看到很多twig里面都有user这个变量(它是个Drupal\Core\Session\AccountProxy对象)。这个变量给我们带来很多方便,比如可以直接在Twig里根据当前用户或者所属组(它有getRoles()方法)实现一些逻辑。

问题在于它不出现在twig文件注释中的变量说明里,这就让人很不放心了,担心它是一个undocumented特性,使用它以后会带来问题。

那这个变量是在哪里设置的呢?按道理应该在某个预处理函数里。根据Drupal 8 API网站上的Theme system overview

Preprocessing for Template Files

If the theme implementation is a template file, several functions are called before the template file is invoked to modify the variables that are passed to the template. These make up the "preprocessing" phase, and are executed (if they exist), in the following order (note that in the following list, HOOK indicates the hook being called or a less specific hook. For example, if '#theme' => 'node__article' is called, hook is node__article and node. MODULE indicates a module name, THEME indicates a theme name, and ENGINE indicates a theme engine name). Modules, themes, and theme engines can provide these functions to modify how the data is preprocessed, before it is passed to the theme template:

  • template_preprocess(&$variables, $hook): Creates a default set of variables for all theme hooks with template implementations. Provided by Drupal Core.
  • template_preprocess_HOOK(&$variables): Should be implemented by the module that registers the theme hook, to set up default variables.
  • MODULE_preprocess(&$variables, $hook): hook_preprocess() is invoked on all implementing modules.
  • MODULE_preprocess_HOOK(&$variables): hook_preprocess_HOOK() is invoked on all implementing modules, so that modules that didn't define the theme hook can alter the variables.
  • ENGINE_engine_preprocess(&$variables, $hook): Allows the theme engine to set necessary variables for all theme hooks with template implementations.
  • ENGINE_engine_preprocess_HOOK(&$variables): Allows the theme engine to set necessary variables for the particular theme hook.
  • THEME_preprocess(&$variables, $hook): Allows the theme to set necessary variables for all theme hooks with template implementations.
  • THEME_preprocess_HOOK(&$variables): Allows the theme to set necessary variables specific to the particular theme hook.

先简单搜索了一番,比如针对我知道肯定有user变量的的field.html.twig,在core目录下执行:

find . -type f -exec grep -Hn preprocess_field {} \;

只找到了一个field的预处理函数:

./includes/theme.inc:1526:function template_preprocess_field(&$variables, $hook) {

看源代码,它里面设置了一些变量,比如:


<?php
  // Creating variables for the template.
  $variables['entity_type'] = $element['#entity_type'];
  $variables['field_name'] = $element['#field_name'];
  $variables['field_type'] = $element['#field_type'];
  $variables['label_display'] = $element['#label_display'];
?>

这些正是我们在twig文件的注释里看到的那些。但没有设置user变量。没找到可能是好事,说明这个user变量不是针对某个theme的,而是一视同仁的。

那我们就从上面那个文档中提到的第一个预处理函数template_preprocess()看起吧。果然好像有戏:


<?php
  $default_variables = &$drupal_static_fast['default_variables'];
  if (!isset($default_variables)) {
    $default_variables = _template_preprocess_default_variables();
  }
  $variables += $default_variables;
?>

查一下_template_preprocess_default_variables()函数:

<?php
function _template_preprocess_default_variables() {
  // Variables that don't depend on a database connection.
  $variables = array(
    'attributes' => array(),
    'title_attributes' => array(),
    'content_attributes' => array(),
    'title_prefix' => array(),
    'title_suffix' => array(),
    'db_is_active' => !defined('MAINTENANCE_MODE'),
    'is_admin' => FALSE,
    'logged_in' => FALSE,
  );

  // Give modules a chance to alter the default template variables.
  \Drupal::moduleHandler()->alter('template_preprocess_default_variables', $variables);

  // Tell all templates where they are located.
  $variables['directory'] = \Drupal::theme()->getActiveTheme()->getPath();

  return $variables;
}
?>

哦,这里又设置了一堆变量,在某些情况下应该还挺好使。可以看到后来又调了一个alter型的hook,搜索:

find . -type f -exec grep -Hn _template_preprocess_default_variables_alter {} \;

结果只有user模块实现了这个hook,听这模板的名字就很让人欣慰:

./modules/user/user.module:430: * Implements hook_template_preprocess_default_variables_alter().
./modules/user/user.module:435:function user_template_preprocess_default_variables_alter(&$variables) {

看看:

<?php
function user_template_preprocess_default_variables_alter(&$variables) {
  $user = \Drupal::currentUser();

  $variables['user'] = clone $user;
  // Remove password and session IDs, since themes should not need nor see them.
  unset($variables['user']->pass, $variables['user']->sid, $variables['user']->ssid);

  $variables['is_admin'] = $user->hasPermission('access administration pages');
  $variables['logged_in'] = $user->isAuthenticated();
}
?>

终于找到了。还很细心地帮我们把不该给大家看的东西去掉了。

这下我们可以放心了,只要user模块在,twig里就不愁没有user变量用!

Drupal 版本