你现在的位置:首页 > PHP网站建设知识库 > Drupal > 正文

Drupal 7 模块开发Module Development

创建你的第一个模块

这一章的焦点在于模块的创建。上一章我们学习了不少概念,现在咱们开始正式编程了。下面是一些我们即将学到的要点:

 

  • 开始一个新模块
  • 创建.info文件---给drupal提供模块信息
  • 创建.module文件---drupal代码写在这里
  • 增加一个block
  • 掌握drupal的常用function
  • 根据drupal代码格式来写代码
  • 为drupal写自动化测试

 

在这章结束时 你应该对创建自己的模块有一个大体认识。

 

我们的目标:带区块的模块

我们准备创建一个简单的模块,这个模块将用block子系统来添加一个新的block,它能简单展示一个包括所有的现在可用的模块的列表。

区块子系统的概念请自行脑补

我们打算分三步走

 

  • 创建新的模块文件夹和模块文件
  • 进入区域子系统
  • 用drupal自带测试框架写测试代码

 

按照敏捷开发 我们本应该要先写测试代码,这叫TDD 测试驱动,很流行~

但是,在这里我们不是要关注某一种方法论,而是要掌握如何编写模块,因为编写模块非常简单,顺带手学学测试而已,所以我们要先写模块后写测试 原因如下:

 

  • SimpleTest它大概有我们实际模块代码量的两倍。
  • 我们在编写测试时 需要先假定API是什么

 

当然 我们也可以选择常规的TDD开发方式 即:先写测试代码,然后写模块。

so 让我们来编写第一个模块吧

 

创建新模块

创建一个drupal模块是很简单的。有多简单呢?简单到超过5000个模块已经被开发出来了,而且很多drupal开发者居然都是PHP菜鸟级的!实际上,咱们这一章的代码就能给你展示代码编写有多简单 --- 一个文件夹和两个小文件而已。

 

模块名

不用说 模块需要一个名字,但是需要注意,一个drupal模块有两个名字:

  • 人类可读名:如 Views
  • 机读名:drupal内部用

 

放哪儿?

这里有三个地方有module 

\

 

你们猜猜应该放在哪儿呢?一般人可能认为会放在/modules里面,错啦~

 

因为/modules是不允许你放任何东西的,它们都是drupal核心模块,在更新的时候将会被覆写掉

 

第二个放模块比较明显的位置是/sites/all/modules,这是放所有的第三方插件模块的地方 诸如Drush等 就会下载后存放到这里,咱们的代码放到这里也是可以的 起码更新时不会被覆写掉,但是不推荐,除非你做多站,模块需要被所有站访问。

 

推荐放到这里/sites/default/modules,缺省状态下你要自己建一个文件夹,这样做的好处不少,一个是我们找自己的代码好找,还有就是加载时顺序可以调整等等。

 

它的缺点在于制作多站时模块不会被其他站读到。

 

创建模块目录

我们要在 /sites/default/modules里创建一个新模块,里面包含一个.info文件和一个.module文件

 

文件夹与文件都应该被命名为机读名称

 

like this

\

The purpose of the .infofile is to provide Drupal with information about a 
module—information such as the human-readable name, what other modules 
this module requires, and what code files this module provides

编写info文件

.info文件:提供模块的基本信息

代码如下:

 

;$Id$
name = First
description = A first module.
package = Drupal 7 Development
core = 7.x
files[] = first.module
;dependencies[] = autoload
;php = 5.2

 

好 第一行的 是什么?

它是占位符的版本控制系统来存储有关文件的信息的。当文件check in 到drupal的CVS库中时,这一行将自动扩展为:

 

;$Id: first.info,v 1.1 2009/03/18 20:27:12 mbutcher Exp $

 

这段信息表明了该文件何时签入,以及谁放入的

 

现在$Id$已经不用了 这是CSV专用的,而drupal迁入git后 $Id$标签将不再出现

 

name和description是必须的,每个模块的info都会有 在哪里出现呢?如图:

 

\

 

上面的name和description就是对应的~!

 

package = Drupal 7 Development

 

