Как наследовать методы родительского контроллера при роутинге через аннотации?

Symfony 3.4.

Хочу хранить весь функционал в абстрактном контроллере, а в дочерних только прописывать префиксы для роутинга:

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

abstract class TaskAbstractController extends Controller
{

    /**
     * Lists all Task entities.
     *
     * @Route("/", name="tasks_index")
     * @Method("GET")
     */
    public function indexAction()
    {
        $em = $this->getDoctrine()->getManager();

        $tasks = $em->getRepository($this->getTargetClassName())->findAll();
        return $this->render('@App/' . $this->getPrefix() . '/index.html.twig', array(
            'tasks' => $tasks,
            'delete_forms' => $this->generateDeleteFormsViews($tasks)
        ));
    }




/**
 * Daily task controller.
 *
 * @Route("daily_tasks")
 */
class DailyTaskController extends TaskAbstractController
{

   protected function getPrefix(): string
   {
       return 'daily_task';
   }

    protected function getTargetClassName(): string
    {
        return 'AppBundle:DailyTask';
    }
}


Но роутер мне говорит "No route found for "GET /daily_tasks/"? хотя должен сработать префикс из дочернего контроллера и роут из метода абстрактного класса. В чем проблема? Как реализовать эту идею, не прописывая реализацию методов с аннотациями в каждом дочернем контроллере?
  • Вопрос задан
  • 1642 просмотра
Решения вопроса 1
Audiophile
@Audiophile Автор вопроса
Нашел решение для того, что хотел: аннотации роутинга не читаются из абстрактных классов, поэтому я расширил абстрактный контроллер неким промежуточным, и прописал в нём экшены с роутингом:
/**
     *
     * @Route("/{id}", name="task_show")
     * @Method("GET")
     *
     */
    public function showAction(string $prefix, string $id)
    {
        return parent::{__FUNCTION__}(...func_get_args());
    }


После чего я уже наследовал этот контроллер другими... Но от этой идеи я отказался, так как имена роутов выходили одинаковыми и сделать их динамическими (с помощью префикса, как URL) не представляется возможным. Потому я просто сделал один контроллер:

/**
 * Lists all Task entities.
 *
 * @Route("/{prefix}", requirements={"prefix":"daily_tasks|events"})
 */
class TaskController extends Controller


универсальный для всех классов - с каким классом сущностей и папками работать он определяет на основе параметра prefix. Так что сделал фабрику:

class TaskFactory
{
    private $map = [
        'daily_tasks' => DailyTask::class,
        'events' => Event::class,
        'todo' => Todo::class,
        'periodical' => Periodical::class,
        'until' => Until::class,
        'ponder' => Ponder::class
    ];


    public function get(string $prefix): TaskInterface
    {
        $class = $this->getTargetClassName($prefix);
        return new $class;
    }


    public function getTargetClassName(string $prefix): string
    {

        if (isset($this->map[$prefix])) {
            return $this->map[$prefix];
        }

        throw new \RuntimeException('Task not found');

    }
}


Только вот теперь инъекция сущностей в декларацию экшенов перестала работать, приходится их самостоятельно создавать / доктриной загружать.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 3
voronkovich
@voronkovich
При помощи YAML и XML это не сделать. Если очень хочется, создайте файл routing.php и загрузите его:
# app/config/routing.yml
routes:
    resource: 'routing.php'

Затем, в это файле настройте маршрутизацию програмно, т.е. через PHP.

Еще один вариант - использовать слушатель события kernel.request и, в зависимости от запроса, менять переменную _controller.

UPD: Еще один способ (возможно, для вас наилучший) - сделать собственный загрузчик маршрутов. См. https://symfony.com/doc/current/routing/custom_rou...
Ответ написан
Комментировать
@Fortop
Tech/Team lead
Красивые ответы... О невозможности
Но сделать таки можно.

Upd: вариант два для "очевидной идеи" автора вопроса
https://github.com/sfortop/symfony-routing-inheritance

Вариант один:
routing.yml

app:
    resource: "@AppBundle/Controller/DefaultController.php"
    type: annotation
demo:
    resource: "@AppBundle/Controller/DemoController.php"
    prefix: "/demo"
    type: annotation
other:
    resource: "@AppBundle/Controller/OtherController.php"
    prefix: "/other"
    type: annotation


AbstractController.php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

abstract class AbstractController extends Controller
{
    /**
     * @param Request $request
     * @return Response
     * @Route("/")
     */
    public function indexAction(Request $request)
    {
        // replace this example code with whatever you need
        return $this->render('default/index.html.twig', [
            'base_dir' => realpath($this->getParameter('kernel.project_dir')).DIRECTORY_SEPARATOR,
        ]);
    }

    /**
     * @param Request $request
     * @Route("/demo/")
     * @return Response
     */
    public function demoAction(Request $request)
    {
        return new Response(sprintf("I'm %s controller", static::class));
    }
}

DefaultController.php

namespace AppBundle\Controller;
class DefaultController extends AbstractController {}
OtherController.php

namespace AppBundle\Controller;
class OtherController extends AbstractController {}


DemoController.php

namespace AppBundle\Controller;
class DemoController extends AbstractController {}

Ответ написан
lexxpavlov
@lexxpavlov
Программист, преподаватель
Настраивайте роутинг не в аннотациях, а в yaml. Да, там тоже не получится наследовать, но в контроллерах настраивать роутинг не нужно.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы