12.6. Роут с параметром.

Главные вкладки

levmyshkin 7 августа 2019 в 18:34

Видео: https://youtu.be/uoIfbI-2Xoc

Мы можем использовать параметры в URL для роутов. Они работают также как contextual filters во Views. Мы можем например передавать в URL ID различных сущностей, текстовые строки или последовательной ID разделенных запятой или плюсами. В этом уроке мы будем передавать ID ноды и выводить title и body этой ноды в контенте.

Примеры кода можно посмотреть на github:
https://github.com/levmyshkin/drupalbook8

Давайте добавим route в файл нашего модуля drupalbook.routing.yml:

drupalbook.display_node:
  path: '/display-node/{node}'
  defaults:
    _controller: '\Drupal\drupalbook\Controller\DisplayNode::content'
    _title_callback: '\Drupal\drupalbook\Controller\DisplayNode::getTitle'
  requirements:
    _custom_access: '\Drupal\drupalbook\Controller\DisplayNode::access'
  options:
    parameters:
      node:
        type: entity:node

Здесь в path мы передаем во втором аргументе {node}, в урле мы будем писать обычный ID: /display-node/101, но в наш контроллер будет приходить уже готовый объект ноды. Для этого мы указываем в options параметры, что в эти параметры должно передаваться и что будет на выходе

options:
   parameters:
     node: #имя аргумента, то что находится между {}, это может быть  node1, node2, если мы будем передавать два разных аргумента.
       type: entity:node # то что будет на выходе, мы можем использовать объект этой сущности внутри контроллера.

Также я определил в каком методе мы будем выводить заголовок с помощью параметра _title_callback. И мы будем ограничивать вывод статей для анонимных пользователей, для этого мы используем _custom_access параметр в котором указываем в каком методе мы будем накладывать различные ограничения.

Тепреь когда мы разобрались с описанием роута, давайте перейдем к написанию класса для этого роута.

modules/custom/drupalbook/src/Controller/DisplayNode.php:

<?php
 
/**
 * @file
 * Contains \Drupal\drupalbook\Controller\DisplayNode.
 */
 
namespace Drupal\drupalbook\Controller;
 
use 
Drupal\Core\Access\AccessResult;
use 
Drupal\node\NodeInterface;
 
/**
 * Provides route responses for the DrupalBook module.
 */
class DisplayNode {
 
  
/**
   * Returns a simple page.
   *
   * @return array
   *   A simple renderable array.
   */
  
public function content(NodeInterface $node) {
    
$element = array(
      
'#markup' => $node->body->value,
    );
    return 
$element;
  }
 
  
/**
   * Checks access for this controller.
   */
  
public function access(NodeInterface $node) {
    
$user = \Drupal::currentUser();
    if (
$node->getType() == 'article' && !in_array('authenticated'$user->getRoles())) {
      return 
AccessResult::forbidden();
    }
    return 
AccessResult::allowed();
  }
 
  
/**
   * Returns a page title.
   */
  
public function getTitle(NodeInterface $node) {
    return 
$node->getTitle();
  }
 
}
?>

Я создал отдельный класс для нешего роута, чтобы не мешать все в один класс контроллер. Давайте разберемся, что значит каждая из этих строк.

<?phpnamespace Drupal\drupalbook\Controller;
?>

Указываем где лежит наш файл контроллера.

<?phpuse Drupal\Core\Access\AccessResult;
use Drupal\node\NodeInterface;
?>

Подключаем файлы классов AccessResult - мы будем использовать его для вывода ошибки 403 и NodeInterface - мы будем использовать его для получения объекта ноды в параметр методов нашего контроллера.

<?phppublic function content(NodeInterface $node) {
  $element = array(
    '#markup' => $node->body->value,
  );
  return $element;
}?>

Здесь обратите внимание на параметр, мы получаем здесь объект ноды, друпал сам преобразуем ID из URL и передает нам объект, так чтобы мы лишний раз не подгружали объект нода, а получали готовый там где нам нужно. $node->body->value так мы получаем значение поля из ноды, но о работе с объектами мы разберемся подробно в следующим уроке, когда будем разбирать Entity API. И в конце мы возвращаем массив с #markup для вывода на страницу текста наше ноды.

<?phppublic function access(NodeInterface $node) {
  $user = \Drupal::currentUser();
  if ($node->getType() == 'article' && !in_array('authenticated', $user->getRoles())) {
    return AccessResult::forbidden();
  }
  return AccessResult::allowed();
}?>

Сначала мы используем метод друпала currentUser(), чтобы получить объект текущего пользователя, дальше мы будем использовать этот объект, чтобы получить роли текущего пользователя. В проверке if, проверяем контент тип нашей ноды и роли пользователя, для анонимных пользователей будет выдаваться 403 ошибка, если все хорошо, то мы идем дальше и возвращаем allowed(), то есть просто одобряем нашу проверку. Давайте откроем класс AccessResult и посмотрим какие еще методы есть у этого класса. Для этого в PhpStorm нужно дважды нажать shift и ввести названия класса:

Здесь можно найти следующие методы для проверки прав доступа:

neutral
allowed
forbidden

allowedIf
forbiddenIf
allowedIfHasPermission
allowedIfHasPermissions
isAllowed
isForbidden
isNeutral
orIf
andIf

Вы можете поэксперментировать например с allowedIfHasPermission() и по задавать различные права доступа для разных ролей для вашего роута. Например, создать новый permission в своем модуле и использовать его в контроллере. Хотя проще, конечно, использовать permission в yml файле роута. Но через AccessResult класс можно гибко описать логику для доступу к контенту, например "доступ к контенту разрешен авторизированным пользователям с 12 до 16 часов, а пользователям с ролью premium аккаунт круглосуточно". Поэксперментируйте и реализуйте такое разграничение прав доступа к нодам статей.

Как видите друпал предоставляет сразу несколько путей для реализации доступа к контенту, что удобно, потому что 99% процентов кейсов используют один permission и этого вполне хватает для разграничений прав доступа.

<?phppublic function getTitle(NodeInterface $node) {
  return $node->getTitle();
}?>

Здесь мы просто возвращаем название ноды как title, но можно и расширить возможности нашего callback'а. Например так:

<?phppublic function getTitle(NodeInterface $node) {
    $user = \Drupal::currentUser();
    if ($node->getType() == 'article' && !in_array('authenticated', $user->getRoles())) {
     return 'Premium content:  ' . $node->getTitle();
   }
   else {
     return 'Free access content:  ' . $node->getTitle();
   }
 
}
?>

Или вывести дату публикации в title:

<?phppublic function getTitle(NodeInterface $node) {
  return $node->getTitle() . ' from ' . date('d.m.Y', $node->getCreatedTime());
}?>

Как видите Drupal предоставляет возможность гибко настроить ваш роут и контроллер. Так что когда у вашего клиента возникнут самые безумные идеи по выводу материалов на сайт, вы всегда сможете это сделать с помощью Drupal API и немного PHP кода.

Примеры кода можно посмотреть на github:
https://github.com/levmyshkin/drupalbook8

Атрибуция

Абраменко Иван

Автор