Drupal → Коротко о хуках hook_node_*

25.12.2012

hook_node_load($nodes, $types)

Вызывается после загрузки нод из БД или из кэша.

Пример использования:

function hook_node_load($nodes, $types) {
  $result = db_query('SELECT nid, foo FROM {mytable} WHERE nid IN(:nids)', array(':nids' => array_keys($nodes)));
  foreach ($result as $record) {
    $nodes[$record->nid]->foo = $record->foo;
  }
}

hook_node_delete($node)

Вызывается перед удалением ноды из БД.

Пример использования:

function hook_node_delete($node) {
  db_delete('mytable')
    ->condition('nid', $node->nid)
    ->execute();
}

hook_node_presave($node)

Вызывается перед добавлением или обновлением ноды в БД. При добавлении ноды её nid ещё недоступен.

Пример использования:

function hook_node_presave($node) {
  if ($node->nid && $node->moderate) {
    // Reset votes when node is updated:
    $node->score = 0;
    $node->users = '';
    $node->votes = 0;
  }
}

hook_node_insert($node)

Вызывается после добавления ноды в БД.

Пример использования:

function hook_node_insert($node) {
  db_insert('mytable')
    ->fields(array(
      'nid' => $node->nid, 
      'extra' => $node->extra,
    ))
    ->execute();
}

hook_node_update($node)

Вызывается после обновления ноды в БД.

Пример использования:

function hook_node_update($node) {
  db_update('mytable')
    ->fields(array('extra' => $node->extra))
    ->condition('nid', $node->nid)
    ->execute();
}

hook_node_prepare($node)

Вызывается перед открытием формы добавления/редактирования ноды.

Пример использования:

function hook_node_prepare($node) {
  if (!isset($node->comment)) {
    $node->comment = variable_get("comment_$node->type", COMMENT_NODE_OPEN);
  }
}

hook_node_validate($node, $form, &$form_state)

Вызывается во время валидации ноды перед добавлением/обновлением её в БД. Объект ноды недоступен к изменению.

Пример использования:

function hook_node_validate($node, $form, &$form_state) {
  if (isset($node->end) && isset($node->start)) {
    if ($node->start > $node->end) {
      form_set_error('time', t('An event may not end before it starts.'));
    }
  }
}

hook_node_search_result($node)

Вызывается перед выводом ноды в результатах поиска.

Пример использования:

function hook_node_search_result($node) {
  $comments = db_query('SELECT comment_count FROM {node_comment_statistics} WHERE nid = :nid', array('nid' => $node->nid))->fetchField();
  return array('comment' => format_plural($comments, '1 comment', '@count comments'));
}

hook_node_submit($node, $form, &$form_state)

Вызывается после валидации формы создания/редактирования ноды, но до добавления/сохранения ноды в БД.

Пример использования:

function hook_node_submit($node, $form, &$form_state) {
  // Decompose the selected menu parent option into 'menu_name' and 'plid', if
  // the form used the default parent selection widget.
  if (!empty($form_state['values']['menu']['parent'])) {
    list($node->menu['menu_name'], $node->menu['plid']) = explode(':', $form_state['values']['menu']['parent']);
  }
}

hook_node_view($node, $view_mode, $langcode)

Вызывается перед рендерингом (выводом) ноды. Элементы ноды, отображаемые на странице, доступны в свойстве $node->content в виде рендер-массива.

Пример использования:

function hook_node_view($node, $view_mode, $langcode) {
  $node->content['my_additional_field'] = array(
    '#markup' => $additional_field, 
    '#weight' => 10, 
    '#theme' => 'mymodule_my_additional_field',
  );
}

hook_node_view_alter(&$build)

Вызывается перед рендерингом (выводом) ноды. В отличии от hook_node_view, используется для изменения частей рендер-массива.

Пример использования:

function hook_node_view_alter(&$build) {
  if ($build['#view_mode'] == 'full' && isset($build['an_additional_field'])) {
    // Change its weight.
    $build['an_additional_field']['#weight'] = -10;
  }

  // Add a #post_render callback to act on the rendered HTML of the node.
  $build['#post_render'][] = 'my_module_node_post_render';
}

hook_node_access($node, $op, $account)

Вызывается во время проверки прав доступа к ноде (создание, удаление, просмотр, обновление). Хук не вызывается для главного администратора.

Пример использования.

В официальной документации можно посмотреть порядок выполнения хуков при различных событиях.

Написанное актуально для
Drupal 7
Похожие записи

Комментарии

Роман
25.12.2012, 12:26

Спасибо за полезную справку.

Интересно, а в чем логика разделения задачи обработки ноды перед её отображением на два хука - hook_node_view и hook_node_view_alter ?

Почему бы их не объединить в один хук, где можно было бы как добавлять новые элементы ноды, так и редактировать уже существующие ?

чтобы можно было изменять данные, добавленные модулями с бОльшим весом.
на самом деле есть ещё два "хука" — hook_preprocess_node и hook_process_node :)

