xandeadx.ru Блог музицирующего веб-девелопера

Drupal → Drupal 8 Dev FAQ

Обновляемый список простых вопросов и ответов по Drupal 8.

Все консольные команды рассчитаны на выполнение из корня друпала. Windows пользователям перед выполнением команд в cmd.exe нужно заменить разделитель директорий с / на \, т.е. вместо например vendor/bin/drush писать vendor\bin\drush.

Содержание

  1. Общие вопросы
  2. Работа с сущностями и полями (Entity API, Field API)
  3. Темизация (Theming API, Render API, Twig)
  4. Работа с формами (Form API, Ajax API)
  5. Работа с базой данных (Database API)
  6. Работа с сервисом entity.query (Entity Query API)
  7. Работа с меню и адресами, навигация, роутинг (Menu API, Url, Routing API)

Общие вопросы

С чего начать освоение Drupal?
С установки и настройки Xdebug.
Как правильно скачать Drupal?
Способ 1 (папка vendor будет выше web root):
$ composer create-project drupal/recommended-project --no-dev

Способ 2 (папка vendor будет в web root):
$ composer create-project drupal/legacy-project --no-dev
Как обновить ядро друпала?
Если установленная версия свежее или равна 8.8.0:
$ composer update drupal/core* --with-all-dependencies --no-dev
$ vendor/bin/drush updb

Подробнее.

Перед обновлением обязательно делаем бэкап файлов и базы!

Как установить Drush?
$ composer require drush/drush --update-no-dev

Подробнее.
Как скачать модуль?
Последняя рекомендуемая версия:
$ composer require drupal/devel --update-no-dev

RC версия:
$ composer require drupal/field_group:^3.0-rc2 --update-no-dev

Dev версия:
$ composer require drupal/devel:1.x-dev --update-no-dev

Подробнее.

Замечание: надо всегда следить за тем, какую версию скачивает композер и какая актуальная на странице модуля. Он может легко скачать модуль на пару версий меньше, если например в composer.json есть "prefer-stable": true, а у модуля доступна только rc версия.

Как обновить модуль?
В большинстве случаев:
$ composer update drupal/devel --with-all-dependencies --no-dev

Иногда модуль содержит несколько под-модулей и в каждом свой composer.json. Такие модули можно обновлять по маске:
$ composer update drupal/commerce* --with-all-dependencies --no-dev

Если надо обновить модуль на новую мажорную версию, то необходимо явно указать её в require:
$ composer require drupal/devel:^3.0
Как узнать что блокирует обновление пакета?
Если не удаётся обновить какой-нибудь пакет с помощью composer, то поможет команда prohibits, которая выдаст причины:
$ composer prohibits drupal/core 8.8.1
Как узнать, какие composer пакеты зависят от определённого пакета?
Команда выдаст список пакетов, которые зависят от symfony/process
$ composer why symfony/process
Как создать модуль?
$ vendor/bin/drush generate module

Или вручную
Как создать html ссылку? Какой аналог l()?
Как программно создать страницу?
src/Controller/ModulenameController.php:
<?php
namespace Drupal\modulename\Controller;
 
use Drupal\Core\Controller\ControllerBase;
 
class ModulenameController extends ControllerBase {
  public function helloWorld() {
    return [
      '#markup' => 'Hello, World!',
    ];
  }
}

modulename.routing.yml:
modulename.hello_world:
  path: '/hello/world'
  defaults:
    _controller: '\Drupal\modulename\Controller\ModulenameController::helloWorld'
    _title: 'Hello, World!'
  requirements:
    _permission: 'access content'

Подробнее.

Или с помощью drush:

$ vendor/bin/drush generate controller

Как программно создать блок?
src/Plugin/Block/HelloWorldBlock.php:
<?php
namespace Drupal\modulename\Plugin\Block;
 
use Drupal\Core\Block\BlockBase;
 
/**
 * @Block(
 *   id = "hello_world_block",
 *   admin_label = @Translation("Hello world block"),
 * )
 */
class HelloWorldBlock extends BlockBase {
  public function build() {
    return [
      '#title' => 'It\'s block title', // Optional
      '#markup' => 'Hello, World!',
    ];
  }
}

Подробнее.

Или с помощью drush:

$ vendor/bin/drush generate block

Как запретить кэширование своего блока?
Способ 1:
use Drupal\Core\Cache\UncacheableDependencyTrait;
 
/**
 * @Block(...)
 */
class MyBlock extends BlockBase {
 
  use UncacheableDependencyTrait;
 
  public function build() {
    ...
  }
 
}

Способ 2:
/**
 * @Block(...)
 */
class MyBlock extends BlockBase {
 
  public function build() {
    ...
  }
 
  public function getCacheMaxAge() {
    return 0;
  }
 
}
Как программно ограничить видимость своего блока?
class MyBlock extends BlockBase {
 
  ...
 
  /**
   * {@inheritdoc}
   */
  public function access(AccountInterface $account, $return_as_object = FALSE) {
    $access = FALSE;
 
    if (...) {
      $access = TRUE;
    }
 
    return $return_as_object ? AccessResult::allowedIf($access) : $access;
  }
 
}
Как получить название сайта, слоган и основной e-mail?
$site_config = \Drupal::config('system.site');
 
$site_name = $site_config->get('name');
$site_slogan = $site_config->get('slogan');
$site_mail = $site_config->get('mail');
Как очистить кэш?

Способ 1 - на странице admin/config/development/performance нажать кнопку "Clear all caches".

Способ 2 - выполнить в консоли:

$ vendor/bin/drush cache-rebuild

Способ 3 - выполнить php функцию:

drupal_flush_all_caches();

Как правильно форматировать php код?
// Для отступов используется два пробела
function example() {
  $foo = 'bar';
}
 
// Между бинарными операторами ставится пробел
$a = 1 + 2; // Good
$a=1+2; // Bad
 
// Блок elseif/else начинается с новой строки
if (...) {
  ...
}
elseif (...) {
  ...
}
else {
  ...
}
 
// Массивы создаются с помощью короткого синтаксиса
$array = ['foo', 'bar']; // Good
$array = array('foo', 'bar'); // Bad
 
// Строки заключаются в одиночные кавычки
$string = 'foo'; // Good
$string = "foo"; // Bad
 
// Переменные именуются с помощью нижнего подчёркивания
$my_variable = 'foo'; // Good
$myVariable = 'foo'; // Bad
 
// Названия классов именуются в CamelCase
class MyFirstClass { }
 
// Переменные класса именуются в camelCase
class MyFirstClass {
  public $myFirstVariable;
}

