symfony4 Резолвер аргументов (Argument Resolver)
Primary tabs
Резолвер в общем случае используется для обработки агрументов контроллера непосредственно ПЕРЕД запуском контроллера.
Принцип действия
- Мы передаём в контроллер в качестве аргумента некий класс/интерфейс.
- Скрипт, натыкаясь на неизвестный аргумент, лезет в свои резолверы проверяет, нет ли резолвера для данного класса (см. метод supports()).
- И если находит, резолвер примет из реквеста параметры, сделает с ними нечто (для чего и нужен резолвер), и передаст нужные данные в контроллер.
Где использовать
- Я с помощью резолвера определяю, какой сервис использовать для работы в контроллере. Логика работы обоих сервисов и названия методов одинаковые, поэтому передаём в контроллер интерфейс, а выбирает сервис - резолвер.
- Также резолвер можно использовать просто для валидации параметров или проверки авторизационного токена.
PHP
Создаём класс резольвера ServiceValueResolver:
// src/ArgumentResolver/ServiceValueResolver.php
namespace App\ArgumentResolver;
use App\Service\FirstService;
use App\Service\SecondService;
use App\Service\ServiceInterface;
use Generator;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
class ServiceValueResolver implements ArgumentValueResolverInterface
{
// В качестве свойств класса подгружаем сервисы из сервис-контейнера
/**
* @var FirstService
*/
private $firstService;
/**
* @var SecondService
*/
private $secondService;
/**
* @param FirstService $firstService
* @param SecondService $secondService
*/
public function __construct(
FirstService $firstService,
SecondService $secondService
) {
$this->firstService = $firstService;
$this->secondService = $secondService;
}
// Определяет, соответствует ли аргумент ожидаемому типу
//*резольвер запускается перед КАЖДЫМ контроллером, но благодаря этому
// методу понимает, к чему применять обработчик, а к чему - нет.
public function supports(Request $request, ArgumentMetadata $argument): bool
{
return ServiceInterface::class == $argument->getType();
}
// Производит обработку данных и выбор конкретного значения
public function resolve(Request $request, ArgumentMetadata $argument): Generator
{
if ($request->get('serviceName) == 'first') {
yield $this->firstService;
} elseif ($request->get('serviceName) == 'second') {
yield $this->secondService;
} else {
throw new BadrequestHttpException('Неверно указано имя сервиса, serviceName');
}
}Интерфейс выглядит просто. Он должен иметь общие методы сервисов, вызываемые в контроллере:
// src/Service/GeoserviceInterface.php
namespace App\Service;
interface ServiceInterface
{
/**
* @param string $address
*
* @return array
*/
public function getPlaces(string $address): array;
}
Теперь в контроллере не нужно дублировать код и вставлять элсифы:) :
// src/Controller/AddressController.php
namespace App\Controller;
use App\Service\ServiceInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
public function indexAction(Request $request, ServiceInterface $serviceInterface): array
{
return $geoserviceInterface->getPlaces($request->get('address'));
}И конечно, не забудьте сконфигурировать работу своего резолвера, иначе он не будет вызван, и может возникнуть ошибка.
// config/services.yaml
services:
_defaults:
// ... убедитесь, что автозагрузка сервисов включена
autowire: true
// ...
App\ArgumentResolver\ServiceValueResolver:
tags:
- { name: controller.argument_value_resolver, priority: 150 }
// priority лучше поставить > 100, иначе ваш резольвер не запуститься перед деволтнымГотово :) Не совсем просто, зато красиво)
Источники
- https://symfony.com/doc/current/controller/argument_value_resolver.html
- Log in to post comments
- 5058 reads