Drupal 菜单系统 hook_menu

Drupal的菜单系统复杂但是强大,术语“菜单系统”(menu system)有些取名不当,最好认为菜单系统有三个主要职责:回调映射、访问控制、菜单定制。菜单系统基本的代码在includes/menu.inc中,那些可选代码包含一些定制菜单时激活的特性则在modules/menu中。

创建一个菜单项

为创建一个菜单项,我们使用hook_menu(),hook_menu()持有一个由准备附加到一个菜单的项目组成的数组,每一个项目自己就是一个键值对组成的数组,描述菜单项的属性,下表描述了菜单项数组各键的细节:

KeyValue
title一个必须字段,菜单项未翻译title
title callback一个生成title的函数,默认是t(),因为这个原因,我们不把前面的title放在t()函数中,如果你不想翻译,就简单设置此项为FALSE。
description菜单项的未翻译描述
page callback当用户浏览此路径时调用的函数,用来显示web页面
page arguments要传递给页面回调函数的参数的数组;整型值传递匹配的URL成分
access callback一个函数,返回一个布尔值,判定用户是否有访问该菜单项的权限;默认为user_access(),除非从父菜单项继承一个值
access arguments传递给访问回调函数的参数数组;整型值传递匹配的URL成分
file回调访问之前将要包含的文件;它允许回调函数处于一个单独的文件中。文件应该相对于实现模块路径,除非用“file path”选项另外指定其他值
file path指向包含file指定的文件所在文件夹的路径,默认为实现hook的模块路径
weight一个整数值,决定项目在菜单中的相对位置;值高的项目在下面,默认为0
menu_name
type一个标志的位掩码,描述菜单项的特性,值可以使下面这些:
MENU_NORMAL_ITEM 正常菜单项,可以在菜单树上展示,并能被管理员移动/隐藏
MENU_CALLBACK 回调简单地注册一个路径,URL访问时恰当的函数将被触发
MENU_SUGGESTED_ITEM 模块可以“启发”菜单项,管理员可以激活
MENU_LOCAL_TASK 本地任务,默认渲染成选项卡
MENU_DEFAULT_LOCAL_TASK 每一个本地任务集合都要提供个默认的任务,点击它是就像点击父项连接到相同路径

首先,我们需要在sites/all/modules/custom/menufun中增加menufun.info:

name = Menu Fun
description = Learning about the menu system.
package = Pro Drupal Development
core = 7.x 
files[] = menufun.module

然后我们需要建立sites/all/modules/custom/menufun/menufun.module文件,它包含我们实现的菜单钩子和我们要执行的函数:

<?php
/**
 * @file
 * Use this module to learn about Drupal's menu system.
 */

/**
 * Implementation of hook_menu()
 */
function menufun_menu() {
  $items['menufun'] = array(
    'title' => 'Greeting',
    'page callback' => 'menufun_hello',
    'access callback' => TRUE, // 所有用户和匿名都可以访问
    'type' => MENU_CALLBACK,
  );
  return $items;
}

在前面的代码中,你可以看见我们用3个键值对数组作为项建立我的菜单($items[‘menufun’]):

“title”:必需,定义未翻译的菜单title
“page callback”:用户访问菜单路径是要调用的函数
“access callback”:典型地包含一个返回布尔值的函数
/**
 * Page callback
 */
function menufun_hello() {
  return t('Hello!');
}

激活这个模块,菜单项将被插入路由表,那么当访问http://localhost/bookstore/menufun (link is external)时,Drupal就找到并允许我们的函数,展示如图。
需要注意的重要的东西是我们是定义了一个路径并映射它到一个函数,此路径是Drupal路径。我们将路径定义为我们的$items数组的键值,我们使用的路径的名字与我们的模块名相同,这个实用技术保证了原始URL的命名空间,当然,你能定义任意路径。
这里写图片描述

Page Callback参数

有时,你可能希望提供更多的信息到映射路径的页面回调函数,首先,路径任何附加的部分都是自动向前传递,让我们像下面一样改变我们的函数:

function menufun_hello($first_name = '', $last_name = '') {
  return t('Hello @first_name @last_name', array('@first_name' => $first_name, '@last_name' => $last_name));
}