包,如上图 core就是包的名字 所有的模块都放到这个包中 就是放到文件夹中,请注意 这是个人类可读的名字

 

files[] = first.module

 

这是声明我们用到的模块是哪个 

 

dependencies[]

 

这个指令很重要 但是我们这里没有用 这是声明依赖关系的 如果此模块依赖别的模块 那么单独是无法开启这个模块的

 

这样 我们的first.info创建好了 drupal读取的时候 这个模块将出现在我们的“模块”页面上

 

\

 

创建.module文件

.module是一个包含了主要钩子接口的php文件,我们俩共同学习它

 

钩子,它是一个实现了类似观察者的设计模式的function,常常被用来为某一特定事件做回调。

 

当drupal触发了某一事件,而此事件正好是个钩子(这样的事件有数百个),drupal将遍历所有模块找匹配的钩子,然后执行,当所有钩子执行完毕后,drupal才往下处理。

 

编码开始:我们用到一个钩子 --- 提供help信息的钩子

 

<?php
// $Id$
/**
* @file
* A module exemplifying Drupal coding practices and APIs.
*
* This module provides a block that lists all of the 
* installed modules. It illustrates coding standards, 
* practices, and API use for Drupal 7.
*/
/**
* Implements hook_help().
*/
function first_help($path, $arg) {
if ($path == 'admin/help#first') {
return t('A demonstration module.');
}
}

 

讲解代码前 我们先说说编码标准问题,这里有几个问题需要注意的:

 

  • 缩进:所有php和js文件都不用tab缩进,全部使用两个空格的方式
  • <?php?>指令处理:开始由<?php,但是没有以?>结尾。这样做有很多原因,暂不解释。
  • 注释:除了funtions classes interfaces constants files 和 globals ,其他所有注释用//
  • 函数:函数应该以小写字母使用下划线命名 分隔单词。稍后我们将看到如何类的方法名称不同 从这个。 
  • 变量:变量名应该使用下划线在所有小写字母 分隔单词。在对象的成员变量的名称不同

 

(下面一堆关于注释的讲解 不译了)

 

下面看我们的第一个function

 

/**
* Implements hook_help().
*/
function first_help($path, $arg) {
if ($path == 'admin/help#first') {
return t('A demonstration module.');
}
}

 

注释里的 接口 hook_help() 这是drupal注释标准里的 当一个function是一个钩子函数时 前面必须注明用的哪一个。第一别人能快速理解,第二一些自动化工具能找到钩子接口

 

下面讲解用法

 

hook_help接口当用户在查看帮助文档时被调用,每个模块都有一个help钩子,我们的这个模块通过这个钩子提供了简短的帮助文字

 

这个function是如何变成一个钩子接口的呢?是由于严格命名,把hook_help变成first_help,就可以了

 

每一个钩子都拥有自己的参数 具体请查看http://api.drupal.org

 

一个hook_help钩子有两个参数:

  • $path:帮助系统uri路径
  • $arg:访问这个路径的权限

在我们这个例子中 我们只需要关心第一个参数就可以了 基本上 帮助系统通过确定uri来显示帮助信息,我们需要声明一下为这个特殊的uri返回什么帮助信息就行了

 

具体而言 帮助信息的uri就是admin/help#MODULE_NAME,其中MODULE_NAME就是模块的机读名称。

 

我们的function通过检查$path来开始工作,如果路径是admin/help#first 这是这个模块的缺省uri,这时它将返回一些简单信息。

 

现在 让我们先把模块激活 如图

 

\

 

激活后 可以看到help出现在OPERATIONS下,点击进去 可以看到

 

\

 

系统运作的关键在于对路径的检查,当$path符合时,模块开始扑捉到动作。

 

t()函数和翻译

每一个string都会套在t函数中,这是为了以后的国际化

 

  •  如果没有其他语言支持 那么t函数只有一个参数
  • 多语言用一个.po文件来支持
  • 它并不是简单的赋予一个变量来完成功能的

为什么不用变量来代替呢?因为当自动翻译工具运行代码时 它并不运行,只是读取,并不知道正确的变量到底是什么。

 

下一步 咱们要开始用block api来生成一个block,里面是所有可用的模块名称列表

 

