Как использовать несколько маршрутов и конвертер нескольких параметров с Symfony

Можно ли использовать 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)
     { ... }

Не логично, что вы будете показывать два отдельных объекта, но использовать для передачи id, который может дублироваться.

revengeance 18.05.2018 21:12
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Symfony Station Communiqué - 17 февраля 2023 г
Symfony Station Communiqué - 17 февраля 2023 г
Это коммюнике первоначально появилось на Symfony Station , вашем источнике передовых новостей Symfony, PHP и кибербезопасности.
Управление ответами api для исключений на Symfony с помощью KernelEvents
Управление ответами api для исключений на Symfony с помощью KernelEvents
Много раз при создании api нам нужно возвращать клиентам разные ответы в зависимости от возникшего исключения.
1
1
2 183
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Проблема, которую я здесь обнаружил, заключается в том, что 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 в контроллере.

Roubi 22.05.2018 14:36

Похоже, что невозможно делать именно то, что вы хотите. Но, возможно, удастся получить то, что вам нужно, с помощью двух простых действий-оберток. Вам даже не понадобится явная аннотация @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, и ваше решение приведет к множеству методов / действий

Roubi 22.05.2018 04:15

Другие вопросы по теме