如果现在访问http://localhost/bookstore/menufun/John/Doe (link is external),将展示如图的情形:
这里写图片描述

注意每一个URL扩展成分是怎样作为参数传递给我们的回调函数的。
你还能在菜单钩子中通过给$items数组附加一个可选的page arguments键来定义页面回调参数,定义页面参数比较常用是因为它允许你在传递给回调函数的参数上增加更多控制。
例如,让我们更新我们的menufun模块,为我们的菜单项增加page arguments:

function menufun_menu() {
  $items['menufun'] = array(
    'title' => 'Greeting',
    'page callback' => 'menufun_hello',
    'page arguments' => array('Jane', 'Doe'),
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );

  return $times;
}

而后Drupal有明确给出页面参数给出的指令,任何剩余的路径参数是传递给回调函数但没获得解释,作为额外的参数,使用PHP参数重写机制。从URL来的参数还是可用的,为访问它们,你应当重写你的回调函数声明来增加URL参数。你修改了菜单使下列函数声明$first_name结果是Jane(第一个参数)$last_name是Doe(第二个参数)。

function menufun_hello($first_name = '', $last_name = '') { ... }

让我们测试在页面参数和URL中输入Jane Doe的情形,进入http://localhost/bookstore/menufun/John/Doe将展现如图的情形(如果不是这样,你就是忘了重建菜单)
这里写图片描述

如果你想使用URL传来的值,你要用下面的值更新页面回调函数:

function menufun_hello($first_name = '', $last_name = '') {

  return t('Hello @first_name @last_name from @from_first_name @from_last_name', array('@first_name' => $first_name, '@last_name' => $last_name));

}

更新你的版本,清空缓存,使用http://localhost/bookstore/menufun 访问来看看情况。
这里写图片描述

在另外的文件中页面回调

如果你没有指明另外情况,Drupal假设你的页面回调能在你的.module文件中找到,许多模块都是被分割成多个文件,以使在每个页面请求时按条件载入不常用的文件,菜单项file键(如’file => ‘menu_geetings.inc’)用来指明包含回调函数的文件名。

作为例子,我要更新menufun.module hook_menu()函数去包含里面有回调函数的文件的名字。下列代码增加了’file’ => ‘menufun_greetings’到菜单项数组,我还改变页面回调到menufun_greeting,只是演示,此回调不使用已经存在于menufun.module中的函数。

/**
 * Implementation of hook_menu().
 */