Роман
25.12.2012, 12:48

То есть, получается, что сначала выполняются все хуки hook_node_view, а потом уже все хуки hook_node_view_alter ?

slivorezka
25.12.2012, 13:11

Вопрос, при использовании hook_node_view
Добавленый мною контент будеть отображаться в Manage display ноды?

dansamara
25.12.2012, 18:48

Не совсем корректно говорить о хуке node_insert как "Вызывается после добавления ноды в БД.". Есть небольшой, но важный нюанс - он высывается внутри транзакции создании ноды, что влечёт за собой во-первых возможный её откат, а во-вторых - отсутствие данных в БД о ноде. Было у меня пару случаев, когда это было сложно разрешимой проблемой.

dansamara
25.12.2012, 18:51

Вообще не понимаю почему нельзя вызывать хук node_insert реально после создании ноды.

мне кажется логичным, что если транзакция откатится, то и изменения сделанные в hook_node_insert откатятся

dansamara
25.12.2012, 21:31

Да, так сейчас и сделано. Это логично для текущей реализации. Но я говорю про другое - если транзакция откатилась и нода не создалась, то тогда и не надо вызывать hook_node_insert. Если же всё ок - то вызываем hook_node_insert.

А по-моему всё как раз логично: есть _presave() в котором подготовливаются данные, далее _insert()/_update() в котором уже есть NID и пытаемся добавить/изменить свою запись в базе.
Транзакция откатывается целиком, если любой из модулей не смог сохранить свои данные - иначе это уже не транзакция!

Если нода не создалась/сохранилась (drupal_write_record() ...) то и хук не вызовется - внимательно смотрим код node_save()

Я так понимаю что хуки
hook_node_view
hook_node_view_alter
hook_preprocess_node
hook_process_node

Делают одно и тоже,причем можно использовать и в теме и в модуле...а как выбирать нужный хук,по какому признаку?

hook_node_view[_alter] не доступны в темах. выбирать исходя из задачи

Пример: разбил содержание ноды на вкладки с помощью field_group , хочу перенести комментарии в одну из вкладок. какой из хуков использовать в template.php ? Точее больше интересует исходя из чего я должен выбрать хук

Подскажите, пожалуйста, как отменить проверку на заполнение обязательных полей при удалении ноды?

Т.е., например, фото - обязательное поле, пользователь удаляет сначала фото, потом пытается удалить саму ноду и ему выдает ошибку, что обязательные поля не заполнены. Хочется отменить все проверки CCK на тапе удаления.
Drupal 6.

Спасибо!

Игорь
26.10.2013, 22:20

Доброго времени суток!
У меня такая проблемка - нужно чтобы при сохранении ноды, image перезаписывался, а не добавлялся счетчик (_n), так как фотки одинаковые. Что делать при удалении это потом...
Либо в hook_node_presave - удалить перезаписанный файл и записать со своими настройками или хочется посмотреть в сторону - https://api.drupal.org/api/drupal/modules!field!field.attach.inc/functi…
особенно вот это - _field_invoke_default('extract_form_values', $entity_type, $entity, $form, $form_state);
Как это работает?
Заранее спасибо за ответ.

Игорь
27.10.2013, 01:39

Попробую по подробней. Вообще то это для commerce_product, но для ноды это как то привычней. Вообщем есть тип материала, у него поля разные и поле field_image.
штук по 20 нод - одна и та же картинка прикрепляется, ну и вместо 1Гб - 20 Гб всяких дублей. Вот как при сохранении перехватить вызов функции file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAME) (видимо она там вызывается) то есть файл.jpg второй такой же станет файл_0.jpg потом _1 и т.д. Но это один и тот же файл. И мне хотелось бы чтобы $replace = FILE_EXISTS_REPLACE. Можно тупо влезть в ядро и там перезаписать, но мне это надо только для одного типа.
Единственное, как потом при удалении ноды не удалять файл.jpg, если есть ссылка(id - fid) на этот же файл в другой ноде, но это потом...

сравнение файлов идёт только по имени? это как-то не очень разумно

Роман
27.10.2013, 09:34

А может здесь подойти по-другому ?
Например, сделать, чтобы при работе с картинкой использовался соответствующий менеджер картинок (типа IMCE или ElFinder), где пользователь сам смог бы при необходимости выбрать одну и ту же картинку для разных товаров ?

Игорь
27.10.2013, 15:31

Сделал так(работает!):

/**
 * Implements hook_entity_insert($entity, $type) 
 * Вызывается после добавления новой сущности
 */
function mymodule_entity_insert($entity, $type) {
  if($type == 'commerce_product') {
    $fid = $entity->field_images[LANGUAGE_NONE][0]['fid'];
    $file = file_load_multiple(array($fid));
    $file = reset($file);
    $existing_files = file_load_multiple(array(), array('filename' => $file->filename, 'filesize' => $file->filesize));
    if (count($existing_files) > 1) {
      $array = (array) reset($existing_files);
      $entity->field_images[LANGUAGE_NONE][0] = $array + $entity->field_images[LANGUAGE_NONE][0];
      entity_save($type, $entity);
      file_delete($existing_files[$fid]);
    }          
  }  
}
graceman9
16.12.2013, 18:36

Мне нужно загрузить данные к ноде, но они нужны только при просмотре одной ноды. Где мне их загружать?
(в моём случае это множество "атрибутов")

Игорь
16.12.2013, 19:46

В блоке
только на страницах
node/id

graceman9
17.12.2013, 13:48

Игорь, меня интересуют hook_node_load() и hook_node_view().
Вообщем то понятно что hook_node_load() используется и для множества и для одной ноды.
Но непонятно зачем загружать для множества нод то, что будет показано только для одной? т.е. где там что-то вроде $view_mode?

alborodin85@mail.ru
15.06.2014, 14:56

Если обновить значение поля внутри hook_node_update($node) следующим образом:
$node->field_height['und'][0]['value'] = $new_value;
, то значение field_height_value в таблице field_data_field_height базе данных не обновится.

С другой стороны, если внутри hook_node_update($node) изменить значение поля field_height_value прямо в базе данных, например средствами API баз данных или каким-либо другим образом, то новое значение сохранится.

Это говорит о том, что событие node_update возникает не перед обновлением БД, а после. Если бы этот хук был перед обновлением, то должно быть наоборот.

(хотя могу и ошибаться, для подобного поведения могут быть какие-то другие причины, но обновить поле внутри этого хука получилось только напрямую в базе).

я ошибся, hook_node_update вызывается после обновления.
значения полей можно менять в hook_node_presave

Mihon_kri
18.12.2014, 17:07

Встала такая задача:создал тип материала - "товар на складе" с полями ID товара, название товара, дата добавления товара, место хранения товара (предполагается, что будет веб-интерфейс учета товара на складе). Нужно, что бы при заведении ID товара, из отдельной таблицы с каталогом товара подтягивалось название товара (заводить будет кладовщик сканируя штрих-код, потому и надо подтягивать название). Таблица, из которой должно подтягиваться название товара простая (id товара, название товара, артикул и количество товара). hook_node_insert тут может подойти? Если можно, то киньте пример на русском.

Игорь
18.12.2014, 22:17

Mihon_kri - не проще ли комерц поставить, там всё есть и даже больше, правда потом пол года его понимать и настраивать, но это уже издержки ;-)

Ребят, нужно после обновления ноды проверять одно из полей, если оно не изменилось то ничего не делать, если изменилось то отправлять письмо.

Используя hook_node_update я могу отправить письмо, но не могу проверить каким было это поле до сохранения.

Есть hook_node_presave, но теперь встает вопрос, как передать значение моего поля из одного хука в другой?
hook_node_presave -> hook_node_update

Спасибо

Бородин
22.02.2015, 23:18

Как вариант можно создать класс со статическим свойством и использовать это свойство как аналог некой глобальной переменной.
В hook_node_presave это свойство устанавливать, а в hook_node_update - считывать.
Класс можно описать прямо в файле вашего модуля.

Бородин
22.02.2015, 23:24

И еще есть вариант - возможно будет работать - в druapl'е есть пара функций: что-то типа variable_set и variable_get (в синтаксисе могу ошибаться). Ими можно просто глобальные переменные устанавливать-считывать.
Но я на практике не пробовал - всегда использовал статические свойства, но может этот вариант где-то будет даже правильнее.

@Али в hook_node_presave можно вычислить разницу массива с $node->original. И если в массиве поле присутствует - отправлять письмо.

Здравствуйте!

В своем модуле добавляю сабмит который выполняется после основного и программно создает термин из поля ноды title. Стоит задача прицепить ссылку на этот термин к создаваемой ноде. Получить TID из объекта созданного термина нет проблем, а вот как вытянуть NID из создаваемой ноды ... .
Я не профи в РНР (хотя кое какие понятия имею), а вдобавок и в доках к hook_node_insert все как то туманно описано.

Пробовал. Нет значения. Код вставлял в свой сабмит, который выполнялся после основного.
Ставил метку из drupal_set_message до и после кода. Метки есть, значения нет. А после всех меток сообщение "Материал "Вася Пупкин" создан".

Разобрался. Вытаскивать nid нужно было не в сабмите, а в самом хуке hook_node_insert.
Протупил, что впрочем и неудивительно. Благодарю за помощь.

Приветствую
А как выполнить код после удаления ноды, до не катит?

Евгений
25.07.2016, 11:28

А как выполнить код после удаления ноды, до не катит?

Не понял вопроса.

>hook_node_delete($node)
>Вызывается перед удалением ноды из БД.

А нужно после удаления выполнить некий код

Добавить комментарий