由于我们已经分配了动作,所以当一个新评论被发布时,当前用户将被阻止。让我们仔细的看一下,这里都发生了什么。我们已经知道,Drupal是通过触发钩子来向模块通知特定事件的。在这里触发的就是评论钩子。由于当前是一个新评论正被添加进来,所以当前的特定操作就是insert操作。触发器模块实现了评论钩子。在这个钩子内部,它对数据库进行查询,来获取分配到这个特定触发器上的所有动作。数据库就会将我们分配的动作“阻止当前用户”返回给该钩子。现在,触发器模块就可以执行该动作了,它符合标准的动作函数签名example_action($object, $context)。
但是我们又有了一个问题。当前要被执行的动作是一个用户类型的动作,而不是评论类型的。所以它期望接收到的对象是一个用户对象!但是在这里,一个用户动作在一个评论钩子的上下文中被调用了。与评论相关的信息被传递给了钩子,而传递的不是与用户相关的信息。那么我们该怎么办呢?实际上发生的是,触发器模块会判定我们的动作是一个用户动作,并加载用户动作所需的$user对象。下面是来自modules/trigger/trigger.module的代码,它给出了这是如何实现的:
/**
* When an action is called in a context that does not match its type,
* the object that the action expects must be retrieved. For example, when
* an action that works on nodes is called during the comment hook, the
* node object is not available since the comment hook doesn't pass it.
* So here we load the object the action expects.
*
* @param $type
* The type of action that is about to be called.
* @param $comment
* The comment that was passed via the comment hook.
* @return
* The object expected by the action that is about to be called.
*/
function _trigger_normalize_comment_context($type, $comment) {
switch ($type) {
// An action that works with nodes is being called in a comment context.
case 'node':
return node_load($comment['nid']);
// An action that works on users is being called in a comment context.
case 'user':
return user_load(array('uid' => $comment['uid']));
}
}
当为我们的用户动作执行前面的代码时,匹配的是第2种情况,所以将会加载用户对象并接着执行我们的用户钩子。评论钩子所知道的信息(比如,评论的标题)将会通过$context参数传递给动作。注意,动作是如何查找用户ID的----首先在对象中查找,其次在上下文中查找,最后使用全局变量$user:
/**
* Implementation of a Drupal action.
* Blocks the current user.
*/
function user_block_user_action(&$object, $context = array()) {
if (isset($object->uid)) {
$uid = $object->uid;
}
elseif (isset($context['uid'])) {
$uid = $context['uid'];
}
else {
global $user;
$uid = $user->uid;
}
db_query("UPDATE {users} SET status = 0 WHERE uid = %d", $uid);
sess_destroy_uid($uid);
watchdog('action', 'Blocked user %name.', array('%name' =>
check_plain($user->name)));
}
动作必须要聪明一点,因为当它们被调用时它们并不知道发生了什么。这就是为什么,动作最好是直接的,甚至是原子的。触发器模块总是将当前的钩子和操作放在上下文中,通过上下文将其传递过来。它们的值存储在$context['hook'] 和$context['op']中。这种方式是向动作传递信息的标准方式。
老葛的Drupal培训班 Think in Drupal