function menufun_menu() {
  $items['menufun'] = array(
    'title' => 'Menu Fun',
    'page callback' => 'menufun_greeting',
    'file' => 'menufun_greeting.inc',
    'page arguments' => array('Jane', 'Doe'),
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  return $items;
}

下一步我们将在menufun目录建立名为menufun_greeting.inc,代码是:

<?php

function menufun_greeting($first_name = '', $last_name = '', $from_first_name='', $from_last_name='') {
  return t('Hello @first_name @last_name from @from_first_name @from_last_name', array('@first_name' => $first_name, '@last_name' => $last_name, '@from_first_name' => $from_first_name, '@from_last_name' => $from_last_name));
}

保存这两个文件,清除缓存,测试改变后的情况,你应该能得到相同的结果,只是执行时间上扩大了调用.module的时间。
这里写图片描述

在导航区块增加一个连接

在菜单例子中,我们声明菜单项的类型是MENU_CALLBACK,现在改成MENU_NORMAL_ITEM,我们指明我们不想简单地将路径映射到回调函数,我们想让Drupal去将它包含进一个菜单。

function menufun_menu() {
  $items['menufun'] = array(
    'title' => 'Menu Fun',
    'page callback' => 'menufun_greeting',
    'file' => 'menufun_greeting.inc',
    'page arguments' => array('Jane', 'Doe'),
    'access callback' => TRUE,
    'type' => MENU_NORMAL_ITEM,
  );

  return $items;
}

现在菜单项目应该展示到了导航区块上,如图:
这里写图片描述

如果你不想将它放到那个地方,你可以增加或减少它的weight值,weight是菜单定义项目中的另一个键。

function menufun_menu() {
  $items['menufun'] = array(
    'title' => 'Menu Fun',
    'page callback' => 'menufun_greeting',
    'file' => 'menufun_greeting.inc',
    'page arguments' => array('Jane', 'Doe'),
    'access callback' => TRUE,
    'weight' => -6,
    'type' => MENU_NORMAL_ITEM,
  );

  return $items;
}

菜单项可以不用改变代码而使用管理工具重排位置,Structure->Menus(菜单模块应该激活)。
这里写图片描述

菜单嵌套

目前为止,我们只定义了一个单个静态的菜单项,让我们用下面代码增加第二个和其它的回调:

function menufun_menu() {
  $items['menufun'] = array(
    'title' => 'Menu Fun',
    'page callback' => 'menufun_greeting',
    'file' => 'menufun_greeting.inc',
    'page arguments' => array('Jane', 'Doe'),
    'access callback' => TRUE,
    'type' => MENU_NORMAL_ITEM,
    'weight' => '-1',
  );

  $items['menufun/farewell'] = array(
    'title' => 'Farewell',
    'page callback' => 'menufun_farewell',
    'file' => 'menufun_greeting.inc',
    'access callback' => TRUE,
    'type' => MENU_NORMAL_ITEM,
  );

  return $items;
}

下一步,在menufun_greeting.inc文件中增加如下的页面回调函数menufun_farewell:

function menufun_farewell() {
  return t('Goodbye');
}

更新模块后,别忘记清除缓存。
这里写图片描述

访问控制

到目前为止,我们简单地将access callback键设置成TRUE,这意味着任何人都能访问我们的菜单,通常菜单访问是通过在模块内使用hook_permission()来定义权限来控制的,并使用一个函数来测试他们,函数名用于定义菜单项的access callback键,典型是user_access。让我们定义一个权限叫做 receive greeting,如果用户的规则不允许这个权限,那么他将在访问http://localhost/bookstore/menufun时收到“access denied”消息。

/**
 * Implementation of hook_permission()
 */

function menufun_permission() {
  return array(
    'receive greeting' => array(
      'title' => t('Receive a greeting'),
      'description' => t('Allow users receive a greeting message'),
    ),
  );
}

/**
 * Implementation of hook_menu().
 */

function menufun_menu() {
  $items['menufun'] = array(
    'title' => 'Menu Fun',
    'page callback' => 'menufun_greeting',
    'file' => 'menufun_greeting.inc',
    'page arguments' => array('Jane', 'Doe'),
    'access callback' => 'user_access',
    'access arguments' => array('receive greeting'),
    'type' => MENU_NORMAL_ITEM,
    'weight' => '-1',
  );
  $items['menufun/farewell'] = array(
    'title' => 'Farewell',
    'page callback' => 'menufun_farewell',
    'file' => 'menufun_greeting.inc',
    'access callback' => 'user_access',
    'access arguments' => array('receive greeting'),
    'type' => MENU_NORMAL_ITEM,
  );
  return $items;
}

在前面的代码中,范文将被调用user_access(‘receive greeting’)的结果决定,在这种方式下,菜单系统服务就像个看门人,基于用户规则决定哪个路径可以访问、哪个拒绝访问。

TIP:user_access()函数是默认的访问回调,如果你不定义访问回调函数,你的访问参数将有菜单系统传递给user_access()函数。

子菜单项不从它的父菜单项继承访问回调和访问参数。access argument键应该在每个菜单项都定义,access callback键只在其与user_access不同的时候定义,例外情况是当菜单项的类型是MENU_DEFAULT_LOCAL_TASK的时候,它将继承父项的access callback和access arguments,所以为清晰,最好明确地定义这些键,即使是默认本地任务。

本地化和定制标题

菜单title有两种,静态和动态,静态标题有分配title键的值建立的,动态标题通过一个title回调来建立的。Drupal自动翻译静态标题,所以你不要把它封装到t()函数中,如果你用动态标题,你必须负责在你的回调中做翻译工作。

'title' => t('Greeting') // 不要这样做。

注意:description永远都是静态的,值是由description键设定,由Drupal自动翻译。

定义标题回调

标题可以在运行时用标题回调动态创建,下面例子演示使用标题回调将标题设置成当前日期和时间,然后我用一个标题回调,此函数负责在返回值之前执行翻译,为执行翻译,我们将要返回的值封装在t()中。

function menufun_menu() {
  $items['menufun'] = array(
    'title' => 'Greeting',
    'title_calback' => 'menufun_title',
    'description' => 'A salutation.',
    'page callback' => 'menufun_hello',
    'access callback' => TRUE,
  );
  return $items;
}

/**
 * Page callback.
 */
function menufun_hello() {
  return t('Hello!');
}
/**
 * Title callback.
 */
function menufun_title() {
  $now = format_date(time());
  return t('It is now @time', array('@time' => $now));
}

菜单项标题的设置可以在运行时通过使用一个定制的标题回调来完成。

但是你想分隔菜单项标题和页面标题怎么办?简单 – 你可用drupal_set_title()来设置页面标题:

function menufun_title() { drupal_set_title(t('The page title')); $now = format_date(time()); return t('It is now @time', array('@time' => $now)); }

结果是页面一个标题,而菜单项是另外一个标题

菜单项通配符

字符 % 是Drupal菜单项中的通配符,意味着这个值是在运行时由在URL的通配符位置找到的值决定,这是一个使用通配符的菜单项:

function menufun_menu() {
  $items['menufun/%'] = array(
    'title' => 'Hi',
    'page callback' => 'menufun_hello',
    'page arguments' => array(1),
    'access callback' => TRUE,
  );
  return $items;
}

此菜单项对于Drupal路径menufun/hi menufun/foo/bar menufun/123和menufun/file.html都可以工作,但是它对路径menufun无效。菜单的路径应该分隔开来写,使它只包含路径的一部分,通配符只匹配两部分之中的一个字符串。注意尽管%通常设计成匹配一个数字(如user/%/edit匹配user/2375/edit),但实际上它匹配位置上的任何文本。

注意:带有通配符的菜单项将不再显示在导航菜单中,即使是菜单项type设置成为MENU_NORMAL_ITEM,原因显而易见,因为带有通配符的路径结构Drupal不知道怎样为这个链接构建URL,但是请看后面章节“Building Paths from Wildcards Using to_arg()函数”找到怎样告诉Drupal来使用URL。

通配符和页面回调参数

在菜单路径结尾处的通配符不影响传递给页面回调的URL的附加部分,因为通配符只是匹配到下一个斜杠为止,继续我们的例子路径menufun/%,URL http://example.com/?q=menufun/foo/Fred只有字符串foo匹配通配符,路径的下一个位置(Fred)将被作为一个参数传递给页面回调。

使用通配符的值

要使用路径匹配的部分,需指定在page arguments键指定路径部分的序号:

function menufun_menu() {
  $items['menufun/%/bar/baz'] = array(
    'title' => 'Hi',
    'page callback' => 'menufun_hello',
    'page arguments' => array(1), // The matched wildcard.
    'access callback' => TRUE,
  );
  return $items;
}

/**
 * Page Callback.
 */
function menufun_hello($name = NULL) {
  return t('Hello. $name is @name', array('@name' => $name));
}

第一个参数 namearray(1)100menufun12bar b也将被传递,因为Drupal传递路径的组成部分优先于Drupal传递路径参数(请看Page Callback Argument)。
这里写图片描述

通配符和参数替换

在实际应用中,Drupal路径成分通常用来去查看和改变一个诸如节点或用户等对象,例如,路径node/%/edit用来去编辑node,路径user/%用来去查看关于此用户ID的信息。让我们注视一下后面的菜单项,那个能在modules/user/user.module中找到hook_menu()的实现,这路径相应的URL匹配的应该像http://example.com/?q=user/2375 (link is external)这样的,那是你可能在Drupal页面点击“我的账户”时的URL:

$items['user/%user_uid_only_optional'] = array(
  'title' => 'My account',
  'title callbcak' => 'user_page_title',
  'title arguments' => array(1),
  'page callback' => 'user_view_page',
  'page arguments' => array(1),
  'access callback' => 'user_view_access',
  'access argument' => array(1),
  'weight' => -10,
  'menu_name' => 'user-menu',
);

整理中……..
参照:http://drupalchina.cn/doc/1309.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值