На сайте всегда (или почти всегда) возникает задача управления настройками вроде телефон, email, какие то текстовые элементы, вроде копирайта, адрес организации и т.п.
С точки зрения данных — требуется объект в терминах шаблонов проектирования — синглтон. Т.к. нам нужен всего один экземпляр.
Из готовых модулей для реализации этой концепции можно использовать модуль config_pages.
Другой подход так же традиционный — это создание модуля с конфигурацией, где описываются нужные нам поля. Далее выполняется создание и программирование кастомной формы для редактирования этой конфигурации. И уже в коде — это использование config API, для доступа к непосредственным данным.
Такую конфигу можно экспортировать/импортировать, что может быть как достоинством, так и проблемой.
План Б: нода + сервис
Я иногда использую третий подход, который позволяет избежать работы над программированием формы (а также настройки маршрута, и прав доступа к этой форме). Он заключается в том, что мы создаём дополнительный тип публикации, где объявляем, используя конструктор, нужные поля.
Код всё равно кое-какой придется написать, т.к. доступ к данным их этой публикации я оформляю в виде сервиса.
Администратором создаётся всего одна публикация этого типа, а редакторы ограничиваются в праве удалять или создавать их, но могут при этом редактировать уже существующие.
Далее более подробно.
Первый шаг
Создаём новый тип публикации, объявляем поля — пусть это будет публикация с машинным именем constants, и содержит поле field_email, где мы будем хранить публичный емайл адрес для контактов.
Второй шаг
Создание сервиса. Лучше оформить этот код в виде независимого кастомного модуля (для примера my_constants).
my_constants.info.yml
1 2 3 4 5 6 |
name: 'My Constants' type: module description: 'Contacts and other constants for the website' core_version_requirement: ^10 || ^11 package: Custom version: 1.0 |
Сервис объявляется в файле my_constants.services.yml
1 2 3 |
services: my_constants.get_constants: class: Drupal\my_constants\Constants |
Здесь указано, что код сервиса my_constants.get_constants должен быть реализован в виде класса в файле /src/Constants.php. Если это не какой то особенный сервис, то для реализации нет нужды расширять существующий класс или интерфейс, мы имеем дело с обычным классом.
Обычно я делаю так, чтобы сервис позволял читать нужные мне данные из нашей уникальной ноды. Он выполняет всю рутину по поиску ноды, чтению её полей, подготовке данных и кешированию.
Вот пример кода сервиса.
/src/Constants.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
<?php namespace Drupal\my_constants; use Drupal\Core\Cache\Cache; class Constants { const CID = 'singleton_data'; protected $email = ''; public function __construct() { $cached = $this->getFromCache(); if ($cached === FALSE) { // load singleton_contacts node $nids = \Drupal::entityQuery('node') ->condition('type', 'contacts') ->accessCheck(false) ->range(0, 1) ->execute(); if (count($nids)) { $nid = array_pop($nids); $node = \Drupal\node\Entity\Node::load($nid); $this->email = $node->field_mail->value; $this->saveToCache($nid); } } else { $this->email = $cached->email; } } private function getFromCache() { if ($cache = \Drupal::cache()->get(self::CID)) { return $cache->data; } return false; } private function saveToCache(int $nid) { $data = new \stdClass(); $data->email = $this->email; \Drupal::cache()->set(self::CID, $data, Cache::PERMANENT, ['node:' . $nid]); } public function getMail(): string { return $this->email; } } |
CID — это ключ кеша в таблице cache_default.
Третий шаг — настройка
Ограничиваем права редакторов на создание и удаление публикаций этого типа, но выдать разрешение на редактирование (раздел user persmissions в админке).
Далее администратор создаёт одну публикацию, так мы укладываемся в концепцию singleton.
Четвертый шаг — использование
При создании кеша я использую тег node:NID, чтобы при изменении констант, редактировании публикации, сервис пересоздавал свои данные. Публичный метод getMail(), можно использовать для получения емейла из созданных нами констант сайта.
К примеру в теме:
1 2 3 4 5 6 7 |
/** * Implements theme_preprocess_page(). */ function THEME_preprocess_page(&$vars): void { $service = \Drupal::service('my_constants.get_constants'); $vars['public_mail'] = $service->getMail(); } |
Преимущества и недостатки
На мой взгляд — меньше рутины, проще добавить и утилизировать поля, не используются лишние контриб. модули, мы используем базовые возможности drupal из коробки.
Минусом может быть то, что константы не являются часть codebase, как если бы они были в конфигурации сайта и могли бы быть импортированы/экспортированы, вместе с конфиг файлами. Но эти данные — по сути контент, и они не должны быть частью codebase.
Иногда поля удобнее сразу рендерить, это делается штатно через сервис renderer:
1 2 |
$render_array = $node->field_email->view(); $this->email= \Drupal::service('renderer')->renderRoot($render_array); |