Подробнее.
Как замерить время выполнения участка кода? Какой аналог timer_start() и timer_read()?
use Drupal\Component\Utility\Timer;
 
Timer::start('test');
sleep(1);
debug(Timer::read('test') . ' ms');
Как в своём модуле подключить определённую library на все страницы сайта?
MODULENAME.module:
/**
 * Implements hook_page_attachments().
 */
function MODULENAME_page_attachments(array &$page) {
  $page['#attached']['library'][] = 'MODULENAME/libraryname';
}

Подробнее.
Как получить текущего пользователя? Какой аналог $GLOBALS['user']?
/** @var \Drupal\Core\Session\AccountProxyInterface $current_user */
$current_user = \Drupal::currentUser();

Функция \Drupal::currentUser() возвращает прокси-объект AccountProxy, которого для большинства случаев будет достаточно, но если нужна именно сущность пользователя, то:
$current_user_uid = \Drupal::currentUser()->id();
/** @var \Drupal\user\Entity\User $current_user */
$current_user = \Drupal\user\Entity\User::load($current_user_uid);
Как проверить права текущего пользователя? Какой аналог user_access()?
if (\Drupal::currentUser()->hasPermission('administer site configuration')) {
  ...
}
Как получить данные из $_GET, $_POST и $_COOKIE?
// $_GET
$nid = \Drupal::request()->query->get('nid');
$all_get_params = \Drupal::request()->query->all();
 
// $_POST
$nid = \Drupal::request()->request->get('nid');
$all_post_params = \Drupal::request()->request->all();
 
// $_COOKIE
$nid = \Drupal::request()->cookies->get('nid');
$all_cookie_params = \Drupal::request()->cookies->all();
Как экранировать html код? Что использовать вместо check_plain()?
$string = \Drupal\Component\Utility\Html::escape('<b>Hello</b>'); // Переменная будет содержать "&lt;b&gt;Hello&lt;/b&gt;"

Подробнее.
Что использовать вместо format_string()?
$string = new \Drupal\Component\Render\FormattableMarkup('My name is: @name', [
  '@name' => $name,
]);
@variable — текст будет пропущен через Html::escape().
%variable — текст будет пропущен через Html::escape() и обёрнут в <em></em>.
:variable — текст будет пропущен через Html::escape() и UrlHelper::stripDangerousProtocols().

Подробнее.

Чтобы запретить обрабатывать текст с помощью Html::escape() нужно передать в плэйсхолдер объект MarkupInterface:

$string = new \Drupal\Component\Render\FormattableMarkup('My name is: @name', [
  '@name' => \Drupal\Core\Render\Markup::create('<b>Dries</b>'),
]);

Как склонять строки с числом? Какой аналог format_plural()?
$string = \Drupal::translation()->formatPlural(123, '@count day', '@count days');

Подробнее.
Как обрезать текст?
$truncated_text = \Drupal\Component\Utility\Unicode::truncate($text, 128);
Как сериализовать массив в JSON? Какой аналог drupal_json_encode()?
use Drupal\Component\Serialization\Json;
$string = Json::encode($array);
$array = Json::decode($string);
Как получить дефолтный язык?
$default_language = \Drupal::languageManager()->getDefaultLanguage();
$default_langcode = $default_language->getId();
Как получить текущий язык?
$current_language = \Drupal::languageManager()->getCurrentLanguage();
$current_langcode = $current_language->getId();
Как форматировать unix timestamp в дату? Какой аналог format_date()?
/** @var \Drupal\Core\Datetime\DateFormatterInterface $date_formatter */
$date_formatter = \Drupal::service('date.formatter');
$formatted_date = $date_formatter->format(1558730206, 'short');
Как импортировать несколько конфигов из определённой папки?
$ vendor/bin/drush config-import --partial --source=modules/modulename/config/install
Как отправить e-mail/письмо?
Практически так-же, как в Drupal 6 и 7. В нужный момент вызываем:
\Drupal::service('plugin.manager.mail')->mail(
  'modulename',
  'example_mail_key',
  'to@gmail.com',
  'en',
  ['myvar' => 123]
]);

Плюс реализуем хук hook_mail():
function MODULENAME_mail($key, &$message, $params) {
  if ($key == 'example_mail_key') {
    $message['subject'] = 'Example email subject';
    $message['body'][] = 'Example email body. myvar = ' . $params['myvar'];
  }
}

Можно обойтись без реализации хука, если указать модуль system и специальным образом сформировать массив $params:
\Drupal::service('plugin.manager.mail')->mail('system', 'example_mail_key', 'example@gmail.com', 'en', [
  'context' => [
    'subject' => 'Subject',
    'message' => \Drupal\Core\Render\Markup::create('Message'),
  ],
]);
Как получить текущее время в timestamp?
$current_timestamp = \Drupal::time()->getCurrentTime();
Как транслитерировать строку?
$transliterated_string = \Drupal::transliteration()->transliterate('Привет Мир', 'ru');
// --> Privet Mir
Как отдать пользователю текст в виде файла?
class ExampleController extends ControllerBase {
 
  public function export() {
    $response = new Response();
    $response->headers->set('Content-Type', 'text/csv; charset=utf-8');
    $response->headers->set('Content-Disposition', 'attachment; filename="example.txt"');
    $response->setContent('Hello World!');
 
    return $response;
  }
 
}
Как вывести страницу 404 (страница не найдена) или 403 (доступ запрещён)?
// 404
throw new NotFoundHttpException();
// 403
throw new AccessDeniedHttpException();
Как создать свой токен вида "[node:example-token]"?
modulename.tokens.inc
/**
 * Implements hook_token_info().
 */
function MODULENAME_token_info() {
  $token_info['tokens']['node']['example-token'] = [
    'name' => 'Example node token',
  ];
 
  return $token_info;
}
 
/**
 * Implements hook_tokens().
 */
function MODULENAME_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
  $replacements = [];
 
  if ($type == 'node' && !empty($data['node'])) {
    $node = $data['node']; /** @var NodeInterface $node */
 
    foreach ($tokens as $name => $original) {
      if ($name == 'example-token') {
        $replacements[$original] = 'It\'s example token for node ' . $node->id();
      }
    }
  }
 
  return $replacements;
}

Подробнее.
Как заменить токены в тексте на их значения?
$string = \Drupal::token()->replace('Example string with [node:title] token', [
  'node' => Node::load(123),
]);
Как программно добавить на странице метатег?
В любой рендер-массив добавляем специально сформированный элемент ['#attached']['html_head']. Пример добавления метатега description из своего контроллера:
class ExampleController extends ControllerBase {
 
