Розширення розвʼязування аргументу дії
Дата оновлення перекладу 2023-05-27
Розширення розвʼязування аргументу дії
У довіднику контролера ви дізналися, що ви можете отримати обʼєкт
Request через аргумент у вашому контролері.
Цей аргумент повинен бути типізований класом Request
, щоб його можна було розпізнати.
Це робиться через ArgumentResolver.
Створюючи та реєструючи користувацькі розвʼязувачі значень аргументу, ви можете розширити
цю функціональність.
Вбудовані розвʼязувачі значень
Symfony постачається з наступними розвʼязувачами значень у компоненті HttpKernel:
- BackedEnumValueResolver
-
Намагається розвʼязати випадок перерахування бекенду з параметру шляху маршруту, який співпадає з іменем аргументу. Призводить до відповіді "404 Не знайдено", якщо значення не є валідним резервним значенням для типу перерахування.
Наприклад, якщо ваше перерахування бекенду:
1 2 3 4 5 6 7 8 9
namespace App\Model; enum Suit: string { case Hearts = 'H'; case Diamonds = 'D'; case Clubs = 'C'; case Spades = 'S'; }
А ваш контролер містить наступне:
1 2 3 4 5 6 7 8 9 10
class CardController { #[Route('/cards/{suit}')] public function list(Suit $suit): Response { // ... } // ... }
При запиті
/cards/H
URL, змінна$suit
збереже випадокSuit::Hearts
.Більш того, ви можете обмежити дозволені значення параметра до одного (або більше) з
EnumRequirement
:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
use Symfony\Component\Routing\Requirement\EnumRequirement; // ... class CardController { #[Route('/cards/{suit}', requirements: [ // це дозволяє всі значення, визначені в Enum 'suit' => new EnumRequirement(Suit::class), // це обмежує можливі значення до перелічених тут значень Enum 'suit' => new EnumRequirement([Suit::Diamonds, Suit::Spades]), ])] public function list(Suit $suit): Response { // ... } // ... }
Приклад вище дозволяє запит лише URL
/cards/D
та/cards/S
і призводить до відповіді "404 не знайдено" у двох інших випадках.6.1
BackedEnumValueResolver
таEnumRequirement
були представлені в Symfony 6.1. - RequestAttributeValueResolver
- Намагається знайти атрибут запиту, що співпадає з іменем аргументу.
- DateTimeValueResolver
-
Намагається знайти атрибут запиту, що співпадає з іменем аргументу, та впроваджує обʼєкт
DateTimeInterface
, якщо він типізований класом, що розширюєDateTimeInterface
.За замовчуванням будь-яке введеня можна парсувати, так як рядок дати приймається PHP. Ви можете обмежити, як може бути відформатоване введення, за допомогою атрибуту MapDateTime.
6.1
DateTimeValueResolver
був представлений в Symfony 6.1. - RequestValueResolver
-
Впроваджує поточний
Request
при типізаціїRequest
або класом, що розширюєRequest
. - ServiceValueResolver
- Впроваджує сервіс при типізації валідним класом сервісу або інтерфейсом. Це працює як автомонтування.
- SessionValueResolver
-
Впроваджує сконфігурований клас сесії, що розширює
SessionInterface
при типізаціїSessionInterface
або класом, що розширюєSessionInterface
. - DefaultValueResolver
- За наявності, встановить значення аргументу за замовчуванням, якщо аргумент не є обовʼязковим.
- UidValueResolver
-
Намагається перетворити будь-які значення UID з параметру шляху маршруту на обʼєкти UID. Призводить до відповіді "404 Не знайдено", якщо значення не є валідним UID.
Наприклад, наступне перетворить параметр токена на обʼєкт
UuidV4
:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// src/Controller/DefaultController.php namespace App\Controller; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Uid\UuidV4; class DefaultController { #[Route('/share/{token}')] public function share(UuidV4 $token): Response { // ... } }
6.1
UidValueResolver
був представлений в Symfony 6.1. - VariadicValueResolver
- Верифікує, чи є дані запиту масивом, та додає їх до списку аргументів. При виклику дії, останній (змінний) аргумент буде містити всі значення цього масиву.
На додаток, деякі компоненти та офіційні пакети надають інші розвʼязувачі значень:
- UserValueResolver
-
Впроваджує обʼєкт, що предтавляє поточного користувача в системі, якщо типізований
UserInterface
. Ви також можете типізувати ваш власний класUser
, але тоді ви повинні додати атрибут#[CurrentUser]
до аргументу. Значення за замовчуванням може бути встановлене якnull
у випадку, якщо до контролеру можуть отримати доступ анонімні користувачі. Це вимагає утановки SecurityBundle.Якщо аргумент не null і немає коритувача в системі, або користувач в системі має клас, що не співпадає з типізованим класом, розвʼязувачем викликається
AccessDeniedException
, щоб запобігти доступу до контролера. - Розвʼязувач обʼєктів PSR-7:
-
Впроваджує обʼєкт Symfony HttpFoundation
Request
, створений з обʼєкту PSR-7 типу ServerRequestInterface, RequestInterface або MessageInterface. Це вимагає встановлення компонента PSR-7 Bridge.
Додавання користувацького розвʼязувача значення
Додавання нового розвʼязувача значення вимагає створення визначень одного класу
та одного сервісу. В наступному прикладі ви створите розвʼязувач значення для
впровадження обʼєкта User
з системи безпеки. За умови, що ви напишете наступний
контролер:
1 2 3 4 5 6 7 8 9 10 11 12 13
// src/Controller/UserController.php
namespace App\Controller;
use App\Entity\User;
use Symfony\Component\HttpFoundation\Response;
class UserController
{
public function index(User $user)
{
return new Response('Hello '.$user->getUserIdentifier().'!');
}
}
Майте на увазі, що ця функція вже надана атрибутом #[ParamConverter] з SensioFrameworkExtraBundle. Якщо у вас встановлено цей пакет у проекті, додайте цю конфігурацію для відключення автоперетвореня типізованих аргументів методу:
1 2 3 4 5
# config/packages/sensio_framework_extra.yaml
sensio_framework_extra:
request:
converters: true
auto_convert: false
Додавання нового розвʼязувача значення вимагає створення класу, що реалізує ArgumentValueResolverInterface, та визначення сервісу для нього. Інтерфейс визначає два методи:
supports()
-
Цей метод використовується, щоб перевірити, чи підтримує розвʼязувач значення
даний аргумент.
resolve()
буде виконано лише коли буде поверненоtrue
. resolve()
-
Цей метод розвʼяже справжнє значення аргументу. Як тільки значення буде розвʼязано,
ви повинні виробити значення в
ArgumentResolver
.
Обидва методи отримують обʼєкт Request
, який є поточним запитом, та екземпляр
ArgumentMetadata. Цей
обʼєкт містить всю інформацію, отриману з підпису методу для поточного аргументу.
Тепер, коли ви знаєте, що робити, ви можете реалізувати цей інтерфейс. Щоб отримати
поточного User
, вам потрібен поточний токен безпеки. Цей токен можна отримати
зі сховища токенів:
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
// src/ArgumentResolver/UserValueResolver.php
namespace App\ArgumentResolver;
use App\Entity\User;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
use Symfony\Component\Security\Core\Security;
class UserValueResolver implements ArgumentValueResolverInterface
{
private $security;
public function __construct(Security $security)
{
$this->security = $security;
}
public function supports(Request $request, ArgumentMetadata $argument): bool
{
if (User::class !== $argument->getType()) {
return false;
}
return $this->security->getUser() instanceof User;
}
public function resolve(Request $request, ArgumentMetadata $argument): iterable
{
yield $this->security->getUser();
}
}
Щоб отримати справжній обʼєкт User
у вашому аргументі, дане значення має
відповідати наступним вимогам:
- Аргумент повинен бути типізований як
User
у вашому підписі методу дії; - Значення повинно бути екземпляром
User
.
Коли всі вимоги виконані та повернено true
, ArgumentResolver
викликає
resolve()
з тими ж значеннями, як викликав supports()
.
Ось і все! Тепер все, що вам залишилося зробити - це додати конфігурацію для
сервіс-контейнера. Це можна зробити, тегувавши сервіс за допомогою
controller.argument_value_resolver
та додавши пріоритетність.
1 2 3 4 5 6 7 8 9 10
# config/services.yaml
services:
_defaults:
# ... переконайтеся, що включено автомонтування
autowire: true
# ...
App\ArgumentResolver\UserValueResolver:
tags:
- { name: controller.argument_value_resolver, priority: 50 }
Хоча додавання пріоритетності необовʼязкове, рекомендується її додати, щоб переконатися,
що впроваджується очікуване значення. Вбудований RequestAttributeValueResolver
, який
вилучає атрибути Request
має проритет 100
. Якщо ваш розвʼязувач також вилучає
атрибути Request
, встановіть пріоритет 100
або більше. В інших випадках, встановіть
пріоритет нижче, ніж 100
, щооб переконатися в тому, що розвʼязувач аргумментів не буде
викликаний, коли наявний атрибут Request
(наприклад, при передачі користувача по підзапитам).
Щоб гарантувати, що розвʼязувачі додаються у правильні місця, ви можете виконати наступну команду, щоб побачити які розвʼязувачі аргументів наявні, і в якому порядку вони запускаються.
1
$ php bin/console debug:container debug.argument_resolver.inner --show-arguments
Tip
Як ви можете побачити в методі UserValueResolver::supports()
, користувач може бути
недоступний (наприклад, коли контролер не знаходиться за файерволом). У таких випадках,
розвʼязувач не буде виконано. Якщо не розвʼязано жодного значення аргументу, буде викликане
виключення.
Щоб уникнути цього, ви можете додати значення за замовчуванням в контролер
(наприклад, User $user = null
). DefaultValueResolver
виконується як
останній розвʼязувач, і буде використовувати значення за замовчуванням, якщо
до цього не було розвʼязано жодного значення.