Search code examples
phpsymfonyroutes

Override router and add parameter to specific routes (before path/url used)


I would use a easy management routing system.

For example NOW i have this routes.

_welcome                    ANY      ANY    ANY  /
acmedemo_example_index      ANY      ANY    ANY  /acme/demos
acmedemo_example_edit       ANY      ANY    ANY  /acme/edit/{id}
acmedemo_example_delete     ANY      ANY    ANY  /acme/delete/{id}
acmeapi_backup_get          GET      ANY    ANY  /api/acme
acmeapi_backup_edit         POST     ANY    ANY  /api/acme

Now I would add the current user id to each route, because if a user send me or another supporter/administrator a link, we would see what the user see. You understand?

I would have this now.

_welcome                    ANY      ANY    ANY  /
acmedemo_example_index      ANY      ANY    ANY  /{user}/acme/demos
acmedemo_example_edit       ANY      ANY    ANY  /{user}/acme/edit/{id}
acmedemo_example_delete     ANY      ANY    ANY  /{user}/acme/delete/{id}
acmeapi_backup_get          GET      ANY    ANY  /api/acme
acmeapi_backup_edit         POST     ANY    ANY  /api/acme

And now the "problem"... I want to add the "user" parameter to each route automatically if the route name matches preg_match('/^acmedemo_/i').

For example (index.html.twig):

<a href="{{ path('acmedemo_example_index') }}">Show demos</a>

Or

<a href="{{ path('acmedemo_example_edit', {id: entity.id}) }}">Edit demo</a>

I NOT want to use {{ path('acmedemo_example_edit', {id: entity.id, user: app.user.id}) }}!

And the "user" parameter requires "\d+".

I would like to override the "generate" function on the router, for example. Then I could check if $router->getUrl() matches the ^acmedemo_ and then I could add the user parameter :)

Thanks!


Solution

  • Soooo new day for me :D

    I overrided the router and the UrlGenerator.

    @Chausser: I fixed your problem 1 with an easy:

    acme_demo_example:
        resource: "@AcmeDemoBundle/Controller/"
        type:     annotation
        prefix:   /{user}
    

    Now I have routes like this.

    _welcome                    ANY      ANY    ANY  /
    acmedemo_example_index      ANY      ANY    ANY  /{user}/acme/demos
    acmedemo_example_edit       ANY      ANY    ANY  /{user}/acme/edit/{id}
    acmedemo_example_delete     ANY      ANY    ANY  /{user}/acme/delete/{id}
    acmeapi_examples_get        GET      ANY    ANY  /api/acme
    acmeapi_examples_edit       POST     ANY    ANY  /api/acme
    

    Problem 1 solved!

    Now problem 2, because I want no extra route function or something else. I want to use <a href="{{ path('acmedemo_example_index') }}">Show demos</a> and <a href="{{ path('acmedemo_example_edit', {id: entity.id}) }}">Edit demo</a>.

    But if I would use that I would get errors. Also lets do this.

    The problem I had with this service is that I have no container >.<

    services.yml

    parameters:
        router.class: Acme\DemoBundle\Routing\Router
        router.options.generator_base_class: Acme\DemoBundle\Routing\Generator\UrlGenerator
    

    Acme\DemoBundle\Routing\Router

    use Symfony\Bundle\FrameworkBundle\Routing\Router as BaseRouter;
    class Router extends BaseRouter implements ContainerAwareInterface
    {
        private $container;
    
        public function __construct(ContainerInterface $container, $resource, array $options = array(), RequestContext $context = null)
        {
            parent::__construct($container, $resource, $options, $context);
            $this->setContainer($container);
        }
    
        public function getGenerator()
        {
            $generator = parent::getGenerator();
            $generator->setContainer($this->container);
            return $generator;
        }
    
        public function setContainer(ContainerInterface $container = null)
        {
            $this->container = $container;
        }
    }
    

    Acme\DemoBundle\Routing\Generator\UrlGenerator

    use Symfony\Component\Routing\Generator\UrlGenerator as BaseUrlGenerator;
    class UrlGenerator extends BaseUrlGenerator implements ContainerAwareInterface
    {
        private $container;
    
        protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens)
        {
            /** Set the default user parameter for the routes which haven't a user parameter */
            if(preg_match('/^acmedemo_/i', $name) && in_array('user', $variables) && !array_key_exists('user', $parameters))
            {
                $parameters['user'] = $this->getUser()->getId();
            }
    
            return parent::doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens);
        }
    
        public function setContainer(ContainerInterface $container = null)
        {
            $this->container = $container;
        }
    
        /**
         * @see \Symfony\Component\Security\Core\Authentication\Token\TokenInterface::getUser()
         */
        public function getUser()
        {
            if (!$this->container->has('security.context')) {
                throw new \LogicException('The SecurityBundle is not registered in your application.');
            }
    
            if (null === $token = $this->container->get('security.context')->getToken()) {
                return null;
            }
    
            if (!is_object($user = $token->getUser())) {
                return null;
            }
    
            return $user;
        }
    }
    

    Problem 2 solved!

    (Codes writed by me on Symfony*2.3*)

    Thanks for your help. But this is better I think =)