Створення модулів у Drupal 8

05.07.2016
Створення модулів у Drupal 8
Автор:

Занурюємось у світ створення модулів! Стаття для
Drupal-розробників від Drupal-розробника InternetDevels.

У цій статті ми розглянемо процес створення модуля під Drupal 8. А саме: ми створимо дві сторінки, одна з яких додаватиме дані в "свою" табличку, а друга — відображатиме її вміст в потрібному для нас вигляді. Отже, почнемо.

Налаштування середовища

Перше, що я б порадив зробити, — це налаштувати оточення для простоти написання коду. Я використовую phpstorm, тож ось посилання для його налаштування: https://confluence.jetbrains.com/display/PhpStorm/Drupal+Development+using+PhpStorm

Створення директорії для модуля

Після налаштування середовища нам потрібно створити папку для модуля по шляху /modules/custom/mypage (в Drupal 7 модулі лежать /sites/all/modules).

Створення .info файлу

Далі нам потрібно придумати назву модуля і створити файл для опису модуля. Я назвав mypage, отже, файл назвемо mypage.info.yml (в Drupal 7 це mypage.info).

Ось що у мене вийшло:

 # Назва модуля.
name: My Page

# Детальний опис модуля.
description: Create page in drupal 8

# Вказуємо, в якій групі модулів відображатиметься наш модуль на сторінці /admin/modules.
package: Custom

# Вказуємо, що це модуль. Буває(module, theme, profile).
type: module

# Ключ, що визначає версію ядра. Обов’язковий!!!
core: 8.x

# Тут вказується назва роута (який оголошується в файлі mypage.routing.yml) для конфігурації модуля.
configure: mypage.add_record

# Також можуть бути наступні значення:
# dependencies - список модулів, від яких залежить ваш модуль.
# test_dependencies - список модулів, які будуть включатися при виконанні автоматизованих тестів.
# hidden: TRUE - опція приховує цей модуль із списку модулів.
# Щоб показати всі приховані модулі в списку, потрібно прописати $settings['extension_discovery_scan_tests'] = TRUE в settings.php.

Детальний опис усіх властивостей можна подивитися тут: https://www.drupal.org/node/2000204

Створення файлу .module

В Drupal 7 файл .module був обов'язковим, а в Drupal 8 його може і не бути. У цьому файлі я створюю свою тему для потрібного нам відображення даних.

 <?php

/**
* Implements hook_theme().
*/
function mypage_theme() {
 return array(
   'mypage_theme' => array( // Назва теми
     'variables' => array( // Масив назви змінних
       'data' => array(),
     ),
     'templates' => 'templates/mypage-theme', // Шлях до шаблону без .html.twig, в якому і будуть доступні змінні, що вказані вище
   ),
 );
}

В Drupal 8, на відміну від Drupal 7, не можна створювати тему, яка віддаватиме html — все лежить у шаблоні TWIG.

Створення шаблону TWIG

В Drupal 8 змінився шаблонізатор. Замість звичного нам PHPtemplate використовується TWIG. Всі відмінності та інформацію про те, як з ним працювати, можна знайти тут: https://www.drupal.org/theme-guide/8/twig

Оскільки в темі ми вказували шлях 'templates/mypage-theme', відповідно, в корені папки модуля створюємо папочку templates і в ній створюємо файлик mypage-theme.html.twig, в якому, власне, і лежатиме наш HTML.

 <div class="row column text-center">
 <h2>{{ data.title }}</h2>
 <hr>
 <p>{{ data.body }}</p>
</div>

Тут якраз видно роботу зі змінною data, яку вказували в hook_theme ().

Створення src дерикторії

