Drupal 模块开发基本教程(三)

来源:互联网 发布:淘宝客佣金怎么查询 编辑:程序博客网 时间:2024/05/10 10:53

第三部分:自定义node类型

Drupal系统本质上是一个CMS系统Framework,你可以在此基础上为满足自己的要求自由的扩展。从Drupal的观点看,所有的内容对象都应该是节点(node),整个Framework都基于这个假设来运转,从一个熟悉OOP的程序员的观点来看,node就是对象,处理不同类型的node就象处理从node类派生的各种子类。当然,这不过是PHP程序而已,你尽可以想怎样就怎样,但是,和OOP的继承的好处一样,将内容纳入node的管理体系至少可以立刻获得如下的功能和好处:

1)Drupal核心中node的基本增删修改删除功能,评论功能,分类功能,审核功能,日志功能,状态统计功能,搜索功能,各种node相关管理功能;
2)所有支持node的drupal扩展模块提供的功能,包括但不仅限于节点附件上传,节点计划发布/隐藏,所见即所得编辑器,图片插入助理等等;
3) 核心和扩展模块的升级将立刻使你的节点类型享受增强的功能或特性;
4) 核心和扩展模块的安全补丁将使你的节点类型更加安全;
等等……,这一切只需要你定义一下节点类型即可获得,何乐而不为呢?如果你将内容放入drupal进行管理却不遵循node定义和管理的方式,我可以肯定地说你会在系统升级方面遇到困难和麻烦——简言之就是大伙儿都不是你这么想的,你的代码和别人不兼容。这可能会是你在开源系统里能够遇到的最糟糕的一件事了。

定义新的node类型其实非常简单,现在仅需一个hook_node_info(),而不向4.6版本时需要hook_node_name() 和 hook_node_types()。模块文件名叫node_example.module:

function node_example_node_info(){
   return array('node_example'=> array('name' => t('example node'),'base' => 'node_example'));
}

 

hook_node_info()钩子是必须要实现的。此函数描述了这个模块可提供的节点。'name'的值是节点的人性化名字,'base'就是drupal系统识别的名字了。Durpal通过'base'的值知道操作这种类型的节点时应当在hook函数将加上什么样的前缀:如果'base'为“node_foo”,那么插入一个这种节点时调用的对应函数是'node_foo_insert'。

为界面友好的目的,我们一般还会实现hook_help(),这样用户创建节点时可以看到诸如介绍一类的东西:

function node_example_help($section){
   switch ($section) {
    case 'admin/modules#description':
      // 在admin/modules页面显示的模块描述信息
      return t('An example module showing how to define a custom nodetype.');
    case 'node/add#node_example':
      // 在node/add“创建内容”页面上显示的此类型节点的帮助信息。
      return t('This is an example node type with a few fields.');
   }
}

 

自然我们也要定义hook_perm以实现节点的权限控制。我们定义了三个权限,一个用来限制谁可以浏览此种节点,另一个用来限制谁可以创建此种节点,最后一个用来限制是否允许用户修改自己创建的节点。为什么需要第三个权限呢?如果管理员允许未注册用户创建此种节点,那么管理员应该同时取消未注册用户修改自己创建的节点的权限,因为未注册用户对系统来说都是uid为0的同一个用户。

function node_example_perm() {
   return array('view examplenode', 'create example node', 'edit own example nodes');
}

 

另外节点权限管理需要定义的钩子是hook_access(),这个钩子可以自定义用户对节点的增删修改等操作的权限。下面是最常见的定义模式:

function node_example_access($op,$node) {
   global $user;
   if ($op == 'view') {
    // 有浏览权限的用户才能浏览此种节点
    return user_access('view example node');
   }
   if ($op == 'create') {
    // 有创建权限的用户才能创建此种节点
    return user_access('create example node');
   }
   //如果有需要的权限,创建节点的用户可以修改和删除它
   if ($op == 'update' || $op =='delete') {
    if (user_access('edit own example nodes')&& ($user->uid ==$node->uid)) {
      return TRUE;
    }
   }
}

 

另一个需要定义权限的地方是节点添加的页面和路径,下面按照国际惯例定义hook_menu以控制谁可以访问增加节点的页面:

function node_example_menu($may_cache){
   $items = array();
   if ($may_cache) {
// 此处只定义访问路径的权限即可
    $items[] = array('path' => 'node/add/node_example','title' => t('example node'),
      'access' => user_access('create examplenode'));
   }
   return $items;
}

 

节点定义和用户权限都有了,下面我们可以开始完善节点的增删修改浏览的功能。这里使用的节点例子类型叫node_example,它允许用户节点中保存“颜色(color)”和“数量(quantity)”两种自定义信息。因为要存储这些附加信息,我们需要在数据库中增加额外的表。

数据库定义:
   CREATE TABLE node_example(
      vid int(10) unsigned NOT NULL default '0',
      nid int(10) unsigned NOT NULL default '0',
      color varchar(255) NOT NULL default '',
      quantity int(10) unsigned NOT NULL default '0',
      PRIMARY KEY (vid, nid),
      KEY `node_example_nid` (nid)
    );

 

事实上,如果不需处理这些额外的附加信息,你会发现有上述函数的模块已经能够处理新定义的节点类型了,前面叙述的你该即刻享受的系统好处你都能享受到了,呵呵。但要处理节点类型特有的附加信息,我们还有许多工作要做:

(一) 增加自定义节点

