Свой блок
Блок — кусок страницы, который администратор размещает в областях темы: в шапке, боковой колонке, подвале. Модуль может добавить свой блок — например, «Свежие статьи». Для этого нужен один класс в определённой папке.
Где лежит блок
Класс блока кладут в папку src/Plugin/Block модуля. Путь важен: Drupal ищет блоки именно там. Создайте папки и файл вручную или командой drush generate plugin:block.
Простой блок
// web/modules/custom/blog/src/Plugin/Block/LatestArticlesBlock.php
namespace Drupal\blog\Plugin\Block;
use Drupal\Core\Block\Attribute\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\StringTranslation\TranslatableMarkup;
#[Block(
id: 'blog_latest_articles',
admin_label: new TranslatableMarkup('Свежие статьи'),
category: new TranslatableMarkup('Blog'),
)]
class LatestArticlesBlock extends BlockBase
{
public function build(): array
{
return [
'#markup' => $this->t('Здесь будут свежие статьи.'),
];
}
}Запись #[Block(...)] над классом — атрибут PHP: по нему Drupal узнаёт машинное имя блока и название для панели. Метод build() возвращает массив вывода — такой же, как в контроллере.
Размещение
Выполните drush cr и откройте «Структура → Схема блоков», адрес /admin/structure/block. Рядом с нужной областью нажмите «Разместить блок», найдите «Свежие статьи» и сохраните. Блок появится на страницах сайта в выбранной области. В форме размещения можно задать заголовок и ограничить показ отдельными страницами или ролями.
Блок с данными
Выведем настоящие статьи. Сервисы блок получает через create(), но у блоков этот метод принимает больше аргументов, и класс должен объявить интерфейс ContainerFactoryPluginInterface:
// web/modules/custom/blog/src/Plugin/Block/LatestArticlesBlock.php
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class LatestArticlesBlock extends BlockBase implements ContainerFactoryPluginInterface
{
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
private EntityTypeManagerInterface $entityTypeManager,
) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static
{
return new static($configuration, $plugin_id, $plugin_definition, $container->get('entity_type.manager'));
}
public function build(): array
{
$storage = $this->entityTypeManager->getStorage('node');
$ids = $storage->getQuery()
->accessCheck(TRUE)
->condition('type', 'article')
->condition('status', 1)
->sort('created', 'DESC')
->range(0, 5)
->execute();
$links = [];
foreach ($storage->loadMultiple($ids) as $node) {
$links[] = $node->toLink()->toRenderable();
}
return [
'#theme' => 'item_list',
'#items' => $links,
'#cache' => ['tags' => ['node_list:article']],
];
}
}Атрибут #[Block] остаётся над классом, как в первом примере. Блок выведет список из пяти ссылок на свежие статьи. Свойство #cache сообщает, что блок нужно построить заново, когда создаётся или меняется статья, — о кеше есть отдельный урок.
Настройки блока
Чтобы число статей выбирал администратор, добавьте в класс три метода:
// web/modules/custom/blog/src/Plugin/Block/LatestArticlesBlock.php
use Drupal\Core\Form\FormStateInterface;
public function defaultConfiguration(): array
{
return ['count' => 5];
}
public function blockForm($form, FormStateInterface $form_state): array
{
$form['count'] = [
'#type' => 'number',
'#title' => $this->t('Сколько статей показывать'),
'#default_value' => $this->configuration['count'],
'#min' => 1,
'#max' => 20,
];
return $form;
}
public function blockSubmit($form, FormStateInterface $form_state): void
{
$this->configuration['count'] = (int) $form_state->getValue('count');
}Поле появится в форме размещения блока. В build() замените range(0, 5) на range(0, $this->configuration['count']). Один блок можно разместить несколько раз с разными настройками.
Частая ошибка
Блок не появляется в списке «Разместить блок». Проверьте по порядку: файл лежит в src/Plugin/Block, namespace равен Drupal\blog\Plugin\Block, над классом стоит атрибут с уникальным id, а после добавления файла выполнен drush cr. Ошибка в любом из пунктов не даёт сообщения — блок молча отсутствует.