Расширения разрешения аргумента действия¶
В справочнике контроллера, вы узнали, что вы можете получить
объект 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
и добавив приоритетность.
- YAML
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 }
- XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
<!-- config/services.xml --> <?xml version="1.0" encoding="UTF-8" ?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-Instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> <services> <!-- ... убедитесь, что включено автомонтирование --> <defaults autowire="true" /> <!-- ... --> <service id="App\ArgumentResolver\UserValueResolver"> <tag name="controller.argument_value_resolver" priority="50" /> </service> </services> </container>
- PHP
1 2 3 4 5
// config/services.php use App\ArgumentResolver\UserValueResolver; $container->autowire(UserValueResolver::class) ->addTag('controller.argument_value_resolver', array('priority' => 50));
Несмотря на то, что добавление приоритетности необязательно, рекомендуется её
добавить, чтобы убедиться в том, что ожидаемое значение будет внедрено.
RequestAttributeValueResolver
имеет приоритетность 100. Так как он отвечает
за получение атрибутов из Request
, рекомендуется установить в вашем разрешителе
значений приоритетность ниже. Таким образом, разрешители значений не будут запускаться,
если имеется атрибут. Например, при передаче пользователя по подзапросам.
Tip
Как вы можете увидеть в методе UserValueResolver::supports()
, пользователь
может быть недоступен (например, когда контроллер на находится за брандмауэром).
В таких случаях, разрешитель не будет выполнен. Если не разрешено ни одно значение
аргумента, будет вызвано исключение.
Чтобы избежать этого, вы можете добавить значение по умолчанию в контроллер
(например, User $user = null
). DefaultValueResolver
выполняется, как
последний разрешитель, и будет использовать значение по умолчанию, если до
этого не было разрешено ни одного значения.
Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.