Drupal 实体方法加载两次
最近我用实体API构建了一个Drupal7模块,但遇到了一个问题。调试编辑表单时,我注意到load方法被调用了两次,这导致了一个错误 可恢复的致命错误:的对象 无法转换类stdClass 串通 DatabaseStatementBase->execute() (雷格尔2039货车) /drupal7/includes/database/database.inc) 这是由执行两次FooController::load引起的 下面是我一直在使用的代码Drupal 实体方法加载两次,drupal,drupal-entities,Drupal,Drupal Entities,最近我用实体API构建了一个Drupal7模块,但遇到了一个问题。调试编辑表单时,我注意到load方法被调用了两次,这导致了一个错误 可恢复的致命错误:的对象 无法转换类stdClass 串通 DatabaseStatementBase->execute() (雷格尔2039货车) /drupal7/includes/database/database.inc) 这是由执行两次FooController::load引起的 下面是我一直在使用的代码 function foo_menu() {
function foo_menu() {
$items = array();
...
$items['admin/foo/%foo/edit'] = array(
'title' => 'Edit foo',
'page callback' => 'foo_edit',
'page arguments' => array(2),
'access arguments' => array('administer content'),
'type' => MENU_CALLBACK,
);
...
return $items;
}
function foo_edit($foo) {
return drupal_get_form('foo_edit_form', $foo);
}
function foo_edit_form($form, &$form_state, $foo) {
$form['#foo'] = $foo;
$form_state['values'] = $foo;
$form['id'] = array(
'#type' => 'hidden',
'#value' => $foo->id,
);
...
$form['picture'] = array(
'#type' => 'file',
'#title' => t('Picture'),
'#default_value' => $foo->picture->filename,
);
...
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
return $form;
}
function foo_edit_form_submit($form, &$form_state) {
$foo = (object)$form_state['values'];
if ($picture = file_save_upload('picture')) {
$picture = file_move($picture, FOO_FILE_PATH . $picture->filename, FILE_EXISTS_RENAME);
$picture->status |= FILE_STATUS_PERMANENT;
$picture = file_save($picture);
$foo->picture = $picture->fid;
}
entity_get_controller('foo')->save($foo);
drupal_set_message(t('Foo saved.'));
$form_state['redirect'] = 'admin/foo';
}
可能是最重要的部分:
function foo_load ($id) {
$foo = foo_load_multiple(array($id));
return $foo ? $foo[$id] : FALSE;
}
function foo_load_multiple ($ids = array(), $conditions = array()) {
return entity_load('foo', $ids, $conditions);
}
class FooController extends DrupalDefaultEntityController {
...
function load ($ids = array(), $conditions = array()) {
$foos = parent::load($ids, $conditions);
// Code executed twice
error_log('FooController::load');
foreach ($foos as $foo) {
$foo->picture = file_load($foo->picture);
}
return $foos;
}
...
}
编辑:调用堆栈
... (Array, 11 elements)
11: load (Array, 7 elements)
10: entity_load (Array, 4 elements)
9: foo_load_multiple (Array, 4 elements)
8: foo_load (Array, 4 elements)
7: _menu_load_objects (Array, 4 elements)
6: _menu_translate (Array, 4 elements)
5: menu_get_item (Array, 4 elements)
4: menu_get_custom_theme (Array, 4 elements)
3: menu_set_custom_theme (Array, 4 elements)
2: _drupal_bootstrap_full (Array, 4 elements)
1: drupal_bootstrap (Array, 4 elements)
Called from /sites/bar/modules/custom/foo/foo.module, line 367
... (Array, 20 elements)
20: load (Array, 7 elements)
19: entity_load (Array, 4 elements)
18: foo_load_multiple (Array, 4 elements)
17: foo_load (Array, 4 elements)
16: _menu_load_objects (Array, 4 elements)
15: _menu_translate (Array, 4 elements)
14: menu_local_tasks (Array, 4 elements)
13: menu_tab_root_path (Array, 4 elements)
12: menu_get_active_help (Array, 4 elements)
11: system_block_view (Array, 2 elements)
10: call_user_func_array (Array, 4 elements)
9: module_invoke (Array, 4 elements)
8: _block_render_blocks (Array, 4 elements)
7: block_list (Array, 4 elements)
6: block_get_blocks_by_region (Array, 4 elements)
5: block_page_build (Array, 4 elements)
4: drupal_render_page (Array, 4 elements)
3: drupal_deliver_html_page (Array, 4 elements)
2: drupal_deliver_page (Array, 4 elements)
1: menu_execute_active_handler (Array, 4 elements)
Called from /sites/bar/modules/custom/foo/foo.module, line 367
以前有人遇到过这个问题吗
提前谢谢
解决方案(感谢@Berdir):
使用attachLoad()代替重写DrupalDefaultEntityController::load():
在我看来,第二个调用实际上传递了一些无效的东西(一个对象而不是int/string) 请尝试“debug\u print\u backtrace()”或如果已安装devel.module,“ddebug\u backtrace()”,而不是调用error\u log()。这将为您提供callstack,以确定何时调用加载函数。请注意,第一个命令将打印大量原始文本,第二个命令更易于阅读 编辑:
与其重写load()方法,不如实现attachLoad()。对于新加载的实体,将只调用一次。例如,请参阅。我认为这也是我必须克服的默认行为。菜单对象为菜单加载一次,为获取帮助系统加载一次。但是,无论如何都应该静态缓存实体,并且应该支持多次调用foo_load()。所以问题是您的file_load()行。如果它已经加载,这就是您最终得到的结果。我会用一种方法更新我的答案。我现在正在使用快速修复,但显然我不想使用它。文件_load(string)在第二次加载()时导致错误,因为它试图加载已插入第一次加载()中的对象。奇怪的是,在第二次加载()时,picture对象已经插入到foo对象中。您可能会认为它是从parent::load中的数据库中选择的,但不知何故它使用了第一个load()中加载的对象(缓存?)。缺少实体api的文档也没有帮助:)。请参见上面的编辑,您希望使用attachLoad()而不是重写load()。
protected function attachLoad(&$foos, $revision_id = FALSE) {
foreach ($foos as $foo) {
$foo->picture = file_load($foo->picture);
}
parent::attachLoad($foos, $revision_id);
}