2011-03-07 2 views
2

В последнее время я создаю модуль Drupal 7 с API-интерфейсом Entity, но я столкнулся с проблемой. При отладке формы редактирования я заметил, что метод загрузки вызывается дважды, что вызывает ошибку.Метод сущности загрузки дважды

Recoverable fatal error: Object of class stdClass could not be converted to string in DatabaseStatementBase->execute() (regel 2039 van /drupal7/includes/database/database.inc).

Это вызвано тем, что FooController :: load выполняется дважды.

Под кодом, который я использовал.

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; 
    } 
    ... 
} 

Edit: CallStack

... (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 

ли кто-нибудь испытал эту проблему раньше?

Заранее спасибо.

Solution (благодаря @Berdir):

Вместо перекрывая DrupalDefaultEntityController :: нагрузки(), с помощью attachLoad():

protected function attachLoad(&$foos, $revision_id = FALSE) { 
    foreach ($foos as $foo) { 
     $foo->picture = file_load($foo->picture); 
    } 
    parent::attachLoad($foos, $revision_id); 
    } 

ответ

2

Он смотрит на меня, как и второй вызов на самом деле передавая что-то недействительное (объект вместо int/string).

Вместо этого вызова error_log() попробуйте «debug_print_backtrace()» или если у вас установлен devel.module, «ddebug_backtrace()». Это даст вам вызов, чтобы выяснить, кто именно вызывает функцию загрузки, когда. Обратите внимание, что первая команда будет печатать много необработанного текста, вторая - легче читать.

Редактировать: Вместо того, чтобы переопределять метод load(), вместо этого вы должны реализовать attachLoad(). Это будет вызываться только один раз для вновь загруженных объектов. См. Пример http://api.worldempire.ch/api/privatemsg/privatemsg.module/function/PrivatemsgMessageController::attachLoad/7-1.

+0

Добавлен стек вызовов! – Bart

+1

Я думаю, что это поведение по умолчанию, с которым мне пришлось сражаться уже и так. Объекты меню загружаются один раз для меню и один раз, чтобы получить справочную систему. Тем не менее, объекты должны быть статически кэшированы в любом случае, и необходимо многократно вызывать foo_load() несколько раз. Таким образом, проблема заключается в вашей строке file_load(). Если он уже загружен, это то, что вы в итоге получаете. Я обновлю свой ответ, чтобы исправить это. – Berdir

+0

Я использую quickfix в данный момент, но, очевидно, я бы предпочел не использовать его. Файл_страница (строка) вызывает ошибку при втором загрузке(), поскольку она пытается загрузить объект, который уже вставлен в первую загрузку(). Кажется странным, что во второй загрузке() объект изображения уже вставлен в объект foo. Вы думаете, что он будет выбран из базы данных в parent :: load, но каким-то образом он использует объект, загруженный в первый load() (кеширование?). Отсутствие документации по объекту api тоже не помогает :). – Bart