分享

从头学习Drupal--基本架构三 | Drupal China

 gaofrank 2008-10-17

从头学习Drupal--基本架构三

xeopn 于 周六, 2008-06-21 20:38 提交。

菜单(Menu)
前面曾经简单提到过Drupal的菜单, 今天稍微深入来探讨一下. 菜单能用来显示导航信息, 我们安装的系统, 默认安装有3个菜单, 让我们查看一下数据库吧, 以menu_开头的总共有三张表: menu_custom, menu_links, menu_router. 其中menu_custom表存放菜单定义信息, 但想知道他们都是由哪个模块定义的麽? 别忘记了菜单如果要显示就是区块哦, 打开区块表(blocks)看看吧. Here it is! 用户模块(User Module)定义了Navigation菜单(没看数据库我以为是系统模块(System Module)定义的呢), 菜单模块(Menu Module)定义了Primary linksSecondary links两个空菜单. 所以从表现层来看, 一个菜单就对应一个区块(Block), 它被放置在页面的某个区域(Region)来显示给用户进行导航.
其实Drupal的菜单机制不仅要能把导航显示给用户, 更重要的是在用户点击这些导航的时候, 能够准确快速定位到相应的业务逻辑. 有人会问, 难道这也是个问题吗? 要知道导航其实都对应他们具体的URI, 而传统的URI的定位是先按目录结构找处理文件, 然后根据Request参数对应业务逻辑,同时还要在业务逻辑中判断用户权限; 而Drupal有一套自己的内部路径, 它是基于模块化构建的, 与目录结构一点关系也没有了, 所以必须要有一套机制能在URI和业务逻辑间进行映射, 而Drupal的菜单机制就是完成这项工作的, 用户点击菜单项链接时, Drupal解析出内部路径, 并根据内部路径找到对应的业务逻辑, 并再完成判断权限后转交给业务逻辑进行处理, 这个过程Drupal称之为分发.
Drupal核心框架中的菜单api(includes/menu.inc文件)实现了上述功能, 它成功地解决了动态URL路径到具体执行函数间映射, 对用户屏蔽了系统预定义的Request参数的复杂处理, 在路径和功能建立了必由之路. 啥也别说了, 看看分发函数的实现:

<?php
>
function
menu_execute_active_handler($path = NULL) {
  if (
_menu_site_is_offline()) {
    return
MENU_SITE_OFFLINE;
  }
  if (
variable_get(‘menu_rebuild_needed‘, FALSE)) {
   
menu_rebuild();
  }
  if (
$router_item = menu_get_item($path)) {
    if (
$router_item[‘access‘]) {
      if (
$router_item[‘file‘]) {
        require_once(
$router_item[‘file‘]);
      }
      return <
strong>call_user_func_array($router_item[‘page_callback‘], $router_item[‘page_arguments‘]);</strong>
    }
    else {
      return
MENU_ACCESS_DENIED;
    }
  }
  return
MENU_NOT_FOUND;
}
?>

用户URL请求到达后, Drupal先进行Bootstrap初始化, 然后调用分发函数menu_execute_active_handler, 该函数根据解析出的内部路径, 在系统构建出的菜单路由表中查找, 如果找到则判断可访问权限, 然后调用路由表中对应路径注册的Pange_callback回调函数, 这样就完成一个URL请求到具体页面逻辑的过程. 流程非常简单清晰, 学过计算机原理, 熟悉中断调用的对这流程应该都非常熟悉.

菜单路由(Menu Router)
Drupal系统主要依据menu_router表构建系统菜单路由, 而menu_router表的内容则是基于各模块的hook_menu钩子来获得, 这个钩子较少被调用, 一般都在模块初始化或其他菜单需要重建的情况. 下面我们选一段Book模块的menu钩子代码来看看:

<?php
function book_menu() {
 
$items = array();
 
$items[‘admin/content/book/%node‘] = array(              
   
‘title‘ => ‘Re-order book pages and change titles‘,
   
‘page callback‘ => ‘drupal_get_form‘,
   
‘page arguments‘ => array(‘book_admin_edit‘, 3),
   
‘access callback‘ => ‘_book_outline_access‘,
   
‘access arguments‘ => array(3),
   
‘type‘ => MENU_CALLBACK,
   
‘file‘ => ‘book.admin.inc‘,
  );
}
?>

book模块所定义的所有菜单路由表由一个二维数组表示, 其中每一项为一个菜单路由, 下标即为该路由的入口路径, 这里为‘admin/content/book/%node‘. 菜单路由项的各属性看命名已经比较清晰了, 想了解可以参考Drupal.
我们看上面用的路径中包含一段‘%node‘, 对了这是使用了通配符, 比如你访问admin/content/book/7的时候, 会自动把node7装载进来, 太多细节, 就此打住.

菜单项(Menu Item)
保存在menu _links表中, 定义了每个条目的名字, 条目间父子关系, 对应路径, 所属模块等等很多属性, 它应不像菜单路由项一样隐藏在背后干活, 它是可见的; 同时由于有通配符的存在, 它与菜单路由项并不是一一对应关系. (比较奇怪的是默认的Navigation菜单是用户模块创建的, 但它里面的所有菜单项却是系统模块定义的.) 既然router表的数据从钩子函数而来, 那link表是否也有对应的钩子呢, 实际上菜单项是由菜单模块(Menu Moudle)进行管理, 通过GUI界面直接配置, 当然菜单API也有对应接口,比如menu_link_save(). ( 实在不行你直接写数据库也行, 那不就是hack菜单模块了麽)

Drupal6.x增加了两个alter钩子函数对应这两张表, 它们是hook_menu_alter()<--->menu_router, hook_menu_link_alter()<--->menu_links, 它们主要处理内容变化时的处理逻辑, 由Drupal_alter()函数调用, 看代码注释说这个函数非常Ugly, 要在7中把它解决掉. 头晕了一天, 今天也没有心思再看下去了.

总结:
现在我们有点明白Drupal的菜单机制了吧, 它主要由菜单api和菜单模块组成, 提供一种框架, 使得其他功能模块能过注册菜单路由项, 并在分发过程中, 通过该菜单路由表完成用户页面请求(具体URL)到功能模块业务逻辑的映射. 当然Drupal的菜单机制还有很多复杂特性, 来日方长, 有空继续钻研.

另:有一点不明白的是menu_router为啥要用钩子函数, 不能直接用初始数据库脚本麽, 不过我也没研究过安装过程, 似乎好像没有一个地方用了数据库脚本, 有人清楚这一块麽?

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多