Dynamic Routes in Symfony2

| Comments

Given that most times we need to associate a static route entry with a method of a controller, it is normal for Symfony developers are used to working with the annotation @Route of FrameworkExtraBundle.

In some cases, it will be interesting or necessary to work with the router to generate dynamic routes. This means that any bundle can generate a route from a service, defining both the name of the route as all the information necessary for the resolution of the route.

Consider the following example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
<?php

namespace Mmoreram\AcmeBundle\Router;

use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Loader\LoaderResolverInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;

/**
 * Acme dynamic router
 */
class AcmeRoutesLoader implements LoaderInterface
{
    /**
     * @var boolean
     *
     * Route is loaded
     */
    private $loaded = false;

    /**
     * Loads a resource.
     *
     * @param mixed  $resource The resource
     * @param string $type     The resource type
     *
     * @return RouteCollection
     *
     * @throws RuntimeException Loader is added twice
     */
    public function load($resource, $type = null)
    {
        if ($this->loaded) {

            throw new \RuntimeException('Do not add this loader twice');
        }

        $routes = new RouteCollection();

        /**
         * url('controller_name') will point AcmeController:methodAction()
         */
        $routes->add('controller_name', new Route('controller/route', array(
            '_controller'   =>  'AcmeBundle:Acme:method',
        )));

        $this->loaded = true;

        return $routes;
    }

    /**
     * Returns true if this class supports the given resource.
     *
     * @param mixed  $resource A resource
     * @param string $type     The resource type
     *
     * @return boolean This class supports the given resource
     */
    public function supports($resource, $type = null)
    {
        return 'acme' === $type;
    }

    /**
     * Gets the loader resolver.
     *
     * @return LoaderResolverInterface A LoaderResolverInterface instance
     */
    public function getResolver()
    {
    }

    /**
     * Sets the loader resolver.
     *
     * @param LoaderResolverInterface $resolver A LoaderResolverInterface
     */
    public function setResolver(LoaderResolverInterface $resolver)
    {
    }
}

In method supports(), $type value can be any desired value, and only should be defined once in all project.

As any service, we must define this class in dependency injection with specific tag.

1
2
3
4
5
services:
    acme.routes.loader:
        class: Mmoreram\AcmeBundle\Router\AcmeRoutesLoader
        tags:
            - { name: routing.loader }

And finally we just need to make our project know where to build our route, so in routing.yml file we must add these lines.

1
2
3
acme_routes:
    resource: .
    type: acme

At this point, type value must be the same as defined in Router service.

Comments