作者:老葛,北京亚艾元软件有限责任公司,http://www.yaiyuan.com
通过Google搜索,找不到答案。有很多同样的问题,但是里面没有我们想要的答案。这个时候,我们需要打开common.inc文件,找到7750行,Drupal核心的版本不一样,这里显示的行数也不一致。但是代码是一样的:
/**
* Get the entity controller class for an entity type.
*/
function entity_get_controller($entity_type) {
$controllers = &drupal_static(__FUNCTION__, array());
if (!isset($controllers[$entity_type])) {
$type_info = entity_get_info($entity_type);
//print debug($type_info);
$class = $type_info['controller class'];
//drupal_set_message('123456');
$controllers[$entity_type] = new $class($entity_type);
}
return $controllers[$entity_type];
}
当然,核心里面是没有print debug和drupal_set_message这两句的,这是我调试的时候添加了,我想查看一下$type_info里面包含哪些信息。发现这里打印出来的信息,和我们在前面定义的有出入。我首先想到的是,是不是'controller class'没有设置正确?我们需要自己定义一个控制器类?
问题1:
通过print debug,我还是发现了一个错误,在breadcrumb2_entity_info里面:
'entity keys' => array(
'id' => 'pid',
),
漏网之鱼,复制别人的代码的时候,经常会遇到这种情况,将它修正过来:
'entity keys' => array(
'id' => 'bid',
),
问题2:
将'bundle keys'注视掉,我们只有一个bundle,没有‘type’这个概念。
/*
'bundle keys' => array(
'bundle' => 'type',
),
*/
问题3:
前面的两个问题,是我重读breadcrumb2_entity_info代码以后,所做的两个修正;但是问题仍然存在。我通过进一步的测试,发现验证是正常的,问题出在了表单的提交函数里面。我把breadcrumb2_form_submit里面的代码全部注销掉,问题没有了,当然我们的面包屑实体也没有保存起来。进一步的细化,发现breadcrumb2_save($breadcrumb);出了问题。
接着检查了breadcrumb2_save的代码,里面的逻辑也非常简单,只有这么简单的一行:
return $breadcrumb->save();
在Breadcrumb里面,我们并没有定义save方法,这个方法应该是它的父类Entity定义的。所以回到Entity API模块里面查找Entity类的对应代码。此时我发现,这个类存放到了entity\includes下面的entity.inc文件中了。注意,这个文件夹下面,包含4个inc文件,分别为entity.controller.inc、entity.inc、entity.property.inc、entity.ui.inc、entity.wrapper.inc。我们打开entity.inc文件,找到save方法:
/**
* Permanently saves the entity.
*
* @see entity_save()
*/
public function save() {
return entity_get_controller($this->entityType)->save($this);
}
对这里的代码,做以下修改:
public function save() {
drupal_set_message($this->entityType);
//return entity_get_controller($this->entityType)->save($this);
}
我们去掉了实际的保存逻辑代码,加上了一个drupal_set_message,我想看看此时传递过来的实体类型到底是什么。
再次测试,发现$this->entityType的值竟然为“breadcrumb”,而实际应该为“breadcrumb2”,如果我们的模块名字为breadcrumb就好了,就不会遇到这个问题了。找到原因以后,很快就定位到了错误的代码地方,Breadcrumb的构造方法,原来为:
public function __construct($values = array()) {
parent::__construct($values, 'breadcrumb');
}
我们将它修正为:
public function __construct($values = array()) {
parent::__construct($values, 'breadcrumb2');
}
一字之差,就让我们调试了半天,复制粘贴别人的代码的时候,经常会遇到这样的问题,要改的地方没有改过来,或者改错了。
问题4:
原来的错误消失了,新的错误出现了:
这个错误也非常严重,但是相比前面的错误,至少这个错误还能把页面完整显示出来。刚才发现Breadcrumb类定义的有问题。现在根据错误提示,“entity_id”有问题,也就是我们的bid设置的有问题,前面已经修正了一个地方了。我重读了Profile2模块里面的Profile类的定义,发现里面的代码中,包括很多属性,其中就包括pid。人家是这样定义的:
class Profile extends Entity {
/**
* The profile id.
*
* @var integer
*/
public $pid;
。。。。
当时我定义Breadcrumb类的时候,不知道这些属性的含义,另外在类的成员函数里面也没有用到这些属性,所以一股脑的把它们删除了,现在我们为Breadcrumb添加一个属性:
/**
* The breadcrumb id.
*
* @var integer
*/
public $bid;
问题5:
修改后,我们清空缓存,再次测试,原来的错误消失了,又出现了新的问题:
我直接打开includes\database\query.inc文件,找到716行,看源代码:
public function preExecute() {
// Confirm that the user did not try to specify an identical
// field and default field.
if (array_intersect($this->insertFields, $this->defaultFields)) {
throw new FieldsOverlapException('You may not specify the same field to have a value and a schema-default value.');
}
if (!empty($this->fromQuery)) {
// We have to assume that the used aliases match the insert fields.
// Regular fields are added to the query before expressions, maintain the
// same order for the insert fields.
// This behavior can be overridden by calling fields() manually as only the
// first call to fields() does have an effect.
$this->fields(array_merge(array_keys($this->fromQuery->getFields()), array_keys($this->fromQuery->getExpressions())));
}
// Don't execute query without fields.
if (count($this->insertFields) + count($this->defaultFields) == 0) {
throw new NoFieldsException('There are no fields available to insert with.');
}
// If no values have been added, silently ignore this query. This can happen
// if values are added conditionally, so we don't want to throw an
// exception.
if (!isset($this->insertValues[0]) && count($this->insertFields) > 0 && empty($this->fromQuery)) {
return FALSE;
}
return TRUE;
}
这里的黑体部分,就是我们出错的地方,此时我突然想到了一个问题,我们的breadcrumb数据库表,里面只有一个bid字段,并且这个字段是自增的字段。我们向数据库里面插入数据的时候,是不需要设置这个字段的,除此以外,我们没有别的字段了。我要哭了。在没有遇到这个问题以前,我是没有思考过这个问题的。我查看了多个实体类型的定义,发现主表里面,都是有属性的,即便是Field Collection里面也有一个属性field_name。现在为我们的breadcrumb数据库表,添加什么样的属性呢?最初我考虑的是created、changed,但是这两个属性对于我们来说,没有多大用处,最后我觉得,使用path,把它定义为这里的属性,不用Field模块来管理它了,当然也就不使用Field validation负责它的验证了,我们自己编写它的验证逻辑。这次改动有点多哟,好事多磨。
首先是schema里面的定义,粗体表示新增的:
'bid' => array(
'type' => 'serial',
'not null' => TRUE,
'description' => t("'Primary Key: Unique breadcrumb item ID."),
),
'path' => array(
'description' => t('URL where breadcrumb should be shown.'),
'type' => 'varchar',
'length' => 256,
'not null' => TRUE,
),
接着为类Breadcrumb,添加一个属性$path:
class Breadcrumb extends Entity {
/**
* The breadcrumb id.
*
* @var integer
*/
public $bid;
/**
* The internal path where breadcrumb should be shown.
*
* @var string
*/
public $path;
这下,我们的breadcrumb2.info.inc文件有用了:
class Breadcrumb2MetadataController extends EntityDefaultMetadataController {
public function entityPropertyInfo() {
$info = parent::entityPropertyInfo();
$properties = &$info[$this->type]['properties'];
$properties['path'] = array(
'label' => t('path'),
'description' => t('The internal path where breadcrumb should be shown.'),
'setter callback' => 'entity_property_verbatim_set',
'setter permission' => 'administer breadcrumbs',
'schema field' => 'path',
);
return $info;
}
}
当然,我们的面包屑表单里面,也需要加上这个元素:
function breadcrumb2_form($form, &$form_state, $breadcrumb) {
// Save the breadcrumb for later, in case we need it.
$form['#breadcrumb'] = $breadcrumb;
$form_state['breadcrumb'] = $breadcrumb;
$form['bid'] = array(
'#type' => 'value',
'#value' => isset($breadcrumb->bid) ? $breadcrumb->bid : NULL,
);
$form['path'] = array(
'#type' => 'textfield',
'#title' => t('Path'),
'#maxlength' => 60,
'#default_value' => !empty($breadcrumb->path) ? $breadcrumb->path : '',
'#weight' => -10,
);
做完这些修改以后,卸载breadcrumb2模块,然后重新安装,然后删除Path字段,重新测试。成功了,只不过重定向回来的时候,路径还不存在,导致了空白问题。我通过phpmyadmin检查对应的数据,基本上都正确了,就剩下一个小问题,就是path属性没有保存下来,里面一直问题。这个问题解决起来也并不复杂:
function breadcrumb2_form_submit($form, &$form_state) {
$breadcrumb = &$form_state['breadcrumb'];
$breadcrumb->bid = $form_state['values']['bid'];
$breadcrumb->path = $form_state['values']['path'];
….
粗体部分为新增的代码,这样就正常了。我是在解决这些问题的时候,更加深刻的掌握了创建一个实体类型的技术。原本打算重新写作这部分资料,后来还是觉得把过程中遇到的错误,也原原本本的呈现出来。这是Think in Drupal的一个风格,就是我们把开发中、配置中的错误也完全呈现出来,告诉大家的解决这些问题的步骤。