O
O
Outsider V.2017-07-14 18:20:39
symfony
Outsider V., 2017-07-14 18:20:39

How to inherit parent controller methods when routing via annotations?

Symfony 3.4.
I want to store all the functionality in an abstract controller, and only prefixes for routing in child controllers:

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';
    }
}

But the router tells me "No route found for "GET /daily_tasks/"? although the prefix from the child controller and the route from the abstract class method should work. What's the problem? How to implement this idea without writing the implementation of methods with annotations in each child controller?

Answer the question

In order to leave comments, you need to log in

4 answer(s)
O
Outsider V., 2017-07-15
@Audiophile

I found a solution for what I wanted: routing annotations are not read from abstract classes, so I extended the abstract controller with some intermediate one, and added routing actions to it:

/**
     *
     * @Route("/{id}", name="task_show")
     * @Method("GET")
     *
     */
    public function showAction(string $prefix, string $id)
    {
        return parent::{__FUNCTION__}(...func_get_args());
    }

After that, I already inherited this controller by others ... But I abandoned this idea, since the names of the routes came out the same and it is not possible to make them dynamic (using a prefix, like a URL). Because I just made one controller:
/**
 * Lists all Task entities.
 *
 * @Route("/{prefix}", requirements={"prefix":"daily_tasks|events"})
 */
class TaskController extends Controller

universal for all classes - it determines which entity class and folders to work with based on the prefix parameter. So I made a factory:
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');

    }
}

Only now the injection of entities into the declaration of actions has stopped working, you have to create them yourself / load them with a doctrine.

V
voronkovich, 2017-07-14
@voronkovich

You can't do this with YAML and XML. If you really want to, create a routing.php file and upload it:

# app/config/routing.yml
routes:
    resource: 'routing.php'

Then, in this file, configure the routing programmatically, i.e. via PHP.
Another option is to use the kernel.request event listener and change the _controller variable depending on the request.
UPD: Another way (perhaps the best for you) is to make your own route loader. See https://symfony.com/doc/current/routing/custom_rou...

F
Fortop, 2017-07-15
@Fortop

Beautiful answers... About impossibility
But it is possible to make.
Upd: option two for the question author's "obvious idea"
https://github.com/sfortop/symfony-routing-inheritance

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

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));
    }
}

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

namespace AppBundle\Controller;
class OtherController extends AbstractController {}

namespace AppBundle\Controller;
class DemoController extends AbstractController {}

A
Alexey Pavlov, 2017-07-14
@lexxpavlov

Set up routing not in annotations, but in yaml. Yes, it won’t be possible to inherit there either, but you don’t need to configure routing in controllers.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question