Как программно получить ноду

Опубликовал Максим Баев, 13 ноября 2014, 11:17

Для программного получения (загрузки) ноды есть как минимум 2 пути и нужно понимать в каких случаях что использовать.

1-ый вариант (node_load, entity_load)

Использование функции node_load гарантирует безошибочную загрузку ноды, при этом если мы попытаемся 100 раз загрузить одну и туже ноду, то она будет загружена 100 раз со всеми вытекающими: загрузка данных из базы и формирование entity.(см. Upd: 07.11) В Drupal 7, entity интегрирован в ядро, по этому node_load(1) равнозначно entity_load('node', array(1))

<?php
function node_load($nid = NULL, $vid = NULL, $reset = FALSE) {
  $nids = (isset($nid) ? array($nid) : array());
  $conditions = (isset($vid) ? array('vid' => $vid) : array());
  $node = node_load_multiple($nids, $conditions, $reset); // загрузка ноды
  return $node ? reset($node) : FALSE;
}

function node_load_multiple($nids = array(), $conditions = array(), $reset = FALSE) {
  return entity_load('node', $nids, $conditions, $reset); // вот этот момент
}

2-ой вариант (menu_get_object)

Об этой функции подробнее. Сразу скажу, что через эту функцию можно загружать не только ноды, но и aggregator_feed, aggregator_category и т.д.

menu_get_object($type = 'node', $position = 1, $path = NULL)
Core provides aggregator_feed, aggregator_category, contact, filter_format, forum_term, menu, menu_link, node, taxonomy_vocabulary, user. See the relevant {$type}_load function for more on each.

Этот вариант интересен тем, что он кэширует каждую загруженную ноду и, соответственно, если мы будем загружать ноду 100 раз через эту функцию, то загружена нода будет один раз. (см. Upd: 07.11) Если мы попытаемся так загрузить из функции, которая указана в метаданных хука hook_menu, то получим зацикливание скрипта.

Почему так? А вот почему: использование menu_get_object говорит о том, что нода должна быть загружена на основе роутинга. Пример:

<?php
$node = menu_get_object(); // по умолчанию возвращается текущая нода
<?php
function node_menu(){
  //...
  $items['node/%node'] = array(
    'title callback'   => 'node_page_title',
    'title arguments'  => array(1),
    // The page callback also invokes drupal_set_title() in case
    // the menu router's title is overridden by a menu link.
    'page callback'    => 'node_page_view',
    'page arguments'   => array(1),
    'access callback'  => 'node_access',
    'access arguments' => array('view', 1),
  );
  //...
}

Если бы мы сделали так:

<?php
function node_page_title($node){
  $node = menu_get_object(); // добавили эту строку
  return $node->title;
}

То было бы зацикливание скрипта, т.к. menu_get_object() запускает node_menu(), а тот, в свою очередь, запускает node_page_title(), в котором запускается menu_get_object(). menu_get_object_cycle.png
Таким образом использовать menu_get_object() желательно везде, кроме функций указанных в метаданных к какому-либо пути хука меню. (в функции 'page callback' использовать можно)

И еще пример:

<?php
$type         = 'node';
$node_id      = 2;
$node_path    = 'node/' . $node_id;
$arg_position = 1; // отсчитывается от 0.

$node = menu_get_object($type, $arg_position, $node_path);

Upd 07.11.14: Из комментов:

  1. menu_get_object так же использует node_load()
  2. node_load() кэширует ноды.

Другие посты