Drupal 7: адаптируем модуль от версии 6

Восьмая версия популярной CMS Drupal 8 набирает обороты, в разработке 9-я версия, а еще немало сайтов на 6-й версии. И причиной, порой тому является необходимость переписывать собственные модули, а это требует времени и знаний. Последними и хочу поделиться.

На странице https://www.drupal.org/update/modules/6/7 приведено около 250 изменений, рассказывающих об отличиях в коде 7-ки от 6-ки, но на английском. Я же расскажу про самые востребованные при конвертации модулей от 6-й версии в 7-ю. Но на русском.

Итак, поехали.
Свой модуль необходимо помещать в отдельную папку в папке sites/all/modules/custom при односайтовой конфигурации или в site_folder/modules/custom при мультисайтинге. Например: sites/all/modules/custom/mymodule.

Появилась удобная функция debug(variable, description) позволяющая выводить в удобном виде переменную/объект/массив. Например: debug($node, 'Tish node in function mymodule_view').

Нет необходимости в файле module.install явно указывать выполнение схемы для создания/удаления таблиц в базе, это выполняется автоматически. Но необходимо помнить, что действия по удалению материалов или других данных из системных таблиц в случае необходимости лежит на вас и выполняется в хуке hook_uninstall через sql-запрос.

Функции для работы с БД тоже изменились, примеры смотрите здесь.

Тело статьи body теперь необходимо подключать в файле module.install в хуке hook_install. Пример:

function mymodule_install() {
   	node_types_rebuild();
	$types = node_type_get_types();
	node_add_body_field($types['mymodule'], 'Type node description.');
	// Загрузка определения экземпляра для тела типа контента.
	$body_instance = field_info_instance('node', 'body', 'mymodule');
	// Настройка поля тела.
	$body_instance['type'] = 'text_summary_or_trimmed';
	// Сохранение изменений в экземпляре поля тела.
	field_update_instance($body_instance);
}

Соответственно, в хуке hook_form нет необходимости предусматривать вывод $node->body. К тому же $node->body теперь ассоциативный массив. Тело статьи будет выводиться автоматически. Если есть необходимость выводить body определенном образом в своем коде, то предварительно необходимо отключить его вывод в настройках типа материала в меню администрирования, причем отдельно для предпросмотра и полного просмотра. В настройках формы типа материала можно указать обязательность ввода значения в тело статьи.

Таблица node_revisions переименована в node_revision.

Хук hook_perm переименован в hook_permission.

В хуке hook_acces необходимо возвращать NODE_ACCESS_ALLOW.

Тело статьи из таблицы node_revision поля body переместилось в таблицу field_data_body в поле body_value.

Хук hook_link() больше не используется. Вместо этого ссылки под статьей указываем в hook_node_view(). Необходимо при этом проверять тип материала. Пример:

<?php
function blog_node_view($node, $view_mode, $langcode) {
  if ($node->type == 'blog') {
    if (arg(0) != 'blog' || arg(1) != $node->uid) {
      $links['blog_usernames_blog'] = array(
        // Unrelated to the change where links are added, Drupal 7 also adds a
        // format_username() function.
        'title' => t("!username's blog", array('!username' => format_username($node))),
        'href' => "blog/$node->uid",
        'attributes' => array('title' => t("Read !username's latest blog entries.", 
         array('!username' =>format_username($node)))),
      );
      $node->content['links']['blog'] = array(
        '#theme' => 'links__node__blog',
        '#links' => $links,
        '#attributes' => array('class' => array('links', 'inline')),
      );
    }
  }
}
?>

Если необходимо в ссылке указать дополнительные параметры, то они добавляются ассоциативным массовом в ключе 'query'. Пример:

$links['blog_usernames_blog'] = array(
        // Unrelated to the change where links are added, Drupal 7 also adds a
        // format_username() function.
        'title' => t("!username's blog", array('!username' => format_username($node))),
        'href' => "blog/$node->uid",
        'query' => array('parent' => $node->nid),
      );

Примечание: после добавления hook_node_view() необходимо переподключить модуль в настройках.

Изменен вызов node_view. Теперь возвращается массив. Необходимо использовать drupal_render. Пример:

Drupal 6:
<?php
$node->build_mode = NODE_BUILD_NORMAL;
$output = node_view($node, TRUE);
?>
 
Drupal 7:
<?php
$content = node_view($node, 'teaser');
$output = drupal_render($content);
?>

Хук hook_view теперь объявляется так:
hook_view($node, $view_mode, $langcode = NULL)
где $view_mode может быть full, teaser, rss и, возможно, другие, определенные в дополнительных модулях, значения.

Также, при объявлении функций theme ключ называется не value, а #markup. Затем необходимо ассоциативным массивом передать необходимые переменные и/или массивы. Пример:

$node->content['dgaldata'] = array(
	'#markup' => theme("mynode_view_chapter", 
	      array('state' => $node->state, 'image' => $node->image, 'body' => $node->body, 'nid' => $node->nid, 'pid' 
               => $node->pid, 'file' => $node->file)
				),
	'#weight' => 0,
	);

Вместо theme_table необходимо использовать theme('table', ...), к примеру:

theme('table', array('rows' => $tablarr)).

Изменилась работа с механизмом разбивки на страницы pager. Параметр $limit не используется, эта функция возложена на запрос к базе. Теперь при вызове theme('pager',...) используются только два параметра, где второй - ассоциативный массив и может быть опущен.

Drupal 6:
<?php
  theme('pager', $tags, $limit, $element, $parameters, $quantity);
?>
 
Drupal 7:
<?php
  theme('pager', array('tags' => $tags, 'element' => $element, 'parameters' => $parameters, 'quantity' => $quantity));
?>

Пример

 $select = $select->fields('n', array('nid', 'title', 'created'))
    ->fields('p', array('active'))
    ->condition('n.status', 1)
    ->orderBy('n.created', 'DESC')
    ->groupBy('n.nid')
    ->groupBy('n.title')
    ->groupBy('p.active')
    ->groupBy('n.created')
    ->extend('PagerDefault')
    ->limit($polls_per_page)
    ->addTag('node_access');
...
$output .= theme('pager'); 

Если после включение какого-то из модулей сайт перестает отвечать, то найдите в базе в таблице system строку с именем модуля и полем type=module_name, и в поле status поменяйте 1 на 0. Если модуль на чисто установленном Drupal включается и работает нормально, то попробуйте сделать следующее. Поищите в тестовой базе и в рабочей все вхождения машинного имени модуля, найдите те таблицы, где модуль упоминается в рабочей базе, но не упоминается в тестовой (кроме таблиц, содержащих материалы, таких как node_revision, field_data_body и т.д.) и удалите эти записи в рабочей базе. Если модуль использует собственную таблицу в базе, переименуйте ее. Установите модуль. Если все прошло успешно, удалите вновь созданную установщиком модуля таблицу, а рабочую переименуйте обратно.

Это были основные моменты на которые необходимо обратить внимание при адаптации модуля Drupal 6 к версии Drupal 7.
Удачи!