  public function exampleAction() {
    $meta_description = [
      '#tag' => 'meta',
      '#attributes' => [
        'name' => 'description',
        'content' => ['#plain_text' => 'Random text'],
      ],
    ];
 
    return [
      '#attached' => [
        'html_head' => [
          [$meta_description, 'description']
        ],
      ],
      ...
    ];
  }
 
}
Как создать свой permission? Какой аналог hook_permission()?
modulename.permissions.yml
administer modulename:
  title: 'Administer Modulename'

Подробнее.
Как создать свой alter-хук? Какой аналог drupal_alter()?
$data = ['foo' => 'bar'];
\Drupal::moduleHandler()->alter('my_data', $data);

После этого другие модули смогут альтерить с помощью хука hook_my_data_alter:
function MODULENAME_my_data_alter(&$data) {
  $data['foo'] = 'baz';
}

Подробнее.
Как получить число комментариев у сущности?
$comment_count = (int)$entity->get('field_comment')->comment_count;
Как сделать, чтобы комментарии сортировались по дате создания, от свежих к старым?
/**
 * Implements hook_query_TAG_alter(): comment_filter.
 */
function hook_query_comment_filter_alter(QueryAlterableInterface $query) {
  if ($query instanceof PagerSelectExtender) {
    $order_by = &$query->getOrderBy();
    unset($order_by['c.cid']);
    $query->orderBy('c.created', 'DESC');
  }
}
Как получить номер страницы списка комментариев, на которой отображается нужный комментарий?
Функция возвращает номер страницы комментария:
function _get_comment_page($entity_id, $comment_cid, $per_page) {
  $comment_index = \Drupal::database()
    ->select('comment_field_data')
    ->condition('entity_id', $entity_id)
    ->condition('status', 1)
    ->condition('cid', $comment_cid, '>=')
    ->countQuery()
    ->execute()
    ->fetchField();
 
  return ceil($comment_index / $per_page) - 1;
}

$entity_id - id сущности с комментариями

$comment_cid - id комментария

$per_page - число комментариев на страницу

Работа с сущностями и полями (Entity API, Field API)

Как получить объект сущности по его id?
// С помощью статического метода load()
$node = \Drupal\node\Entity\Node::load(123);
$term = \Drupal\taxonomy\Entity\Term::load(234);
 
// С помощью сервиса entity_type.manager
$node = \Drupal::entityTypeManager()->getStorage('node')->load(123);
$term = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->load(234);
Как получить информацию о поле? Какой аналог field_info_field()?
Универсальный способ получить информацию о любом поле, хоть базовом, хоть настраиваемом:
$category_field = \Drupal::service('entity_field.manager')->getFieldDefinitions('node', 'page')['field_category'];
$category_field_storage = $field_definition->getFieldStorageDefinition();

Для настраиваемых полей можно так же пользоваться методом \Drupal\field\Entity\FieldConfig::loadByName() и \Drupal\field\Entity\FieldStorageConfig::loadByName().
Как проверить, есть ли у сущности или бандла определённое поле?
Если есть доступ к объекту сущности, то:
if ($entity->hasField('field_example')) {
  ...
}

Важный момент — hasField() проверяет наличие поля у бандла сущности, а не наличие значения в этом поле.

Если доступа к объекту сущности нет, то:

$entity_type = 'node';
$entity_bundle = 'page';
$field_name = 'field_example';
 
$field_storage = FieldStorageConfig::loadByName($entity_type, $field_name);
if ($field_storage && in_array($entity_bundle, $field_storage->getBundles())) {
  ...
}

Как получить название/label поля сущности?
// Если есть доступ к объекту сущности
$field_example_label = $entity->get('field_example')->getFieldDefinition()->getLabel();
// Иначе
$field_example_label = \Drupal::service('entity_field.manager')->getFieldDefinitions('node', 'page')['field_example']->getLabel();
Как вычислить число дней между датами в поле типа Date range?
$date_start = $node->field_daterange->start_date;
$date_end = $node->field_daterange->end_date;
$days_between_dates = $date_end->diff($date_start)->format('%a');
Как сделать необязательным второе значение в поле типа Date range?
До того, как не закрыта соответствующая issue, можно поставить модуль Optional End Date.
Как проверить тип поля на базовое/настраиваемое?
if ($node->get('title')->getFieldDefinition() instanceof BaseFieldDefinition) {
  // Поле title является базовым
}
if ($node->get('field_example')->getFieldDefinition() instanceof FieldConfigInterface) {
  // Поле field_example является настраиваемым
}
Как получить название значения у поля типa List?
$field_example_items = $entity->get('field_example');
$allowed_values = options_allowed_values($field_example_items->getFieldDefinition()->getFieldStorageDefinition(), $entity);
$value_label = $allowed_values[$field_example_items->value];
Как получить объект бандла сущности, например NodeType у ноды?
// Способ 1
$node_type = $node->get('type')->entity;
 
// Способ 2
$node_type = NodeType::load($node->bundle());
Как получить адрес/url/uri файла у сущности типа File?
$file = \Drupal\file\Entity\File::load(123); /** @var \Drupal\file\FileInterface $file */
$file_relative_url = $file->createFileUrl(); // /sites/default/files/example.jpg
$file_absolute_url = $file->createFileUrl(FALSE); // http://example.com/sites/default/files/example.jpg
$file_uri = $file->getFileUri(); // public://example.jpg
Как получить адрес/url/uri файла из поля сущности?
$file = $entity->get('field_example_file')->entity; /** @var \Drupal\file\FileInterface $file */
$file_url = $file->createFileUrl(); // /sites/default/files/example.jpg
$file_uri = $file->getFileUri(); // public://example.jpg
Как получить объект сущности файла по его uri?
$file_uri = 'public://example.jpg';
if ($files = \Drupal::entityTypeManager()->getStorage('file')->loadByProperties(['uri' => $file_uri])) {
  $file = current($files); /** @var FileInterface $file */
}
Как создать файловую сущность по uri существующего файла?
$uri = 'public://example.jpg';
$file = \Drupal\file\Entity\File::create([
  'uri' => $uri,
  'filename' => basename($uri),
]);
$file->save();
Как программно добавить файл в многозначное файловое поле сущности?
$file_id = 123;
$entity->get('field_file')->appendItem(['target_id' => $file_id]);

или так:
$file = \Drupal\file\Entity\File::load(123);
$entity->get('field_file')->appendItem($file);
Как скачать файл на сервер по url файла?
$file = system_retrieve_file('http://example.com/image.jpg', 'public://image.jpg', TRUE);
Как программно удалить материал или другую сущность?
$node = \Drupal\node\Entity\Node::load(123);
$node->delete();

