Цей блог написаний розробниками для розробників. Якщо ви
хочете почитати про Drupal 7 в 8 у більш "легкому" стилі,
пропонуємо вам перейти на інший блог із презентацією.
Всім привіт! Ще зовсім недовго лишилося чекати до виходу Drupal 8, але вже зараз є можливість використовувати його бета версію. Отож, давайте разом вивчати Drupal 8.
Створення сторінок та пунктів меню
Мабуть, у кожного першим хуком, який прийшлось реалізовувати, був hook_menu(). Але, як би не було важко в це повірити, hook_menu() вилучений із Drupal 8. Основним завданням hook_menu() є створення пунктів меню і сторінок. У новій версії на зміну hook_menu() прийшла нова система маршрутизації. Для створення сторінки у Drupal 8 необхідно створити у папці модуля файл your_module.routing.yml. Вміст цього файлу виглядає приблизно так:
example_mod.mypage: path: '/cust-page' defaults: _controller: '\Drupal\example_mod\Controller\ExampleModuleController::pagecontent' _title: 'My page' requirements: _permission: 'idevels example conf'
- example_mod.mypage - назва маршруту
- path - шлях сторінки. Можна використовувати динамічні властивості за допомогою фігурних дужок (наприклад, path: '/cust-page/{node}' )
- _controller - метод, що відповідає за відображення сторінки
- _title - заголовок сторінки
- permission - права, які необхідні для доступу
Наступним кроком є створення контролера сторінки. Для цього створюємо файл src/Controller/ExampleModuleController.php
Вміст файлу:
'markup', '#markup' => t('My first page!!!'), ); return $build; } }
Ось таким буде результат:
Варто зауважити, що назва класу контролера та його методу має співпадати із тими, які вказані в your_module.routing.yml.
У Drupal 7 для створення пункту меню у hook_menu потрібно було додати такий рядок: 'type' => MENU_NORMAL_ITEM . Виглядало це приблизно так:
function your_module_menu(){ $items = array(); $items['page_example'] = array( 'title' => 'Title', 'description' => '', 'page callback' => '_page_example', 'access callback' => TRUE, 'type' => MENU_NORMAL_ITEM, 'menu_name' => 'main-menu', // вказуємо меню у якому буде створено посилання на сторінку ); return $items; }
Щоб створити пункт меню для сторінки у Drupal 8, необхідно створити файл із назвою your_module.links.menu.yml.
Вміст цього файлу виглядає приблизно так:
example_mod.mypage: title: 'Link to my page' description: 'example' route_name: example_mod.mypage weight: 100 #If menu_name is omitted, the "Tools" menu will be used. menu_name: main
Результат вищевказаних дій:
Створення блоків
У Drupal 7 для створення блоків використовувались хуки hook_block_info та hook_block_view. Виглядало це приблизно так:
/**
* Implements
hook_block_info(). */ function your_module_block_info() { $blocks = array(); $blocks['BLOCK1'] = array( 'info' => t('BLOCK NAME'), ); return $blocks; } /** * Implements hook_block_view(). */ function your_module_block_view($delta = '') { $block = array(); switch ($delta) { case 'BLOCK1': $block['subject'] = ''; $block['content'] = t(‘Block content’); break; } return $block; }
У Drupal 8 ці хуки видалено. Отож, щоб створити блок, необхідно створити файл your_module/src/Plugin/Block/MyBlock.php. Вміст файлу виглядає так:
<?php /** * @file * Contains \Drupal\internetdevels\Plugin\Block\AboutLvivBlock. */ namespace Drupal\example_mod\Plugin\Block; use Drupal\Core\Block\BlockBase; /** * @Block( * id = "myblock", * admin_label = @Translation("MyBlock") * ) */ class MyBlock extends BlockBase { public function build() { return array( '#markup' => '<div class="myblock1"> <b><p>Creating a custom block defined by your module involves the following steps:</p></b> <li>Create a block plugin using annotations</li> <li>Extend the Drupal\Core\Block\BlockBase class.</li> <li>Implement the necessary methods from the Drupal\Core\Block\BlockPluginInterface interface depending on your use case</li> </div>', ); } }
Отже, для створення блоку необхідно:
- створити плагін блоку, використовуючи анотації
- у класі блоку зробити наслідування від класу Drupal\Core\Block\BlockBase
- реалізувати необхідні методи інтерфейсу Drupal\Core\Block\BlockPluginInterface залежно від вашого випадку
Необхідно звернути увагу на рядки 13-14. В них вказуємо ідентифікатор та заголовок блоку.
Програмне виведення блоку на сторінці можливе таким способом:
'markup', '#markup' => $block['#markup'], ); return $build; } }
Реалізація функціоналу hook_init() на Drupal 8 за допомогою EventSubscriber
У Drupal 8 вилучено hook_init(). Якщо модуль повинен виконувати зміни досить рано у відповідь на запит, потрібно використовувати обробники подій.
Ось список подій ядра Drupal 8:
- KernelEvents::CONTROLLER - подія контролера, що відбувається один раз, коли контролер був знайдений для обробки запиту.
- KernelEvents::EXCEPTION - подія винятків, що відбувається, коли з'явиться неперехоплений виняток.
- KernelEvents::FINISH_REQUEST - відбувається, коли згенерована відповідь для запиту.
- KernelEvents::REQUEST - подія, яка відбувається на початку відправлення запиту.
- KernelEvents::RESPONSE - відбувається, коли створена відповідь на запит.
- KernelEvents::TERMINATE - подія припинення, що відбувається, коли запит відправлений.
- KernelEvents::VIEW - відбувається, коли значення, яке повертається контролером, не є об’єктом відповіді.
Давайте зробимо редірект із сторінки My page (/cust-page) на сторінку My page (/pagewithblock), щоб подивитися, як це все працює.
Спочатку потрібно створити файл сервісів your_module.services.yml:
services: example_mod_subscriber: class: Drupal\example_mod\EventSubscriber\ExampleSubscriber tags: - { name: event_subscriber }
Потім створити файл your_module/src/EventSubscriber/MyExampleSubscriber.php, у якому будемо обробляти події.
Вміст цього файлу виглядає наступним чином:
getRequest()->getPathInfo() == '/cust-page') { $url = \Drupal::url('example_mod.mypage2'); $event->setResponse(new RedirectResponse($url)); } } /** * {@inheritdoc} */ static function getSubscribedEvents() { $events[KernelEvents::REQUEST][] = array('checkForRedirection'); return $events; } }
Метод getSubscribedEvents() повертає масив імен подій. Ключами масиву є імена подій, а значеннями можуть бути:
- ім'я методу для виклику (за замовчуванням пріоритет 0)
- масив, який складається з імені методу для виклику і пріоритету масив
- масивів з імен методів і їхніх пріоритетів, або пріоритет = 0, якщо не встановлено
Метод checkForRedirection(GetResponseEvent $event) - метод, який викликається відповідно на встановлену подію.
У рядку 16 провіряємо адресу сторінки, на яку здійснюємо перехід. За допомогою стрічки 17 формуємо url сторінки, на яку хочемо зробити редірект (example_mod.mypage2 - назва маршруту сторінки, на яку здійснюємо редірект). У рядку 18 встановлюємо відповідь на подію, у нашому випадку це об’єкт класу RedirectResponse з посиланням у якості параметра.
Підключення файлів скриптів та стилів css у Drupal 7 зручно здійснювати через hook_init(). Виглядає це так:
function your_module_init() { drupal_add_js(drupal_get_path('module','module_name') . '/script.js'); drupal_add_css(drupal_get_path('module','module_name') . '/style.css'); }
У Drupal 8 підключення скриптів, стилів та бібліотек можна здійснити за допомогою hook_page_attachments(). Спочатку треба створити файл з описом бібліотек your_module/your_module.libraries.yml
my_lib: version: 1.x css: theme: css/example-style.css: {} my_lib: - назва бібліотеки version: 1.x - версія css: - вказуємо, що підключаємо css css/example-style.css: {} - шлях до необхідного файлу
Потім реалізуємо hook_page_attachments():
function example_mod_page_attachments(array &$attachments) { $attachments['#attached']['library'][] = 'example_mod/my_lib'; }
Як бачимо, бібліотека успішно підключилась. Також можна об’єднувати в одну бібліотеку скрипти та файли стилів.
Приклад:
my_lib: version: 1.x css: theme: css/example-style.css: {} js: your_script.js: {}
Детальнішу інформацію можна знайти тут.
Створення форм
Для створення форм у Drupal 7 використовувався hook_form(). Виглядало це якось так:
function example_form($form, &$form_state) { $form['fieldset'] = array( '#type' => 'fieldset', '#title' => t('firstcolumn'), '#collapsible' => TRUE, '#collapsed' => FALSE, '#description' => t('Field description'), ); $form['fieldset']['field1'] = array( '#type' => 'textfield', '#title' => t('Field title'), '#size' => 40, '#maxlength' => 50, '#required' => TRUE, '#element_validate' => array('example_form_validate'), ); $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'), '#prefix' => '<div class="form-actions"> ', '#suffix' => '</div>', '#tree' => TRUE, ); return $form; }
У Drupal 8 форми створюються іншим шляхом. Отож давайте розглянемо, як створити форму у Drupal 8.
Спочатку потрібно створити файл your_module/src/Form/MyExampleForm.php, у якому власне і буде створюватися форма:
'email', '#required' => TRUE, ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Submit'), '#name' => 'form-submit', ); return $form; } /** * {@inheritdoc} */ public function validateForm(array &$form, FormStateInterface $form_state) { //тут відбувається валідація введених даних } /** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { //дії при відправленні форми } }
У методі getFormId() повертаємо ідентифікатор форми. За створення форми відповідає метод buildForm(), де саме й відбувається створення її елементів. Валідацію форми потрібно здійснювати у методі validateForm().
Далі в файл your_module.routing.yml додаємо наступні рядки:
example_mod.form: path: '/myform' defaults: _form: '\Drupal\example_mod\Form\MyExampleForm' _title: 'Example form' requirements: _permission: 'idevels example conf'
Детальнішу інформацію по створенню форм у Drupal 8 можна знайти тут і тут.
hook_cron() у Drupal 8
Також хотілося б привести простий приклад роботи hook_cron() на Drupal 8. Нехай крон усім новим нодам до тайтла додає в дужках nid цієї ноди. Такий приклад я обрав, щоб вбити двох зайців одразу. По-перше, побачимо, як працює крон, а по-друге — навчимося отримувати і змінювати значення полів нод.
Ось такий вигляд має реалізація хуку:
function example_mod_cron() { $nodes[] = node_load_multiple(); $expires = \Drupal::state()->get('example_mod.cron_last_run', REQUEST_TIME); foreach ($nodes[0] as $node) { $nid = $node->nid->value; $time = $node->created->value; $title = $node->title->value; if ($time > $expires) { $new_title = $title . '(' . $nid . ')'; $node->setTitle($new_title); $node->save(); } } \Drupal::state()->set('example_mod.cron_last_run', REQUEST_TIME); }
Давайте детальніше розберемося, який рядок за що відповідає. У рядку (2) створюємо масив, у який за допомогою функції node_load_multiple() завантажуємо усі ноди. За допомогою стрічки (3) отримуємо час останнього запуску крона. У стрічках (5), (6), (7) отримуємо значення потрібних полів. За допомогою стрічки (10) встановлюємо потрібне значення тайтла ноди, у рядку (11) зберігаємо ноду за допомогою методу save(). І, нарешті, встановлюємо в значення останнього запуску крона поточний час. Як бачимо, все дуже просто!
Детальнішу інформацію про hook_cron() у Drupal 8 можна знайти тут.
Використовуємо власний twig шаблон з допомогою hook_theme()
Як відомо, у Drupal 8 замість шаблонів Phptemplate використовуються Twig шаблони. Отож, розглянемо, як з допомогою hook_theme() змусити Друпал використовувати власний шаблон. Для прикладу давайте замінимо шаблон 'block__system_menu_block на свій, назвемо його custom-menu-template.html.twig. Виглядає встановлення власного шаблону наступним чином:
function bartik_theme($existing, $type, $theme, $path) { return array( 'block__system_menu_block' => array( 'template' => 'custom-menu-template', ), ); }
За замовчуванням вимкнена можливість дебажити файли шаблонів. Щоб увімкнути цю можливість, необхідно у файлі sites/default/services.yml встановити:
twig.config: debug: true
Для виведення змінних можна використовувати модуль Devel. Тепер можна виводити інформацію про змінні за допомогою функцій dump() та kint(). kint() виводить інформацію у досить читабельному для програміста вигляді:
Сподіваємось, ці поради допоможуть вам у роботі з Drupal 8 та зроблять процес написання коду легшим та ефективнішим!
PS. Приклади модулів ви можете знайти в доданому файлі.