Расширения разрешения аргумента действия
Дата обновления перевода 2023-07-20
Расширения разрешения аргумента действия
В справочнике контроллера, вы узнали, что вы можете получить
объект Request через аргумент в вашем
контроллере. Этот аргумент должен быть типизирован классом Request
, чтобы его
можно было распознать. Это делается через
ArgumentResolver. Создавая и
регистрируя пользовательские разрешители значений аргумента, вы можете расширить
эту функциональность.
Функциональность, поставляемая с HttpKernel
Symfony поставляется с пятью разрешителями значений в компоненте HttpKernel:
- RequestAttributeValueResolver
- Пытается найти атрибут запроса, совпадающий с именем аргумента.
- RequestValueResolver
-
Внедряет текущий
Request
при типизацииRequest
или классе, расширяющемRequest
. - ServiceValueResolver
- Внедряет сервис при типизации валидным классом сервиса или интерфейсом. Это работает как автомонтирование.
- SessionValueResolver
-
Внедряет сконфигурированный класс сессии, расширяющий
SessionInterface
при типизацииSessionInterface
или классом, расширяющимSessionInterface
. - DefaultValueResolver
- При наличии, установит значение аргумента по умолчанию, если аргумент необязательный.
- VariadicValueResolver
- Верифицирует является ли данные запроса массивом, и добавляет их в список аргументов. При вызове действия, последний (переменный) аргумент будет содержать все значения этого массива.
Добавление пользовательского разрешителя значения
Добавление нового разрешителя значения требует создания определений одного
класса и одного сервиса. В следующем примере, вы создадите разрешитель значения
для внедрения объекта User
из системы безопасности. При условии, что вы
напишете следующий контроллер:
1 2 3 4 5 6 7 8 9 10 11 12
namespace App\Controller;
use App\Entity\User;
use Symfony\Component\HttpFoundation\Response;
class UserController
{
public function index(User $user)
{
return new Response('Hello '.$user->getUsername().'!');
}
}
Каким-то образом вам нужно будет получить объект User
и внедрить его в
контроллер. Это можно сделать, реализовав
ArgumentValueResolverInterface.
Этот интерфейс указывает на то, что вы должны реализовать два метода:
supports()
-
Этот метод используется, чтобы проверить, поддерживает ли разрешитель
значения данный аргумент.
resolve()
будет выполнен только, когда будет возвращеноtrue
. resolve()
-
Этот метод разрешит настоящее значение аргумента. Как только значение будет
разрешено, вы должны yield значение в
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 33 34 35 36
// src/ArgumentResolver/UserValueResolver.php
namespace App\ArgumentResolver;
use App\Entity\User;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class UserValueResolver implements ArgumentValueResolverInterface
{
private $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function supports(Request $request, ArgumentMetadata $argument)
{
if (User::class !== $argument->getType()) {
return false;
}
$token = $this->tokenStorage->getToken();
if (!$token instanceof TokenInterface) {
return false;
}
return $token->getUser() instanceof User;
}
public function resolve(Request $request, ArgumentMetadata $argument)
{
yield $this->tokenStorage->getToken()->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
имеет приоритетность 100. Так как он отвечает
за получение атрибутов из Request
, рекомендуется установить в вашем разрешителе
значений приоритетность ниже. Таким образом, разрешители значений не будут запускаться,
если имеется атрибут. Например, при передаче пользователя по подзапросам.
Tip
Как вы можете увидеть в методе UserValueResolver::supports()
, пользователь
может быть недоступен (например, когда контроллер на находится за брандмауэром).
В таких случаях, разрешитель не будет выполнен. Если не разрешено ни одно значение
аргумента, будет вызвано исключение.
Чтобы избежать этого, вы можете добавить значение по умолчанию в контроллер
(например, User $user = null
). DefaultValueResolver
выполняется, как
последний разрешитель, и будет использовать значение по умолчанию, если до
этого не было разрешено ни одного значения.