首先,虽然基本的增加节点的提交页面系统已经提供了,但是上面没有我们要的自定义信息的输入框或选择框,这样我们就必须实现hook_form()以在提交页面上收集自定义信息。这个钩子要求我们返回一个数组值,其中包含了定制表单各元素的定义信息,注意这也是drupal4.7的标准表单定义方法。

functionnode_example_form(&$node) {
   // 用来输入节点标题的文本框
   $form['title'] = array(
    '#type' => 'textfield', // 表单元素的类型
    '#title' => t('Title'), // 表单元素的标题
    '#required' => TRUE, // 是否必须输入的值
    '#default_value' => $node->title,//缺省值
    '#weight' => -5
   );
   //如果我们希望表单元素按特定的顺序排列,我们可以设置元素的Weight值。
   //但是其他模块可能也插入设置了weight值的表单元素,这样元素顺序可能仍然
   //不是我们想要的样子。为避免这一情况,我们可以把元素放到子数组中,一个
   // 数组中的元素总是可以保证顺序的了吧!
   $form['body_filter']['body'] =array(
    '#type' => 'textarea',
    '#title' => t('Body'),
    '#default_value' =>$node->body,
    '#required' => FALSE
   );
   $form['body_filter']['filter']= filter_form($node->format);
   //上面是普通应有的标题和正文,下面是我们特有的节点信息了
   $form['color'] = array(
    '#type' => 'textfield',
    '#title' => t('Color'),
    '#default_value' =>$node->color
   );
   $form['quantity'] =array(
    '#type' => 'textfield',
    '#title' => t('Quantity'),
    '#default_value' =>$node->quantity,
    '#size' => 10,
    '#maxlength' => 10
   );
   return $form;
}

 

用户用上面的表单提交数据,drupal自动会过滤提交的数据,对于表单里没有定义却被提交的值,drupal会自动把它们过滤掉以保证系统安全。然后,我们可以通过hook_validate()验证提交的有效数据是不是我们真正想要的,下面我们验证一下用户提交的quantity值是不是一个数字,不是就报告错误,用户会被要求重新输入,如果没有数据提交(因为前面表单定义里并没有要求此值是必须的),则认为quantity是0:

functionnode_example_validate(&$node) {
   if($node->quantity) {
    if (!is_numeric($node->quantity)) {
      form_set_error('quantity', t('The quantity must be anumber.'));
    }
   }
   else {
    // Let an empty field mean "zero."
    $node->quantity = 0;
   }
}

要注意的是告诉系统出错了并不是返回一个什么值,而是调用form_set_error设置表单错误,这样可以在一个函数里设定多个错误。  

 

如果数据验证成功,则新节点就会生成了,新节点信息会写入到数据库中。且慢,我们好像并没有写入附加信息到数据库的程序段。当节点插入时,drupal会调用所有实现hook_insert()的函数,这让我们有机会向数据库写入我们自己的节点自定义信息:

function node_example_insert($node){
   db_query("INSERT INTO{node_example} (vid, nid, color, quantity) VALUES (%d, %d, '%s',%d)", $node->vid, $node->nid,$node->color, $node->quantity);
}

格式很简单,你不用考虑用户的提交是怎么被处理的,你只要直接引用它们就可以了,$node->color、$node->quantity什么的,它们一定正确的在那里。  

 

(二) 修改自定义节点

Drupal本身支持节点拥有多个版本,我们在这里也需要考虑到这一点。

function node_example_update($node){
   // 处理产生一个新版本的情况
   if($node->revision) {
    node_example_insert($node);
   }
   else {
    db_query("UPDATE {node_example} SET color = '%s', quantity = %dWHERE vid = %d", $node->color,$node->quantity, $node->vid);
   }
}

 

(三) 删除自定义节点

删除很简单,系统本来就实现了节点的删除功能,我们需要做的是在适当的时候把我们写入数据库的附加信息给清除掉。当节点删除时,drupal会调用所有实现hook_delete()的函数,这样各个模块都有机会清理自己生产的垃圾了。

function node_example_delete($node){
   // 注意这里我们删除了指定nid的所有版本
   db_query('DELETE FROM{node_example} WHERE nid = %d', $node->nid);
}

 

(四) 与系统其他部分互动

负责载入节点对象的node_load()函数会发出hook_load()调用,以便扩展模块将自定义属性补充进它返回的$node对象中。因此我们还必须实现hook_load(),附加信息才会在载入节点对象时自动也被载入。

function node_example_load($node){
   $additions =db_fetch_object(db_query('SELECT color, quantity FROM{node_example} WHERE vid = %d', $node->vid));
   return $additions;
}

 

(五) 自定义节点显示格式

下面的代码实现了hook_view(),这个钩子在用户浏览节点时被调用。实现得很简单,节点的正文(body)和节选(teaser)都是系统功能提供和实现的,我们只需要把自定义的信息加到其中即可。这里我们用的是最简单的方式,直接加在正文和节选的后面就可以了。实际情况下,这个显示的格式是可以任意调整的。

functionnode_example_view(&$node, $teaser = FALSE, $page =FALSE) {
   $node = node_prepare($node,$teaser);
   $order_info ='<divclass="node_example_order_info">';
   $order_info .= t('The order isfor %quantity %color items.', array('%quantity' =>$node->quantity, '%color' =>$node->color));
   $order_info .='</div>';
   $node->body .=$order_info;
   $node->teaser.= $order_info;
}

 

写到这里,发现要用好自定义节点类型要实现的hook还是蛮多的,不过只要按部就班,也应该能很快实现,就跟定义一个类然后实现些方法差不多。然后这个定义的节点就可以在系统中被自由使用了,似乎比OOP还要略微简单点。

原创粉丝点击