Можно ли использовать ParamConverter с разными классами в зависимости от вызываемого маршрута?
Скажем, у меня есть родительский класс Food с двумя дочерними классами Fruit и классом Vegetable.
Я бы хотел сделать что-то вроде этого:
/**
*
* @Route("fruit/{id}", name = "fruit_show")
* @ParamConverter("food", class = "AppBundle:Fruit")
* @Route("vegetable/{id}", name = "vegetable_show")
* @ParamConverter("food", class = "AppBundle:Vegetable")
*/
public function showAction(Food $food)
{ ... }




Проблема, которую я здесь обнаружил, заключается в том, что ParamConverter использует последний элемент в аннотациях. Если она соответствует маршруту fruit_show, переменная $ food является экземпляром класса AppBundle: Vegetable. То же самое, если он соответствует маршруту vegetable_show.
class FoodController
{
/**
* @Route("/items/fruits/{id}", methods = {"GET"}, name = "fruits_show")
* @ParamConverter("food", class = "AppBundle:Fruit")
* @Route("/items/vegetables/{id}", methods = {"GET"}, name = "vegetables_show")
* @ParamConverter("food", class = "AppBundle:Vegetable")
*/
public function foodAction(Request $request, $food)
{
//
}
}
One workaround you can use is writing your own ParamConverter:
use Doctrine\ORM\EntityManagerInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterInterface;
use Symfony\Component\HttpFoundation\Request;
class FoodConverter implements ParamConverterInterface
{
protected $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
public function apply(Request $request, ParamConverter $configuration)
{
$id = $request->get('id');
$route = $request->get('_route');
$class = $configuration->getOptions()[$route];
$request->attributes->set($configuration->getName(), $this->entityManager->getRepository($class)->findOneById($id));
return true;
}
public function supports(ParamConverter $configuration)
{
return $configuration->getName() === 'food';
}
}
Добавляем его к своим услугам:
services:
food_converter:
class: App\SupportClasses\FoodConverter
arguments: ['@doctrine.orm.entity_manager']
tags:
- {name: request.param_converter, priority: -2, converter: food_converter}
Используя это так:
class FoodController
{
/**
* @Route("/items/fruits/{id}", methods = {"GET"}, name = "fruits_show")
* @Route("/items/vegetables/{id}", methods = {"GET"}, name = "vegetables_show")
* @ParamConverter("food", converter = "food_converter" class = "App:Food", options = {"fruits_show" = "App:Fruit", "vegetables_show" = "App:Vegetable"})
*/
public function foodAction(Request $request, $food)
{
var_dump($food);
exit();
}
}
Работает нормально после нескольких исправлений: отсутствует arguments: ['@doctrine.orm.entity_manager'] в services.yaml и отсутствует converter = food_converter в аннотации @ParamConverter в контроллере.
Похоже, что невозможно делать именно то, что вы хотите. Но, возможно, удастся получить то, что вам нужно, с помощью двух простых действий-оберток. Вам даже не понадобится явная аннотация @ParamConverter. Например.
/**
*
* @Route("fruit/{id}", name = "fruit_show")
*/
public function showFruitAction(Fruit $fruit)
{
return $this->showAction($fruit)
}
/**
*
* @Route("vegetable/{id}", name = "vegetable_show")
*/
public function showVegetableAction(Food $food)
{
return $this->showAction($vegetable)
}
public function showAction (Food $food)
{ ... }
Очень хороший трюк, хотя мне больше нравится ответ Гимсли, потому что я хочу использовать его во всем контроллере с 3 подклассами вместо 2, и ваше решение приведет к множеству методов / действий
Не логично, что вы будете показывать два отдельных объекта, но использовать для передачи id, который может дублироваться.