Расширение разрещения аргумента действия
Дата обновления перевода 2023-01-18
Расширение разрещения аргумента действия
В руководстве о контроллерах, вы узнали, что вы можете получить объект
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 { // ... } // ... }
При запросе URL
/cards/H
, переменная$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
- Верифицирует, являются ли данные запроса массивом, и добавит их все к списку аргументов. Когда вызывается действие, последний (вариативный) аргумент будет содержать все значения этого массива.
In addition, some components and official bundles provide other value resolvers:
- UserValueResolver
-
Внедряет объект, который представляет текущего пользователя в системе, если есть подсказка
UserInterface
. Вы можете также добавить подсказку собственного классаUser
, но вы должны затем добавить атрибут#[CurrentUser]
к аргументу. Значение по умолчанию может быть установлено какnull
в случае, если к контроллеру можно получить доступ анонимным пользователям. Требует установки SecurityBundle.Если аргумент не может быть null и нет пользователя в системе, или пользователь в системе имеет класс пользователя, не совпадающий с классом подсказки, разрешителем вызыввается
AccessDeniedException
, чтобы предотвратить доступ к контроллеру. - Разрешитель объектов PSR-7:
-
Внедряет объект Symfony HttpFoundation
Request
, созданный из объекта PSR-7 типа ServerRequestInterface, RequestInterface или MessageInterface. Требует установки компонента Мост PSR-7.
Добавление пользовательского разрешителя значений
В следующем примере вы создадите разрешитель значений для внедрения объекта
значения 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
}
}
6.2
ValueResolverInterface
был представлен в Symfony 6.2. До версии
6.2, вам нужно было использовать
ArgumentValueResolverInterface,
что определяет другие методы.
Добавление нового разрешителя значений требует создания класса, который реализует ValueResolverInterface, и определения сервиса для него.
Этот интерфейс содержит метод resolve()
, который вызывается для каждого аргумента
контроллера. Он получает текущий объект Request
и экземпляр
ArgumentMetadata, который
содержит всю информацыию из подписи метода.
Метод resolve()
должен вернуть либо пустой массив (если он не может разрешить этот
аргумент) или массив с разрешённым(и) значениям(и). Обычно аргументы разрешаются как
одно значение, но вариативные аргументы требуют разрешения нескольких значений. Поэтому
вы должна всегда возвращать массив, даже для одиночных значений:
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
// src/ValueResolver/IdentifierValueResolver.php
namespace App\ValueResolver;
use App\IdentifierInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
class BookingIdValueResolver implements ValueResolverInterface
{
public function resolve(Request $request, ArgumentMetadata $argument): array
{
// получить тип аргумента (например, 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
и приоритет:
- YAML
- XML
- PHP
1 2 3 4 5 6 7 8 9 10
# config/services.yaml
services:
_defaults:
# ... убедитесь в том, что включено автомонтирование
autowire: true
# ...
App\ArgumentResolver\BookingIdValueResolver:
tags:
- { name: controller.argument_value_resolver, priority: 150 }
Хотя добавление приоритета является необязательным, рекомендуется его добавить, чтобы
гарантировать, что ожидаемое значение будет внедрено. Встроенный RequestAttributeValueResolver
,
который также извлекает атрибуты Request
, должен иметь приоритет 100
или более.
В других случаях, установите приоритет ниже 100
, чтобы убедиться, что разрешитель
аргументов не запускается, когда присутствует атрибут Request
.
Чтобы гарантировать, что ваши разрешители добавляются в правильном месте, вы можете выполнить следующую команду, чтобы увидеть, какие разрешители аргументов присутствуют, и в каком порядке они запукаются:
1
$ php bin/console debug:container debug.argument_resolver.inner --show-arguments