Можно воспользоваться drush:
vendor/bin/drush entity-delete node 123
Как программно удалить сразу несколько материалов или сущностей?
Пример удаления трёх нод:
$entity_storage = \Drupal::entityTypeManager()->getStorage('node');
$entities = $entity_storage->loadMultiple([1, 2, 3]);
$entity_storage->delete($entities);

Можно воспользоваться drush:
vendor/bin/drush entity-delete node 1,2,3
Как программно удалить все сущности определённого бандла? Как программно удалить все ноды определённого типа?
Пример удаления всех нод типа article (способ подходит для удаления небольшого количества сущностей, до тысячи):
$entity_storage = \Drupal::entityTypeManager()->getStorage('node');
$entities = $entity_storage->loadByProperties(['type' => 'article']);
$entity_storage->delete($entities);

Можно с помощью drush:
vendor/bin/drush entity-delete node --bundle=article
Как программно пересохранить все термины определённого словаря?
/** @var TermStorageInterface $term_storage */
$term_storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
$terms = $term_storage->loadByProperties(['vid' => 'category']);
foreach ($terms as $term) {
  $term->save();
}
Как получить информацию обо всех полях определённого типа?
/** @var EntityFieldManagerInterface $entity_field_manger */
$entity_field_manger = \Drupal::service('entity_field.manager');
$entity_reference_fields = $entity_field_manger->getFieldMapByFieldType('entity_reference');
Как получить информацию обо всех полях у определённого бандла сущности?
/** @var EntityFieldManager $entity_field_manager */
$entity_field_manager = \Drupal::service('entity_field.manager');
$article_fields = $entity_field_manager->getFieldDefinitions('node', 'article');
Как получить ноду по её синониму url?
$path = \Drupal::service('path.alias_manager')->getPathByAlias('/example-url-alias');
if (preg_match('/node\/(\d+)/', $path, $matches)) {
  $node = \Drupal\node\Entity\Node::load($matches[1]);
}
Как в hook_entity_update() узнать, что значение определённого поля изменилось?
/**
 * Implements hook_entity_update().
 */
function hook_entity_update(EntityInterface $entity) {
  $field_example_items = $entity->get('field_example');
  $original_field_example_items = $entity->original->get('field_example');
  if ($field_example_items ->hasAffectingChanges($original_field_example_items, $field_example_items->getLangcode())) {
    // field_example changed
  }
}
Как получить дочерние термины у определённого термина?
/** @var TermStorageInterface $term_storage */
$term_storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
 
// Способ 1 - по id термина. Получает только непосредственных детей термина 123.
// Результат не отсортирован.
$children_terms = $term_storage->loadChildren(123, 'category'); /** @var TermInterface[] $children_terms */
 
// Способ 2 - по объекту термина. Получает только непосредственных детей термина $term.
// Результат не отсортирован.
$children_terms = $term_storage->getChildren($term); /** @var TermInterface[] $children_terms */
 
// Способ 3 - по id термина. Получает всех детей термина 123 независимо от вложенности.
// Результат отсортирован по весу термина.
$children_terms = $term_storage->loadTree('category', 123, NULL, TRUE); /** @var TermInterface[] $children_terms */
Как получить родительские термины у определённого термина?
/** @var TermStorageInterface $term_storage */
$term_storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
 
// Получает всех родителей независимо от глубины. В массиве так же возвращается и исходный термин.
$parents = $term_storage->loadAllParents(123); /** @var TermInterface[] $parents */
 
// Получает непосредственных родителей термина 123. В большинстве случаев это будет массив из одного элемента.
$parents = $term_storage->loadParents(123); /** @var TermInterface[] $parents */
 
// Непосредственные родители так же доступны через поле термина "parent"
$parents_items = $term->get('parent'); /** @var EntityReferenceFieldItemListInterface $parents_items */

Следует помнить, что непосредственных родителей у термина может быть несколько. Так же следует помнить, что метод $term->get('parent')->isEmpty() всегда возвращает FALSE, поэтому проверять есть ли у термина родители надо с помощью if ($term->get('parent')->entity).

Темизация (Theming API, Render API, Twig)

Как переопределить шаблон в своей теме?
Скопировать нужный файл *.html.twig в папку templates своей темы, очистить кэш. Подробнее.
Как переопределить шаблон в своём модуле?

1. Найти hook_theme, в котором объявлен шаблон.
2. Скопировать информацию о шаблоне в hook_theme своего модуля.
3. Скопировать twig шаблон в свой модуль.
4. Сбросить кэш.

Должно получиться как-то так:

modules/custom/MODULENAME/MODULENAME.module

/**
 * Implements hook_theme().
 */
function MODULENAME_theme() {
  return [
    'node' => [
      'render element' => 'elements',
    ],
  ];
}

modules/custom/MODULENAME/templates/node.html.twig
{{ content }}

Как переопределить шаблон определённого блока?
У каждого блока есть машинное имя, которое прописывается в настройках блока при его добавлении в регион. Чтобы переопределить шаблон определённого блока, нужно скопировать файл core/modules/block/templates/block.html.twig в папку templates вашей темы, переименовать этот файл по шаблону block--machine-name.html.twig (где machine-name машинное имя нужного блока, нижние подчёркивания заменяются на тире) и сбросить кэш. Подробнее, раздел "Blocks".
Как переопределить шаблон определённого поля?
Скопировать файл core/modules/system/templates/field.html.twig в папку templates своей темы, переименовать файл по шаблону field--field-name.html.twig, где field-name это машинное имя поля, сбросить кэш. Подробнее, раздел "Fields".
Как в шаблоне проверить, есть ли у поля сущности значение?
{% if not node.field_example.isEmpty() %}
   field_example is not empty
{% endif %}

или так:
{% if content.field_example[0] %}
   field_example is not empty
{% endif %}
Как в шаблоне узнать, сколько у поля сущности значений?
{% if node.field_example|length > 1 %}
   Field items count more 1
{% endif %}
Как в шаблоне ноды/сущности вывести отдельные поля?
Отформатированное значение поля, прошедшее через field.html.twig:
{{ content.field_example }}

Отформатированное значение поля, но без использования field.html.twig:
{{ content.field_example[0] }}

Сырое значение поля:
{{ node.field_example.value }}
 
