You are here

7.12 调试并解决已有代码的问题

admin 的头像
Submitted by admin on 星期二, 2015-09-01 09:54
作者:老葛,北京亚艾元软件有限责任公司,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 debugdrupal_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文件中了。注意,这个文件夹下面,包含4inc文件,分别为entity.controller.incentity.incentity.property.incentity.ui.incentity.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

原来的错误消失了,新的错误出现了:

图片4.png 

这个错误也非常严重,但是相比前面的错误,至少这个错误还能把页面完整显示出来。刚才发现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

修改后,我们清空缓存,再次测试,原来的错误消失了,又出现了新的问题:

图片5.png 

我直接打开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数据库表,添加什么样的属性呢?最初我考虑的是createdchanged,但是这两个属性对于我们来说,没有多大用处,最后我觉得,使用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的一个风格,就是我们把开发中、配置中的错误也完全呈现出来,告诉大家的解决这些问题的步骤。


Drupal版本: