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, иначе ваш резольвер не запуститься перед деволтным
Готово :) Не совсем просто, зато красиво)
Источники
- Log in to post comments
- 4396 reads