{# Или так, если в значении поля есть html код (небезопасно!) #}
{{ node.field_example.value|raw }}

Подробнее.
Как из javascript получить папку текущей темы?
THEMENAME.theme:
/**
 * Preprocess function for html.html.twig.
 */
function THEMENAME_preprocess_html(&$vars) {
  $vars['#attached']['drupalSettings']['path']['currentThemePath'] = $vars['directory'];
}

THEMENAME.libraries.yml:
my-library-name:
  js: ...
  dependencies:
    - core/drupalSettings

После этого путь к папке темы будет находиться в js переменной drupalSettings.path.currentThemePath

Как получить информацию о текущей теме?
$current_theme = \Drupal::service('theme.manager')->getActiveTheme(); /** @var ActiveTheme $current_theme */
// Машинное имя темы
$current_theme_machine_name = $current_theme->getName();
// Информация из файла themename.info.yml
$current_theme_info = $current_theme->getExtension()->info;
Как получить название дефолтной темы?
$default_theme_name = \Drupal::config('system.theme')->get('default');
Как в своей теме подключить определённую library на все страницы сайта?
themename.info.yml:
libraries:
  - themename/libraryname

Подробнее.

Замечание - библиотеки не будут подключаться на страницах сайта, отображаемых в другой теме, например административной.

Как подключить js файл в head?
*.libraries.yml:
my-library:
  js:
    js/my-library.js: {}
  header: true # <--
Как в php получить отрендеренное поле сущности? Какой аналог field_view_field()?
$node = \Drupal\node\Entity\Node::load(123);
 
// Render-array поля, прошедшего через field.html.twig
$field_example_build = $node->get('field_example')->view('full');
// Render-array первого значения поля, прошедшего через форматтер
$field_example_build = $node->get('field_example')[0]->view('full');
Как в php получить отрендеренный материал? Какой аналог node_view()?
Пример рендеринга тизера ноды 123:
$node = \Drupal\node\Entity\Node::load(123);
$node_view_builder = \Drupal::entityTypeManager()->getViewBuilder('node');
$node_build = $node_view_builder->view($node, 'teaser');
Как создать свой шаблон?
MODULENAME.module или THEMENAME.theme:
/**
 * Implements hook_theme().
 */
function MODULENAME_theme() {
  return [
    'my_template' => [
      'variables' => [
        'my_variable' => NULL,
      ],
    ],
  ];
}

my-template.html.twig:
<div class="my-template">{{ my_variable }}</div>

Подробнее.
Как передать в twig шаблон переменную с html разметкой?
$variables['my_html_var'] = \Drupal\Core\Render\Markup::create('<b>Hello</b> <i>World</i>');
Как в twig шаблоне проверить, что массив не пустой?
{% if my_array is not empty %}
  ...
{% endif %}

или
{% if my_array|length > 0 %}
  ...
{% endif %}

Подробнее про empty и length.
Как в twig шаблоне проверить, что в массиве есть элемент с определённым ключом?
{% if (example_array.foo is defined) %}
  {{ example_array.foo }}
{% endif %}
 
{% if (example_array.foo is not defined) %}
  Empty message...
{% endif %}
Как темизировать форму?

Пример темизации формы с идентификатором example_form:

themename.theme

/**
 * Implements hook_theme().
 */
function themename_theme() {
  return [
    'example_form' => [
      'render element' => 'form',
    ],
  ];
}

templates/example-form.html.twig
<form{{ attributes }}>
  <header>
    {{ form.element1 }}
    {{ form.element2 }}
  </header>
  {{ form.without('element1', 'element2') }}
</form>

Подробнее.

Как отрендерить render-array?
В большинстве случаев этого делать не надо, потому что twig самостоятельно рендерит рендер-массивы, но если всё же понадобилось, то всё просто:
$render_array = [
  '#theme' => 'status_messages',
  '#message_list' => [
    'warning' => ['Warning!'],
  ],
];
$output = render($render_array);
Как инвалидировать рендер-кэш с определённым тэгом?
\Drupal::service('cache_tags.invalidator')->invalidateTags(['node:123']);

Подробнее.
Как вывести html таблицу?
$build = [
  '#theme' => 'table',
  '#header' => ['ID', 'Title', 'Date'],
  '#rows' => [
    [1, 'Title 1', '01.01.2019'],
    [2, 'Title 2', '02.01.2019'],
    [3, 'Title 3', '03.01.2019'],
  ],
  '#empty' => 'Empty...',
];
Как в twig воспользоваться функцией Element::children()?
{% for key, element in elements if key|first != '#' %}
  {{ element }}
{% endfor %}
Как создать псевдо-поле/extra-field?
Пример вывода автора материала для нод типа article:
/**
 * Implements hook_entity_extra_field_info().
 */
function MODULENAME_entity_extra_field_info() {
  $extra_fields['node']['article']['display']['author'] = [
    'label' => t('Author name'),
    'weight' => 0,
  ];
 
  return $extra_fields;
}
 
/**
 * Implements hook_ENTITY_TYPE_view(): node.
 */
function MODULENAME_node_view(array &$build, NodeInterface $node, EntityViewDisplayInterface $display, $view_mode) {
  if ($display->getComponent('author')) {
    $build['author'] = ['#markup' => $node->getOwner()->getDisplayName()];
  }
}

Подробнее.
Как в field.html.twig получить доступ к объекту сущности?
Объект сущности лежит в переменной element['#object']. Пример вывода заголовка сущности:
{{ element['#object'].title.value }}
Как twig шаблоне склонять слова с числами?
{% trans %}
  {{ count }} review
{% plural count %}
  {{ count }} reviews
{% endtrans %}
Как в пунктах меню разрешить использовать html?
function THEMENAME_preprocess_menu(&$vars) {
  THEMENAME_preprocess_menu_items($vars['items']);
}
 
function THEMENAME_preprocess_menu_items(&$items) {
  foreach ($items as &$item) {
    $item['title'] = Markup::create($item['title']);
 
    if ($item['below']) {
      THEMENAME_preprocess_menu_items($item['below']);
    }
  }
}
Как добавить css-класс определённому меню?
Пример добавления основному меню класса dragscroll:
function THEMENAME_preprocess_menu__main(&$vars) {
  $vars['attributes']['class'][] = 'dragscroll';
}
Как добавить css-класс определённому блоку?
Пример добавления классов блоку с идентификатором main_menu:
function THEMENAME_preprocess_block__main_menu(&$vars) {
  $vars['attributes']['class'][] = 'my-block-class';
  $vars['content_attributes']['class'][] = 'my-content-class';
}
Как с помощью item_list вывести древовидный список?
Каждый элемент в item_list может быть рендер массивом, поэтому просто вместо строки передаём такой же массив с '#theme' => 'item_list':
$build = [
  '#theme' => 'item_list',
  '#items' => [
    1 => 'Item 1',
    2 => [
      'value' => ['#markup' => 'Item 2'],
      'below' => [
        '#theme' => 'item_list',
        '#items' => [
          1 => 'Item 2.1',
          2 => 'Item 2.2',
        ],
      ],
      '#wrapper_attributes' => [
        'class' => ['open'],
      ],
    ],
    3 => 'Item 3',
  ],
];

На выходе будет:
<ul>
  <li>Item 1</li>
  <li class="open">
    Item 2
    <ul>
      <li>Item 2.1</li>
      <li>Item 2.2</li>
    </ul>
  </li>
  <li>Item 3</li>
</ul>
Как изменить выводимое значение поля в Views?
Пример замены значения поля title в представлении content_recent
function THEMENAME_preprocess_views_view_field__content_recent__title(&$vars) {
  $vars['output'] = 'new field output';
}
Как убрать ссылку ответа на комментарий?
Как сделать плавное появление jQuery UI Dialog?
@keyframes dialog-fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
 
.ui-widget-overlay,
.ui-dialog {
  animation-duration: 0.3s;
  animation-name: dialog-fade-in;
}

Работа с формами (Form API, Ajax API)

Как изменить определённую форму?
Пример изменения формы регистрации с идентификатором user_register_form:
// MODULENAME.module
 
use Drupal\Core\Form\FormStateInterface;
 
/**
 * Implements hook_form_FORM_ID_alter(): user_register_form.
 */
function MODULENAME_form_user_register_form_alter(&$form, FormStateInterface $form_state) {
  $form['terms_of_use'] = [
    '#type' => 'checkbox',
    '#title' => t('I agree with terms and conditions.'),
    '#required' => TRUE,
  ];
}

Подробнее.
Как изменить раскрытую форму Views?
/**
 * Implements hook_form_FORM_ID_alter(): views_exposed_form.
 */
function MODULENAME_form_views_exposed_form_alter(&$form, FormStateInterface $form_state) {
  $view = $form_state->get('view'); /** @var ViewExecutable $view */
  if ($view->id() == 'example_views' && $view->current_display == 'example_display') {
    ...
  }
}
Как создать свою форму?
src/Form/ExampleForm.php:
<?php
namespace Drupal\modulename\Form;
 
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
 
class ExampleForm extends FormBase {
 
  public function getFormId() {
    return 'example_form';
  }
 
  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['example_text'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Text'),
    ];
    $form['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
    ];
    return $form;
  }
 
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $text = $form_state->getValue('example_text');
  }
 
}

