12.6. Роут с параметром.
Мы можем использовать параметры в 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: # the name of the argument, what is between {}, it can be node1, node2, if we pass two different arguments.
type: entity: node # what will be the output, we can use the object of this entity inside the controller.
Также я определил в каком методе мы будем выводить заголовок с помощью параметра _title_callback. И мы будем ограничивать вывод статей для анонимных пользователей, для этого мы используем _custom_access параметр в котором указываем в каком методе мы будем накладывать различные ограничения.
Тепреь когда мы разобрались с описанием роута, давайте перейдем к написанию класса для этого роута.
modules/custom/drupalbook/src/Controller/DisplayNode.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();
}
}
Я создал отдельный класс для нешего роута, чтобы не мешать все в один класс контроллер. Давайте разберемся, что значит каждая из этих строк.
namespace Drupal\drupalbook\Controller;
Указываем где лежит наш файл контроллера.
use Drupal\Core\Access\AccessResult;
use Drupal\node\NodeInterface;
Подключаем файлы классов AccessResult - мы будем использовать его для вывода ошибки 403 и NodeInterface - мы будем использовать его для получения объекта ноды в параметр методов нашего контроллера.
function content(NodeInterface $node) {
$element = array(
'#markup' => $node->body->value,
);
return $element;
}
Здесь обратите внимание на параметр, мы получаем здесь объект ноды, друпал сам преобразуем ID из URL и передает нам объект, так чтобы мы лишний раз не подгружали объект нода, а получали готовый там где нам нужно. $node->body->value так мы получаем значение поля из ноды, но о работе с объектами мы разберемся подробно в следующим уроке, когда будем разбирать Entity API. И в конце мы возвращаем массив с #markup для вывода на страницу текста наше ноды.
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 и этого вполне хватает для разграничений прав доступа.
function getTitle(NodeInterface $node) {
return $node->getTitle();
}
Здесь мы просто возвращаем название ноды как title, но можно и расширить возможности нашего callback'а. Например так:
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:
public function getTitle(NodeInterface $node) {
return $node->getTitle() . ' from ' . date('d.m.Y', $node->getCreatedTime());
}
Как видите Drupal предоставляет возможность гибко настроить ваш роут и контроллер. Так что когда у вашего клиента возникнут самые безумные идеи по выводу материалов на сайт, вы всегда сможете это сделать с помощью Drupal API и немного PHP кода.
Примеры кода можно посмотреть на github:
https://github.com/levmyshkin/drupalbook8