运用block API

 

drupal 6中block只有一个function接口 drupal7中 block有了一系列的function可用

 

我们有一大堆的block API可以用,提供了从声明一个新区块到改变已存在区块的内容和行为,在我们这个简单的模块中,我们准备用两个不同的钩子

  • hook_block_info():告诉drupal关于新区块或我们声明的一系列区块的信息
  • hook_block_view(): 告诉drupal区块显示时它将做什么

需要谨记的是---block API与其他的API一样,一个模块的给定钩子的对应接口只能有一个,所以我们只能有一个first_block_info() function。

 

因为模块能够创建无数个block,这就意味着block API必须能够一个接口管理多个bolck,因此,first_block_info()能声明任意多个block,能返回任意多个block

 

这里是block官方API地址 里面甚至有个module的例子 http://api.drupal.org/api/drupal/developer--examples--block_example.module/7.

 

hook_block_info()

所有的function我们都放在first.module中,现在我们开始创建hook_block_info()钩子

 

创建这个钩子的用意是告诉drupal这个模块提供的所有block的信息,注意,就像所有钩子那样,你只需要提供实现功能的接口就行,换言之,如果没有这个接口,那么drupal就会假定这个module没有相关的block。

 

/**
* Implements hook_block_info().
*/
function first_block_info() {
$blocks = array();
$blocks['list_modules'] = array(
'info' => t('A listing of all of the enabled modules.'),
'cache' => DRUPAL_NO_CACHE,
);
return $blocks;
}

 

可以看出 hook_block_info()的实现不需要参数 会返回一个关联数组

 

这个返回的数组应该是一个包含了所有该模块声明了的block的对象,这个对象应该类似这样:$name => array($property => $value)

 

以上代码定义了一个名为list_modules的区块,它有两个属性:

  • info:显示在区块管理界面的一句话简介
  • cache:告诉drupal如何缓存此区块。我们选择的是放弃缓存

还有很多其他属性 请看http://api.drupal.org/api/function/hook_block_info/7

 

下一步 我们要把这个块儿显示出来

The block view hook

需要用到的是hook_block_view(),它需要一个参数---用于检索的块名称,返回一个给定名字的数组集合。

 

我们知道我们的块名称为list_modules,开始编写代码

/**
* Implements hook_block_view().
*/
function first_block_view($block_name = '') {
if ($block_name == 'list_modules') {
$list = module_list();
$theme_args = array('items' => $list, 'type' => 'ol');
$content = theme('item_list', $theme_args);
$block = array(
'subject' => t('Enabled Modules'),
'content' => $content,);
return $block;
}
}

 

这个function拿到了区块名称后,它将把名称作为唯一标识符来对待

 

如果$block_name为list_modules,那么我们将返回内容。

 

我们来看第一行 我们调用了drupal函数 module_list()。这个function简单的返回了一个模块名称的数组。

 

现在我们有了一份儿元数组数据,下一步我们要格式化它好让其显示,drupal总是通过主题层来干这个事儿的 所以我们交给主题层 期望它们显示成为一个有序列表

 

drupal7 中 theme()需要一到两个参数

 

  • 主题操作的名称
  • 传到主题层的变量数组

以前的版本中 theme可以传入任意多的参数 取决于主题层的要求,现在不会这么做了。

 

为了把string数组转变成html list,我们采用item_list主题,我们带入了两个参数

 

  • 我们希望列出的条目
  • 我们希望看到的列表的类型

就这样 我们用theme()得到了一个html

 

现在我们所要做的就是组装我们的区块view层必须返回的data了,hook_block_view()接口需要返回一个带有两个参数的数组:

 

  • subject: 块名称或块题目
  • content: 带有格式的block的内容

第一个参数我们硬编码写死了 第二个参数我们把theme()代入给content

 

我们需要关注的是$block代码的写法

 

$block = array(
'subject' => t('Enabled Modules'),
'content' => $content,
);

 

最后是有逗号的 这是符合php语法的

 

第一个模块的动作

可以开始了 激活模块 然后打开block管理界面,把这个block放到一个region中 看看效果吧

 

\

 

测试代码略