Далі нам потрібно створити підкаталог в папці нашого модуля, в якому ми будемо зберігати контролери, плагіни, форми, шаблони і тести. Цей підкаталог повинен називатися src. Це дозволить класу контролера додатися в автозавантаження (PSR-4 http://www.php-fig.org/psr/psr-4/) автоматично, відповідно, вручну підключати нічого не потрібно.

Створення таблиці в БД

Створення таблиць ніяк не змінилося — в Drupal 8 все той же hook_shema(). Всі типи для таблиць можна подивитися тут https://www.drupal.org/node/159605

Власне, створюється файл в корені модуля mypage.install, який відповідає за дії при встановленні або update.

Для тесту я створив табличку з 3 полями, в які буду записувати дані з конфіг форми, про яку напишу нижче.

 <?php

/**
* @file
* Install, update and uninstall functions for the mypage module.
*/

/**
* Implements hook_schema().
*/
function mypage_schema() {
 $schema['mypage'] = array(
   'description' => 'Custom mypage table.',
   'fields' => array(
     'id' => array(
       'type' => 'serial',
       'unsigned' => TRUE,
       'not null' => TRUE,
     ),
     'title' => array(
       'description' => 'Title page',
       'type' => 'varchar',
       'length' => 255,
       'not null' => TRUE,
       'default' => '',
     ),
     'body' => array(
       'description' => 'Body page',
       'type' => 'text',
       'not null' => TRUE,
       'size' => 'big',
     ),
   ),
   'primary key' => array('id'),
 );
 return $schema;

Створення сервісу

У ранніх версіях Друпала при рендері сторінки ініціалізувалися всі функції і методи незалежно від того, використовуємо ми їх чи ні. Це впливало на швидкість роботи програми. Так ось, в Друпал 8 з'явилося таке поняття, як services. Воно запозичене у Symphony і дозволяє викликати ті функції, які ми використовуємо при написанні коду.

Для створення сервісу потрібно створити файл з ім'ям mypage.services.yml, в якому і опишемо власне сам сервіс.

 services:
 // Назва сервісу.
 mypage.db_logic:
   // Клас, який повертає сервіс.
   // Оскільки в Drupal 8 використовується автозавантажувач PSR-4, то src пропускаємо.
   class: Drupal\mypage\MyPageDbLogic
   // Аргументи, які прилетять в в конструктор класу.
   arguments: ['@database']
   // Детальніше пояснення: https://www.drupal.org/node/2239393.
   tags:
     - { name: backend_overridable }

Детальний опис можна подивитися тут https://www.drupal.org/node/2133171

Цей сервіс потрібен нам для роботи з таблицею, яку створили вище. Аргумент @database дає можливість працювати з БД.

Виклик сервісу в коді

 $db_logic = \Drupal::service('mypage.db_logic');

Клас для сервісу

 <?php

namespace Drupal\mypage;

use Drupal\Core\Database\Connection;

/**
* Defines a storage handler class that handles the node grants system.
*
* This is used to build node query access.
*
* @ingroup mypage
*/
class MyPageDbLogic {

 /**
  * The database connection.
  *
  * @var \Drupal\Core\Database\Connection
  */
 protected $database;

 /**
  * Constructs a MyPageDbLogic object.
  *
  * @param \Drupal\Core\Database\Connection $database
  *   The database connection.
  */
 // Змінна $database прилетіла до нас з аргументу сервісу.
 public function __construct(Connection $database) {
   $this->database = $database;
 }

 /**
  * Add new record in table mypage.
  */
 public function add($title, $body) {
   if (empty($title) || empty($body)) {
     return FALSE;
   }
   // Приклад роботи з БД в Drupal 8.
   $query = $this->database->insert('mypage');
   $query->fields(array(
     'title' => $title,
     'body' => $body,
   ));
   return $query->execute();
 }

 /**
  * Get all records from table mypage.
  */
 public function getAll() {
   return $this->getById();
 }

 /**
  * Get records by id from table mypage.
  */
 public function getById($id = NULL, $reset = FALSE) {
   $query = $this->database->select('mypage');
   $query->fields('mypage', array('id', 'title', 'body'));
   if ($id) {
     $query->condition('id', $id);
   }
   $result = $query->execute()->fetchAll();
   if (count($result)) {
     if ($reset) {
       $result = reset($result);
     }
     return $result;
   }
   return FALSE;
 }

}

Створення роутинга

У Друпал 8 hook_menu замінили роутингами і винесли це все в окремий файл. Файл назвали mypage.routing.yml

 // Назва роутинга, його використовують для генерації посилань, редиректу і так далі.
mypage.add_record:
 // Шлях, який буде на сайті.
 path: '/admin/mypage/add_record'
 defaults:
 // Заголовок сторінки
   _title: 'Add record'
   // Відображення на сторінці форми. Аналог drupal_get_form
   _form: '\Drupal\mypage\Form\ConfigFormMyPage'
 requirements:
 // Права
   _permission: 'access simple page'

mypage.view:
 path: '/mypage/{mypage_id}'
 defaults:
   _title: 'My page'
   // page callback. Метод, який рендерить нашу сторінку.
   _controller: '\Drupal\mypage\Controller\MyPageController::content'
 requirements:
   _permission: 'view content'
   // Можно використовувати регулярку для коректності вмісту аргументу.
   // В даному випадку будуть доступні в змінній mypage_id тільки цілі числа, інакше 404. 
   mypage_id: \d+

Створення controller

Спершу трохи теорії по MVC:

Модель (Model) — надає певну інформацію (дані та методи роботи з цими даними), реагує на запити, змінюючи свій стан. Не містить інформації, як ці знання можна візуалізувати.

Представлення (View) — відповідає за відображення інформації (візуалізацію). Часто в якості представлення виступає форма (вікно) з графічними елементами.

Контролер (Controller) — забезпечує зв'язок між користувачем і системою: контролює введення даних користувачем і використовує модель і представлення для реалізації необхідної реакції.

Створимо файл на шляху /modules/mypage/src/Controller і назвемо його MyPageController.php

 <?php
/**
* @file
* Contains \Drupal\mypage\Controller\MyPageController.
*/

namespace Drupal\mypage\Controller;

use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class MyPageController extends ControllerBase {
 // Назва змінної такої ж як у роутері!!!
 public function content($mypage_id = NULL) {
   // Завантаження сервісу.
   $db_logic = \Drupal::service('mypage.db_logic');
   if ($record = $db_logic->getById($mypage_id, TRUE)) {
     return array(
       // Робота з нашою темою.
       '#theme' => 'mypage_theme',
       '#data' => $record,
     );
   }
   // Поверне: сторінку не знайдено.
   throw new NotFoundHttpException();
 }
}

 

Робота с конфігами

На заміну variable_set/get прийшли config. Для створення змінної потрібно в /mypage/config/schema створити файл configform_mypage.schema.yml і прописати:

 // Назва конфіга
configform_mypage.settings:
 type: config_object
 label: 'Configform Example settings'
 mapping:
   email_address:
     type: string
     label: 'This is the example email address.'

Детальніше тут: https://www.drupal.org/node/1905070

Приклад роботи:

 $config = $this->config('configform_mypage.settings');
$config->set('email_address', ‘test’);
$config->save();

Створення Config Form

Створення форм в Друпал 8 кардинально змінилося — тепер немає звичних нам хуків, а є класи. Отже, створимо файл ConfigFormMyPage.php і помістимо його /modules/mypage/src/Form

 <?php

/**
* @file
* Contains \Drupal\mypage\Form\ConfigFormMyPage.
*/

namespace Drupal\mypage\Form;

use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\Component\Utility\SafeMarkup;

class ConfigFormMyPage extends ConfigFormBase {

 /**
  * {@inheritdoc}.
  */
 // Метод, який вертає ід форми.
 public function getFormId() {
   return 'configform_mypage_form';
 }

 /**
  * {@inheritdoc}.
  */
 // Замість hook_form.
 public function buildForm(array $form, FormStateInterface $form_state) {
   $form = parent::buildForm($form, $form_state);
   $config = $this->config('configform_mypage.settings');
   $form['email'] = array(
     '#type' => 'email',
     '#title' => $this->t('Your .com email address.'),
     '#default_value' => $config->get('email_address'),
   );
   $form['title'] = array(
     '#type' => 'textfield',
     '#title' => $this->t('Title'),
     '#required' => TRUE,
   );
   $form['body'] = array(
     '#type' => 'textarea',
     '#title' => $this->t('Body'),
     '#rows' => 5,
     '#required' => TRUE,
   );
   $db_logic = \Drupal::service('mypage.db_logic');
   $data = $db_logic->getAll();
   if ($data) {
     $form['data'] = array(
       '#type' => 'table',
       '#caption' => $this->t('Table Data'),
       '#header' => array($this->t('id'), $this->t('Title'), $this->t('Body')),
     );
     foreach ($data as $item) {
       // Приклад створення посилання.
       // Першим аргументом вказується назва роута, другим аргументом — його параметри:
       $url = Url::fromRoute('mypage.view', array(
         'mypage_id' => $item->id,
       ));
       $form['data'][] = array(
         'id' => array(
           '#type' => 'markup',
           '#markup' => \Drupal::l($item->id, $url),
         ),
         'title' => array(
           '#type' => 'markup',
           '#markup' => $item->title,
         ),
         'body' => array(
           '#type' => 'markup',
           '#markup' => $item->body,
         ),
       );
     }
   }
   return $form;
 }

 /**
  * {@inheritdoc}
  */
 // Замість hook_form_validate.
 public function validateForm(array &$form, FormStateInterface $form_state) {
   if (strpos($form_state->getValue('email'), '.com') === FALSE) {
     $form_state->setErrorByName('email', $this->t('This is not a .com email address.'));
   }
 }

 /**
  * {@inheritdoc}
  */
 // Замість hook_form_submit.
 public function submitForm(array &$form, FormStateInterface $form_state) {
   $db_logic = \Drupal::service('mypage.db_logic');
   $title = SafeMarkup::checkPlain($form_state->getValue('title'));
   $body = SafeMarkup::checkPlain($form_state->getValue('body'));

   $db_logic->add($title, $body);
   // На заміну variable_set/get прийшли config.
   // Приклад роботи з ними.
   $config = $this->config('configform_mypage.settings');
   $config->set('email_address', $form_state->getValue('email'));
   $config->save();
   return parent::submitForm($form, $form_state);
 }

 /**
  * {@inheritdoc}
  */
 // Масив імен об'єктів конфігурації, які доступні для редагування.
 protected function getEditableConfigNames() {
   return ['configform_mypage.settings'];
 }

}

Додавання посилання в пункт меню

Додамо посилання в admin menu — для цього нам потрібно створити файл в корені модуля mypage.links.menu.yml і там прописати наступне:

 mypage.view:
 // Заголовок
 title: 'Add content for mypage'
 // Системна назва меню
 parent: system.admin
 // Назва роута.
 route_name: mypage.add_record

Детальніше можна прочитати тут https://www.drupal.org/developing/api/8/menu

У результаті повинна вийти наступна структура файлів:

На цьому все — наш модуль готовий!

Дивіться також інші блоги наших розробників про Drupal 8:

Темізація у Друпал 8 в прикладах

Модальні вікна (попапи) у Drupal 8

Використання Twig у Drupal 8

Конфігурація в Drupal 8

Переходимо на Друпал 8: допомога Друпал-розробнику

4 votes, Рейтинг: 5

Також по темі

1

Drupal 8, “найсвіжіша” версія відомої CMF (про інновації якої ми вам колись розповідали в 2 частинах — 1 і...

2

«Друпалгеддон», майбутнє хуків, інтернет-магазини на Drupal 8 і багато-багато іншого......

3

Зручність модальних вікон (або, як їх ще називають - попапів) важко переоцінити. Можливість переглядати/отримувати нові та/або змінені дані...

4

Говорити про нові можливості Drupal 8 — справжня насолода для нас. Тож ми повертаємось до цього знову. У блозі ...

5

Drupal 8 набирає популярності ще з моменту виходу першої бета-версії і він приніс з собою чимало змін. Зокрема це стосується і темізації....

Subscribe to our blog updates