Или:
$ vendor/bin/drush generate form-simple

Подробнее.
Как создать GET форму?
public function buildForm(array $form, FormStateInterface $form_state) {
  ...
  $form['#method'] = 'get';
  $form['#action'] = \Drupal::urlGenerator()->generateFromRoute('example.route');
  ...
}
Как отключить кэширование GET формы?
public function buildForm(array $form, FormStateInterface $form_state) {
  ...
  $form['#cache']['max-age'] = 0;
  ...
}
Как очистить GET форму от системных параметров op, form_build_id и form_id?
public function buildForm(array $form, FormStateInterface $form_state) {
  ...
  $form['#pre_render'][] = [$this, 'preRender'];
 
  return $form;
}
 
public function preRender($form) {
  unset($form['form_id']);
  unset($form['form_build_id']);
  unset($form['form_token']);
  unset($form['submit']['#name']);
 
  return $form;
}

Подробнее.
Как разрешить кэшировать блок с POST формой?
Для авторизованных пользователей ко всем формам добавляется скрытый элемент form_token, который защищает формы от CSRF и попутно запрещает кэширование формы. Если форма не делает каких-то важных изменений, то можно удалить этот элемент, тем самым разрешить кэширование родительского элемента, например блока:
public function buildForm(array $form, FormStateInterface $form_state) {
  ...
  $form['#token'] = FALSE;
 
  return $form;
}
Как создать страницу с формой? Как вывести форму на свой странице?
MODULENAME.routing.yml
modulename.example_form:
  path: '/example/form'
  defaults:
    _form: 'Drupal\MODULENAME\Form\ExampleForm'
    _title: 'Example form'
  requirements:
    _permission: 'access content'

Т.е. это обычный роут, только вместо routename.defaults._controller указывается routename.defaults._form.
Как при выводе формы на странице передать в buildForm() объект ноды?
modulename.routing.yml
modulename.example_form:
  path: '/node/{node}/example-form'
  defaults:
    _form: 'Drupal\modulename\Form\ExampleForm'
    _title: 'Example form'
  requirements:
    _permission: 'access content'

src/Form/ExampleForm.php
class ExampleForm extends FormBase {
  ...
  public function buildForm(array $form, FormStateInterface $form_state, NodeInterface $node = NULL) {
    ...
  }
}

Важное замечание — название параметра в buildForm ($node) должно быть таким же, как в роуте ({node}), иначе в него ничего не передастся.
Как вывести форму в своём блоке?
/**
 * @Block(
 *   id = "my_block_with_form",
 *   admin_label = @Translation("My block with form"),
 *   category = @Translation("Forms")
 * )
 */
class MyBlockWithForm extends BlockBase {
  public function build() {
    return [
      'form' => \Drupal::formBuilder()->getForm('Drupal\modulename\Form\ExampleForm')
    ];
  }
}
Как обойти элементы формы? Какой аналог element_children()?
foreach (\Drupal\Core\Render\Element::children($form) as $key) {
  debug($form[$key]);
}

Подробнее.
Как в submitForm() получить параметры, переданные в buildForm()?
class ExampleForm extends FormBase {
 
  public function buildForm(array $form, FormStateInterface $form_state, NodeInterface $node = NULL) {
    ...
  }
 
  public function submitForm(array &$form, FormStateInterface $form_state) {
    /** @var NodeInterface $node */
    $node = $form_state->getBuildInfo()['args'][0];
  }
 
}
Как в buildForm() получить текущее значение элемента?
public function buildForm(array $form, FormStateInterface $form_state) {
  $form['my_element'] = [
    '#type' => 'number',
    '#default_value' => 1,
  ];
 
  $my_element_current_value = $form_state->getValue('my_element', $form['my_element']['#default_value']);
}
Как в submitForm() сохранить данные, чтобы они были доступны при альтере формы?
Сохранение:
public function submitForm(array &$form, FormStateInterface $form_state) {
  ...
  $form_state->set('example_data', 123);
}

Доступ:
$data = $form_state->get('example_data');
Как показывать элемент только когда другой элемент имеет определённое значение? #states
В коде создаётся два селекта и второй селект будет показываться только когда в первом выбрано значение "Show"
$form['example_select'] = [
  '#type' => 'select',
  '#options' => [
    1 => 'Show',
    2 => 'Hide',
  ],
];
 
$form['dependent_select'] = [
  '#type' => 'select',
  '#options' => [...],
  '#states' => [
    'visible' => [
      ':input[name="example_select"]' => ['value' => 1],
    ],
  ],
];

Подробнее.
Как в #states задать условие "показывать если значение НЕ xxx"?
$form['second_element'] = [
  ...
  '#states' => [
    'invisible' => [
      ':input[name="first_element"]' => [
        ['value' => 'xxx'],
      ],
    ],
  ],
];
Как отключить html валидацию формы?
$form['#attributes']['novalidate'] = 'novalidate';
Как показать ошибки валидации в диалоговом окне?
class ExampleForm extends FormBase {
 
  /**
   * {@inheritDoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    ...
 
    $form['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
      '#ajax' => [
        'callback' => '::ajaxSubmit',
      ],
    ];
 
    $form['#attached']['library'][] = 'core/drupal.dialog.ajax';
 
    return $form;
  }
 
  /**
   * Ajax submit callback.
   */
  public function ajaxSubmit(array $form, FormStateInterface $form_state) {
    $response = new AjaxResponse();
 
    if ($form_state->hasAnyErrors()) {
      $response->addCommand(new OpenModalDialogCommand($this->t('Error'), ['#type' => 'status_messages']));
    }
    else {
      ...
    }
 
    return $response;
  }
 
}
Как с помощью ajax команды добавить или удалить html класс?
$response->addCommand(new InvokeCommand('.my-element', 'addClass', ['my-new-class']));
$response->addCommand(new InvokeCommand('.my-element', 'removeClass', ['my-old-class']));
Как разрешить отправлять форму не чаще одного раза в час?
public function validateForm(array &$form, FormStateInterface $form_state) {
  if (!\Drupal::flood()->isAllowed('example_form', 1)) {
    $form_state->setErrorByName('', $this->t('You cannot send more.'));
  }
}
 
public function submitForm(array &$form, FormStateInterface $form_state) {
  ...
  \Drupal::flood()->register('example_form');
}
Как из $form_state получить значение элемента при #tree=>true?
public function buildForm(array $form, FormStateInterface $form_state) {
  $form['foo'] = [
    '#type' => 'textfield',
  ];
  $form['bar'] = [
    '#tree' => TRUE,
  ];
  $form['bar']['baz'] = [
    '#type' => 'textfield',
  ];
}
 
public function submitForm(array &$form, FormStateInterface $form_state) {
  $foo_value = $form_state->getValue('foo');
  $baz_value = $form_state->getValue(['bar', 'baz']);
}
Как создать форму в виде таблицы (табличную форму)?
$form['table'] = [
  '#type' => 'table',
  '#header' => ['Key', 'Value'],
];
 
foreach ([1, 2, 3] as $key) {
  $form['table'][$key]['key'] = [
    '#markup' => 'Key #' . $key,
  ];
 
  $form['table'][$key]['value'] = [
    '#type' => 'textfield',
    '#title' => 'Value #' . $key,
  ];
}

Подробнее
Как создать табличную форму с чекбоксами?
class ExampleForm extends FormBase {
 
  /**
   * {@inheritDoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $rows = [
      1 => ['nid' => 1, 'title' => 'First node'],
      2 => ['nid' => 2, 'title' => 'Second node'],
      3 => ['nid' => 3, 'title' => 'Third node'],
    ];
 
    $form['nodes'] = [
      '#type' => 'tableselect',
      '#header' => [
        'nid'   => 'Node ID',
        'title' => 'Node title',
      ],
      '#options' => $rows,
      '#empty' => 'Empty...',
    ];
 
    $form['delete'] = [
      '#type' => 'submit',
      '#value' => 'Delete selected',
    ];
 
    return $form;
  }
 
  /**
   * {@inheritDoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    foreach ($form_state->getValue('nodes') as $nid => $state) {
      if ($state) {
        Node::load($nid)->delete();
      }
    }
  }
 
}

Подробнее.
Как вывести элемент формы без обёртки с классом form-item?
$form['element'] = [
  '#type' => 'textfield',
  '#title' => 'Element',
  '#theme_wrappers' => [], // <--
];

Замечание - после удаления обёртки не будут работать некоторые js функции, например #states.
Как загрузить файл на сервер без сохранения его в таблице file_managed?
public function buildForm(array $form, FormStateInterface $form_state) {
  $form['upload'] = [
    '#type' => 'file',
    '#title' => t('File'),
  ];
 
  ...
 
  return $form;
}
 
public function submitForm(array &$form, FormStateInterface $form_state) {
  $file_system = \Drupal::service('file_system'); /** @var FileSystemInterface $file_system */
  $all_files = \Drupal::request()->files->get('files', []); /** @var UploadedFile[] $all_files */
  if ($all_files['upload']) {
    $file_system->move($all_files['upload']->getRealPath(), 'public://file.' . $all_files['upload']->getExtension());
  }
}

Работа с базой данных (Database API)

Как добавить в запрос несколько условий с оператором OR?
$or_conditions = $query->orConditionGroup();
$or_conditions->condition('n.status', 0);
$or_conditions->condition('n.status', 1);
$query->condition($or_conditions);
$query->condition('n.type', 'page');
// Сгенерирует условие:
// WHERE (n.status = 0 OR n.status = 1) AND n.type = 'page'

Подробнее.
Как в запросе проверить значение на NULL или NOT NULL?
$query->isNull('field_name');
$query->isNotNull('field_name');
Как подсчитать число записей с помощью COUNT(*)?
$count = \Drupal::database()
  ->select('node')
  ->condition('type', 'page')
  ->countQuery() // <--
  ->execute()
  ->fetchField();

Подробнее
Как получить максимальное/минимальное значение с помощью MAX()/MIN()?
// Max
$query = \Drupal::database()->select('node', 'n');
$query->addExpression('MAX(n.nid)');
$max_nid = $query->execute()->fetchField();
 
// Min
$query = \Drupal::database()->select('node', 'n');
$query->addExpression('MIN(n.nid)');
$min_nid = $query->execute()->fetchField();
Как использовать условие EXISTS?
$subquery = \Drupal::database()
  ->select('taxonomy_index', 't')
  ->fields('t')
  ->where('t.nid = n.nid');
 
$query = \Drupal::database()
  ->select('node', 'n')
  ->fields('n')
  ->exists($subquery);

Код сгенерит запрос вида:
SELECT * FROM node n
WHERE EXISTS (
  SELECT * FROM taxonomy_index t
  WHERE t.nid = n.nid
)
Как в условии использовать оператор LIKE?
$query = \Drupal::database()
  ->select('node', 'n')
  ->condition('n.title', '%' . \Drupal::database()->escapeLike('world') . '%', 'LIKE');

Entity Query API

Как получить сущности по значениям полей? Как пользоваться сервисом entity.query?
$node_query = \Drupal::entityQuery('node');
$node_query->condition('type', 'article');
$node_query->condition('status', NodeInterface::PUBLISHED);
$node_query->condition('field_foo', 'bar');
$nids = $node_query->execute();

Подробнее.

Как добавить условие - "многозначное поле имеет значение 1 и 2"?
$node_query = \Drupal::entityQuery('node');
$node_query->condition($node_query->andConditionGroup()->condition('field_example', 'foo'));
$node_query->condition($node_query->andConditionGroup()->condition('field_example', 'bar'));
$nids = $node_query->execute();
Как добавить условие - "поле не имеет значений" или наоборот "поле не пустое"?
// Условие "поле field_foo не должно иметь значений"
$entity_query->notExists('field_foo');
// Или
$entity_query->condition($field, NULL, 'IS NULL');
// Обратное условие "поле field_bar должно иметь хотя бы одно значение"
$entity_query->exists('field_bar');
// Или
$entity_query->condition($field, NULL, 'IS NOT NULL');
Как добавить условие - "многозначное поле имеет не больше одного значения"?
$node_query->notExists('field_example.1.value');

В зависимости от типа поля, вместо value может быть что-то другое, например target_id для поля типа entity reference.
Как посчитать число сущностей с помощью COUNT(*)?
$news_count = \Drupal::entityQuery('node')
  ->condition('type', 'news')
  ->count()
  ->execute();
Как посмотреть sql запрос, сгенерированный сервисом entity.query?
Для вывода sql запроса нужно перед выполнением добавить к объекту запроса тэг debug:
$node_query = \Drupal::entityQuery('node');
...
$node_query->addTag('debug');
$result = $node_query->execute();

Работает только с включённым модулем Devel.

Работа с меню и адресами, навигация, роутинг

Как из админки создать пункт меню без ссылки?
Как добавить свою ссылку на странице admin/config?
modulename.links.menu.yml:
modulename.settings:
  title: 'Modulename settings'
  description: 'Settings for modulename.'
  parent: system.admin_config_system
  route_name: modulename.settings

При этом роут modulename.settings уже должен существовать. Подробнее.
Как добавить свой таб/вкладку/локальную-задачу/local-task на страницы нод?
modulename.routing.yml:
entity.node.my_tab:
  path: '/node/{node}/my-tab'
  defaults:
    _controller: '\Drupal\modulename\Controller\MyController::myAction'
    _title: 'My tab'
  requirements:
    _permission: 'access content'

modulename.links.task.yml:
entity.node.my_tab:
  route_name: entity.node.my_tab
  base_route: entity.node.canonical
  title: My tab
  weight: 2

Подробнее.
Как получить адрес текущей страницы? Какой аналог current_path(), request_path() и request_uri()?
// Системный адрес. Аналог current_path().
$current_system_path = \Drupal::service('path.current')->getPath();
 
// Синоним адреса. Аналог request_path().
$current_system_path = \Drupal::service('path.current')->getPath();
$current_path_alias = \Drupal::service('path.alias_manager')->getAliasByPath($current_system_path);
 
// Полный путь из строки браузера, с GET параметрами. Аналог request_uri().
$current_request_uri = \Drupal::request()->getRequestUri();
Как получить адрес папки установки друпала?
$base_path = base_path();
Если друпал установлен в корень сайта, то base_path() вернёт /.
Если друпал установлен в папку /drupal/foler, то base_path() вернёт /drupal/foler/.
Как получить название текущего роута?
$current_route_name = \Drupal::routeMatch()->getRouteName();
Как проверить, является ли текущая страница страницей материала?
if (\Drupal::routeMatch()->getRouteName() == 'entity.node.canonical') {
  ...
}
Как получать параметры из текущего пути? Какой аналог arg() и menu_get_object()?
Пример для адреса node/123
$nid = \Drupal::routeMatch()->getRawParameter('node'); // Integer
$node = \Drupal::routeMatch()->getParameter('node'); // Node object
Как добавить класс ссылке меню, созданной с помощью *.links.menu.yml?
Как получить url файла по его uri?
// Вернёт http://example.com/sites/default/files/example.jpg
$file_url = file_create_url('public://example.jpg');
Как создать url с destination на текущую страницу?
$destination_array = \Drupal::destination()->getAsArray();
$url = \Drupal::urlGenerator()->generateFromRoute('example.route', [], ['query' => $destination_array]);
Как сделать редирект из контроллера?
class ExampleController extends ControllerBase {
 
  public function exampleAction() {
    return $this->redirect('<front>');
  }
 
}
Как получить синоним адреса ноды?
$node_alias = $node->get('path')->alias; // "/example/node/path"
Какой шаблон прописать в pathauto для терминов древовидного словаря?
Чтобы получить адреса терминов вида /catalog/category1/category2/category3, нужно прописать терминам следующий шаблон:
catalog/[term:parents:join-path]/[term:name]
Написанное актуально для Drupal 8
Похожие записи

Комментарии RSS

Спасибо! Ценный материал, и в одном месте.

Насколько люблю 7-ку настолько же ненавижу 8-ку. Может я его неправильно курю? Простой пример, но вместо одной строки в info файле 7-ки делать отдельный файл с кучей строк(https://www.drupal.org/docs/8/api/menu-api/providing-module-defined-menu...). Зачем так усложнять?

Из .info файла ссылку конечно не создать, нужен hook_menu, но то что писать теперь нужно больше это да. Усложнено в угоду масштабируемости, хоть большинству она и не нужна. Тут просто надо свыкнуться с мыслью, что семёрка мертва и придётся учить много нового.

Если использовать генераторы кода (drush и drupal console), то в некоторых местах приходится писать даже меньше кода, чем раньше. Да и многие вещи стали гибче, удобнее и логичнее. Но сложности добавилось, это факт.

Огромное Вам спасибо!
Это должно быть в закладках у всякого начинающего адепта секты "Друпал - наше все" )))

Эххх, такой бы гайд еще по коммерц 2....

Оставить комментарий

Содержимое этого поля является приватным и не будет отображаться публично. Если у вас есть аккаунт в Gravatar, привязанный к этому e-mail адресу, то он будет использован для отображения аватара.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Доступные HTML теги: <a> <i> <b> <strong> <code> <ul> <ol> <li> <blockquote> <em> <s>
  • Строки и параграфы переносятся автоматически.
  • Подсветка кода осуществляется с помощью тегов: <code>, <css>, <html>, <ini>, <javascript>, <sql>, <php>. Поддерживаемые стили выделения кода: <foo>, [foo].

Подробнее о форматировании