You are here

1 无法在当前主题下面覆写breadcrumb.tpl.php

admin 的头像
Submitted by admin on 星期五, 2015-09-18 02:10

作者:老葛,北京亚艾元软件有限责任公司,http://www.yaiyuan.com

如果,我们提供了breadcrumb.tpl.php,但是却无法在当前主题下面覆写的话,这个模板文件的作用就大打折扣了。从发现这个问题以后,我就多次尝试去解决它。首先是借助于Google,有什么技术问题,先问Google。这是我在Google上使用的搜索词“hook_theme_registry_alter template file could not be override”

很幸运,有人遇到了同样的问题http://drupal.org/node/1424048,不幸的是,没有解决办法,如果你现在浏览这个问题的话,你会发现下面已经给出了解决办法,这个解决办法就是老葛给出来的。

我找到了一个类似的:

http://www.metachunk.com/blog/adding-module-path-drupal-7-theme-registry

在这篇文章里面,作者解决一个类似的问题,和我这里解决的问题还不完全一样,但是却为我们解决问题指明了方向。它是想在自己的模块目录下面,为模板文件提供一个模板建议,比如block--module--delta.tpl.php。这篇文章在解决这个问题的时候,也是花费了很大功夫的。它是这样解决的:

/**

 * Implements hook_theme_registry_alter()

**/

function mymodule_theme_registry_alter(&$theme_registry) {

  $mod_path = drupal_get_path('module', 'mymodule');

  $theme_registry_copy = $theme_registry;       // munge on a copy

  _theme_process_registry($theme_registry_copy, 'phptemplate', 'theme_engine', 'pow', $mod_path);

  $theme_registry += array_diff_key($theme_registry_copy, $theme_registry);

  $hooks = array('node');

  foreach ($hooks as $h) {

    _mymodule_insert_after_first_element($theme_registry[$h]['theme paths'], $mod_path);

  }

}

 

/**

 * Helper function for re-ordering arrays (needed by theme_registry_alter)

*/

function _mymodule_insert_after_first_element(&$a, $element) {

  if(is_array($a)) {

    $first_element = array_shift($a);

    array_unshift($a, $first_element, $element);

  }

}

hook_theme_registry_alter调用_theme_process_registry这种方式,让我眼前一亮,我的直觉告诉我,这种方式也能够解决我们的问题。开始的时候,我并没有看到它的代码的具体含义,但是我却做了这样的一件事情,认真的阅读了_theme_process_registry这个方法,以及_theme_build_registry,这两个函数里面的代码我读了很多遍,每个函数都认真的读过3遍以上的,弄清楚了函数里面每行代码的作用。我们来看一下_theme_build_registry的代码:

function _theme_build_registry($theme, $base_theme, $theme_engine) {

  $cache = array();

  // First, process the theme hooks advertised by modules. This will

  // serve as the basic registry. Since the list of enabled modules is the same

  // regardless of the theme used, this is cached in its own entry to save

  // building it for every theme.

  if ($cached = cache_get('theme_registry:build:modules')) {

    $cache = $cached->data;

  }

  else {

    foreach (module_implements('theme') as $module) {

      _theme_process_registry($cache, $module, 'module', $module, drupal_get_path('module', $module));

    }

    // Only cache this registry if all modules are loaded.

    if (module_load_all(NULL)) {

      cache_set('theme_registry:build:modules', $cache);

    }

  }

 

  // Process each base theme.

  foreach ($base_theme as $base) {

    // If the base theme uses a theme engine, process its hooks.

    $base_path = dirname($base->filename);

    if ($theme_engine) {

      _theme_process_registry($cache, $theme_engine, 'base_theme_engine', $base->name, $base_path);

    }

    _theme_process_registry($cache, $base->name, 'base_theme', $base->name, $base_path);

  }

 

  // And then the same thing, but for the theme.

  if ($theme_engine) {

    _theme_process_registry($cache, $theme_engine, 'theme_engine', $theme->name, dirname($theme->filename));

  }

 

  // Finally, hooks provided by the theme itself.

  _theme_process_registry($cache, $theme->name, 'theme', $theme->name, dirname($theme->filename));

 

  // Let modules alter the registry.

  drupal_alter('theme_registry', $cache);

 

  // Optimize the registry to not have empty arrays for functions.

  foreach ($cache as $hook => $info) {

    foreach (array('preprocess functions', 'process functions') as $phase) {

      if (empty($info[$phase])) {

        unset($cache[$hook][$phase]);

      }

    }

  }

  return $cache;

}

这是用来构建主题的注册表的,大致的流程如下:

图片1.png

 

    而注册的具体操作,则是委托给了_theme_process_registry。走到这里的时候,我做了一个非常大胆的尝试,我修改了这里的流程,这是我修改后的流程:

图片2.png

        这是我修改后的代码:

function _theme_build_registry($theme, $base_theme, $theme_engine) {

  $cache = array();

  // First, process the theme hooks advertised by modules. This will

  // serve as the basic registry. Since the list of enabled modules is the same

  // regardless of the theme used, this is cached in its own entry to save

  // building it for every theme.

  if ($cached = cache_get('theme_registry:build:modules')) {

    $cache = $cached->data;

  }

  else {

    foreach (module_implements('theme') as $module) {

      _theme_process_registry($cache, $module, 'module', $module, drupal_get_path('module', $module));

    }

    // Only cache this registry if all modules are loaded.

    if (module_load_all(NULL)) {

      cache_set('theme_registry:build:modules', $cache);

    }

  }

  // Let modules alter the registry.

  drupal_alter('theme_registry', $cache);

  // Process each base theme.

  foreach ($base_theme as $base) {

    // If the base theme uses a theme engine, process its hooks.

    $base_path = dirname($base->filename);

    if ($theme_engine) {

      _theme_process_registry($cache, $theme_engine, 'base_theme_engine', $base->name, $base_path);

    }

    _theme_process_registry($cache, $base->name, 'base_theme', $base->name, $base_path);

  }

 

  // And then the same thing, but for the theme.

  if ($theme_engine) {

    _theme_process_registry($cache, $theme_engine, 'theme_engine', $theme->name, dirname($theme->filename));

  }

 

  // Finally, hooks provided by the theme itself.

  _theme_process_registry($cache, $theme->name, 'theme', $theme->name, dirname($theme->filename));

 

  // Optimize the registry to not have empty arrays for functions.

  foreach ($cache as $hook => $info) {

    foreach (array('preprocess functions', 'process functions') as $phase) {

      if (empty($info[$phase])) {

        unset($cache[$hook][$phase]);

      }

    }

  }

  return $cache;

}

然后,我清除缓存,测试,竟然可以了。我竟然通过这种方式把问题解决掉了。这个时候,我认识到了一个问题,基主题的主题函数/模板文件注册这个流程前面,应该放置drupal_alter('theme_registry', $cache);这段代码,或者放置一段这样的代码:

drupal_alter('theme', $cache);

为什么这么说呢? 我们知道,主题引擎、主题,都是位于模块的上层的,虽然在Drupal7里面,模块可以使用hook_theme,主题引擎、主题也可以使用hook_theme。但是由于主题本身位于最上层,所以模板是没有必要通过hook_theme_registry_alter修改主题里面提供的特有的主题函数/模板文件,如果想要修改主题里面的,只需要在主题层直接修改就是了。而主题层的_theme_process_registry,还包含了对模块层已有主题函数/模板文件的覆写功能,这也是包含了的。

在此之前,我还做过这样的调试:

function breadcrumb2_theme_registry_alter(&$theme_registry) {

 //print debug($theme_registry['breadcrumb']);

  if (isset($theme_registry['breadcrumb'])) {

    $path = drupal_get_path('module', 'breadcrumb2');

//$path = path_to_theme();

    $theme_registry['breadcrumb']['path'] = $path;

$theme_registry['breadcrumb']['theme path'] = $path;

    $theme_registry['breadcrumb']['template'] = 'breadcrumb';

    $theme_registry['breadcrumb']['function'] = NULL;

unset($theme_registry['breadcrumb']['function']);

  }

  // print debug($theme_registry['breadcrumb']);

  //print debug($theme_registry['node']);

print debug($theme_registry);

}

就是直接将$theme_registry的结构在前台输出出来,然后观察里面每一元素的结构,同时重点研究了($theme_registry['node']($theme_registry['page']($theme_registry[' breadcrumb ']的输出。这是没有修改核心代码时的输出:

array (

  'variables' => 

  array (

    'breadcrumb' => NULL,

  ),

  'type' => 'module',

  'theme path' => 'sites/all/modules/breadcrumb2',

  'preprocess functions' => 

  array (

    0 => 'template_preprocess_breadcrumb',

  ),

  'process functions' => 

  array (

  ),

  'path' => 'sites/all/modules/breadcrumb2',

  'template' => 'breadcrumb',

)

Debug: 

array (

  'template' => 'page',

  'path' => 'themes/seven',

  'type' => 'theme_engine',

  'theme path' => 'themes/seven',

  'render element' => 'page',

  'preprocess functions' => 

  array (

    0 => 'template_preprocess',

    1 => 'template_preprocess_page',

    2 => 'contextual_preprocess',

    3 => 'overlay_preprocess_page',

    4 => 'shortcut_preprocess_page',

    5 => 'seven_preprocess_page',

  ),

  'process functions' => 

  array (

    0 => 'template_process',

    1 => 'template_process_page',

    2 => 'rdf_process',

  ),

)

我们看到,这里的数组,里面包含的信息远比hook_theme里面包含的多,而对于$theme_registry['page'],从它里面的结构可以看出,已经被当前主题seven给覆写了。


Drupal版本: