Розширення розвʼязування аргументу дії
Дата оновлення перекладу 2024-05-13
Розширення розвʼязування аргументу дії
У довіднику контролера ви дізналися, що ви можете отримати обʼєкт
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 не знайдено" у двох інших випадках. - RequestPayloadValueResolver
-
Відображає корисне навантаження запиту або рядок запиту в об'єкт із підказкою типу.
Оскільки це цільовий розвʼязувач значень , вам доведеться використовувати або MapRequestPayload або атрибут MapQueryString для того, щоб використати цей розв'язувач.
- RequestAttributeValueResolver
- Намагається знайти атрибут запиту, що співпадає з іменем аргументу.
- DateTimeValueResolver
-
Намагається знайти атрибут запиту, що співпадає з іменем аргументу, та впроваджує обʼєкт
DateTimeInterface
, якщо він типізований класом, що розширюєDateTimeInterface
.За замовчуванням будь-яке введеня можна парсувати, так як рядок дати приймається PHP. Ви можете обмежити, як може бути відформатоване введення, за допомогою атрибуту MapDateTime.
Tip
Об'єкт
DateTimeInterface
генерується за допомогою компонента Clock. Це дає вам повний контроль над значеннями дати та часу, які контролер отримує при тестуванні вашого додатка та використанні реалізації MockClock. - RequestValueResolver
-
Впроваджує поточний
Request
при типізаціїRequest
або класом, що розширюєRequest
. - ServiceValueResolver
- Впроваджує сервіс при типізації валідним класом сервісу або інтерфейсом. Це працює як автомонтування.
- SessionValueResolver
-
Впроваджує сконфігурований клас сесії, що розширює
SessionInterface
при типізаціїSessionInterface
або класом, що розширюєSessionInterface
. - DefaultValueResolver
- За наявності, встановить значення аргументу за замовчуванням, якщо аргумент не є обовʼязковим.
- UidValueResolver
-
Attempts to convert any UID values from a route path parameter into UID objects. Leads to a 404 Not Found response if the value isn't a valid UID.
For example, the following will convert the token parameter into a
UuidV4
object: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\Attribute\Route; use Symfony\Component\Uid\UuidV4; class DefaultController { #[Route('/share/{token}')] public function share(UuidV4 $token): Response { // ... } }
- VariadicValueResolver
- Перевіряє, чи є дані запиту масивом, і додає їх усі до списку аргументів. Коли дія буде викликана, останній (варіаційний) аргумент міститиме всі значення цього масиву.
Крім того, деякі компоненти, мости та офіційні пакети надають інші розв'язувачі значень:
- UserValueResolver
-
Впроваджує об'єкт, який представляє поточного користувача, що увійшов до системи, якщо тип підказано з
UserInterface
. Ви також можете підказати свій власний класUser
, але ви повинні додати атрибут#[CurrentUser]
до аргументу. Значення за замовчуванням може бути встановлено уnull
у випадку, якщо до контролера можуть отримати доступ анонімні користувачі. Це вимагає встановлення SecurityBundle.Якщо аргумент не може бути null і немає користувача у системи, або користувач у системі має клас користувача, що не збігається з типом підказки, буде викликано виключення
AccessDeniedException
, щоб запобігти доступу до контролера. - SecurityTokenValueResolver
-
Впроваджує обʼєкт, який представляє поточний токен входу, якщо підказано тип
TokenInterface
або клас, що розширює його.Якщо аргумент не нульовий і немає токена входу розвʼязувачем викликається статус-код 401
HttpException
, щоб запобігти доступу до контролера. - 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 { // ... } }
Щоб дізнатися більше про використання
EntityValueResolver
, зверніться до спеціального розділу Автоматичне отримання обʼєктів . - Розвʼязувач обʼєктів PSR-7:
-
Впроваджує обʼєкт Symfony HttpFoundation
Request
, створений з обʼєкта PSR-7 типу ServerRequestInterface, RequestInterface або MessageInterface. Вимагає установки компонента Міст PSR-7.
Управління розвʼязувачами значень
Для кожного аргументу, кожен розв'язувач з тегом controller.argument_value_resolver
буде викликатися до тих пір, поки один з них не надасть значення. Порядок їх виклику залежить
від їхнього пріоритету. Наприклад, SessionValueResolver
буде викликано перед
DefaultValueResolver
, оскільки його пріоритет вищий. Це дозволяє написати, наприклад
SessionInterface $session = null
, щоб отримати сесію, якщо вона є, або null
якщо її немає.
У цьому конкретному випадку вам не потрібно запускати ніякий розвʼязувач перед
SessionValueResolver
, тому їх пропуск не тільки покращить продуктивність,
але й не дасть жодному з них надати значення до того, як SessionValueResolver
встигне це зробити.
Атрибут ValueResolver дозволяє вам зробити це, "націлившись" на потрібний вам розвʼязувач:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// src/Controller/SessionController.php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\Attribute\ValueResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\SessionValueResolver;
use Symfony\Component\Routing\Annotation\Route;
class SessionController
{
#[Route('/')]
public function __invoke(
#[ValueResolver(SessionValueResolver::class)]
SessionInterface $session = null
): Response
{
// ...
}
}
У наведеному вище прикладі SessionValueResolver
буде викликано першим, оскільки
він є цільовим. Вирішувач DefaultValueResolver
буде викликано наступним, якщо не
було надано жодного значення; ось чому ви можете призначити null
як значення за
замовчуванням для $session
.
Ви можете вказати на потрібний розв'язувач, передавши його ім'я як перший аргумент
ValueResolver
. Для зручності, назвами вбудованих розвʼязувачів є їхні FQCN.
- Цільовий розв'язувач також можна вимкнути, передавши аргумент
ValueResolver
-
$disabled
якtrue
; ось як :ref:`MapEntity дозволяє відключити
EntityValueResolver для певного контролера <doctrine-entity-value-resolver>`.
Так, MapEntity
розширює ValueResolver
!
Додавання користувацького розвʼязувача значення
У наступному прикладі ви створите розвʼязувач значення, щоб впровадити ID
обʼєкта значення, якщо аргумент контролера має тип, що реалізує IdentifierInterface
(наприклад, BookingId
):
1 2 3 4 5 6 7 8 9 10 11 12 13
// src/Controller/BookingController.php
namespace App\Controller;
use App\Reservation\BookingId;
use Symfony\Component\HttpFoundation\Response;
class BookingController
{
public function index(BookingId $id): Response
{
// ... зробити щось з $id
}
}
Додавання нового розвʼязувача значення вимагає створення класу, що реалізує ValueResolverInterface та визначення сервісу для нього.
Інтерфейс містить метод resolve()
, який викликається для кожного аргументу
контролера. Він отримує поточний обʼєкт Request
та екземпляр
ArgumentMetadata,
який містить всю інформацію з підпису методу.
Метод resolve()
повинен повертати або порожній маив (якщо не може розвʼязати цей
аргумент), або масив розвʼяханого(их) значення(нь). Зазвичай аргументи розвʼязуються
як одне значення, але варіативні аргументи вимагають розвʼязання багатьох значень.
Тому ви повинні завжди повертати масив, навіть для одного значення:
// src/ValueResolver/IdentifierValueResolver.php namespace AppValueResolver;
use AppIdentifierInterface; use SymfonyComponentHttpFoundationRequest; use SymfonyComponentHttpKernelControllerValueResolverInterface; use SymfonyComponentHttpKernelControllerMetadataArgumentMetadata;
class BookingIdValueResolver implements ValueResolverInterface { public function resolve(Request $request, ArgumentMetadata $argument): iterable { // отримати тип аргументу (наприклад, BookingId) $argumentType = $argument->getType(); if ( !$argumentType || !is_subclass_of($argumentType, IdentifierInterface::class, true) ) { return []; }
- // отримати значення із запиту, засновуючись на імені аргументу
$value = $request->attributes->get($argument->getName()); if (!is_string($value)) { return []; }
// створити та повернути обʼєкт значення return [$argumentType::fromString($value)];
}
}
Цей метод спершу перевіряє, чи він може розвʼязати значення:
- Аргумент повинен мати підказку класу, що реалізує користувацький
IdentifierInterface
; - Імʼя аргументу (наприклад,
$id
) повинно співпадати з іменем атрибуту запиту (наприклад, використовуючи заповнювач маршруту/booking/{id}
).
Коли ці вимоги виконано, метод створює новий екземпляр користувацького обʼєкта значення і повертає його як значення цього аргументу.
Ось і все! Тепер все, що вам потрібно зробити, - додати конфігурацію для сервіс-контейнера. Це можна зробити додавши один з наступних тегів до вашого розвʼязувача значень.
controller.argument_value_resolver
Цей тег автоматично додається до кожного сервіса, що реалізує ValueResolverInterface
,
але ви можете встановити його самостійно, щоб змінювати його атрибути priority
або name
.
1 2 3 4 5 6 7 8 9 10 11 12
# config/services.yaml
services:
_defaults:
# ... переконайтеся в тому, що підключено автомонтування
autowire: true
# ...
App\ValueResolver\BookingIdValueResolver:
tags:
- controller.argument_value_resolver:
name: booking_id
priority: 150
Хоча додавання пріорітетності необовʼязково, рекомендовано додавати її, щоб гарантувати,
що очікуване значення буде впроваджено. Вбудований RequestAttributeValueResolver
,
який отримує атрибути з Request
, має пріоритет 100
. Якщо ваш розвʼязувач також
добуває атрибути Request
, встановіть пріоритет 100
або більше. Інакше, встановіть
пріоритет нижче за 100
, щоб гарантувати, що розвʼязувач аргументів не буде запущено,
коли наявний атрибут Request
.
Щоб гарантувати, що ваші розвʼязувачі додані у правильному місці, ви можете виконати наступну команду, щоб побачити, які розвʼязувачі аргументів наявні, і в якому порядку вони виконуються:
1
$ php bin/console debug:container debug.argument_resolver.inner --show-arguments
Ви також можете сконфігурувати імʼя, передане атрибуту ValueResolver
, щоб вказати
ваш розвʼязувач. Інакше він буде за замовчуванням id сервісу.
controller.targeted_value_resolver
Встановіть цей тег, якщо ви хочете, щоб ваш розвʼязувач викликався лише тоді, коли на
нього націлено ValueResolver
. Подібно до controller.argument_value_resolver
, ви
можете налаштувати ім'я, за яким може бути визначений ваш розвʼязувач.
Як варіант, ви можете додати атрибут AsTargetedValueResolver до вашого розвʼязувача і передати ваше користувацьке ім'я як його перший аргумент:
1 2 3 4 5 6 7 8 9 10 11
// src/ValueResolver/IdentifierValueResolver.php
namespace App\ValueResolver;
use Symfony\Component\HttpKernel\Attribute\AsTargetedValueResolver;
use Symfony\Component\HttpKernel\Controller\ValueResolverInterface;
#[AsTargetedValueResolver('booking_id')]
class BookingIdValueResolver implements ValueResolverInterface
{
// ...
}
Потім ви можете передати це імʼя як перший аргумент ValueResolver
, щоб вказати
на ваш розвʼязувач:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// src/Controller/BookingController.php
namespace App\Controller;
use App\Reservation\BookingId;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\ValueResolver;
class BookingController
{
public function index(#[ValueResolver('booking_id')] BookingId $id): Response
{
// ... зробити щось з $id
}
}