覆盖路由器并将参数添加到特定路由(在使用路径/ url之前)

时间:2014-03-19 15:25:53

标签: symfony routing

我会使用简单的管理路由系统。

例如我现在有这条路线。

_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

现在我将当前用户ID添加到每个路由,因为如果用户向我或其他支持者/管理员发送链接,我们将看到用户看到的内容。 你明白了吗?

我现在就有这个。

_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

现在问题" ...我想添加"用户"如果路径名称与preg_match('/^acmedemo_/i')匹配,则自动为每条路线提供参数。

例如(index.html.twig):

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

或者

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

想要使用{{ path('acmedemo_example_edit', {id: entity.id, user: app.user.id}) }}

&#34;用户&#34;参数需要&#34; \ d +&#34;。

我想覆盖&#34;生成&#34;例如,路由器上的功能。 然后,我可以检查$router->getUrl()是否与^acmedemo_匹配,然后我可以添加user参数:)

谢谢!

3 个答案:

答案 0 :(得分:5)

Soooo给我的新一天:D

我覆盖了路由器和UrlGenerator。

@ Chausser :我轻松修复了您的问题1:

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

现在我有这样的路线。

_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

问题1解决了!

现在问题2,因为我不想要额外的路线功能或其他东西。 我想使用<a href="{{ path('acmedemo_example_index') }}">Show demos</a><a href="{{ path('acmedemo_example_edit', {id: entity.id}) }}">Edit demo</a>

但如果我愿意,我会得到错误。 还可以这样做。

我对此服务的问题是我没有容器&gt;。&lt;

services.yml

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

的Acme \ DemoBundle \路由\路由器

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 \路由\发电机\ 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;
    }
}

问题2解决了!

(我在Symfony上写的代码 * 2.3 *

感谢您的帮助。但我认为这更好 =)

答案 1 :(得分:5)

实际上使用RequestContext::setParameter()方法有一种更简单的方法。此上下文可通过路由器通过Router::getContext()方法获得。

kernel listener上使用incoming requests初始化此上下文,或者在请求范围(例如命令)之外,直接通过调用路由器服务上的方法。

$router->getContext()->setParameter('user', $user->getId());

// where $router is the @router service.

请求侦听器的示例:

namespace AppBundle\Listener;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Routing\RequestContextAwareInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

/**
 * Add user param to router context on new request.
 */
class UserAwareRouterContextSubscriber implements EventSubscriberInterface
{
    /**
     * @var TokenStorageInterface
     */
    private $tokenStorage;

    /**
     * @var RequestContextAwareInterface
     */
    private $router;

    /**
     * @param TokenStorageInterface        $tokenStorage
     * @param RequestContextAwareInterface $router
     */
    public function __construct(TokenStorageInterface $tokenStorage, RequestContextAwareInterface $router)
    {
        $this->tokenStorage = $tokenStorage;
        $this->router = $router;
    }

    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents()
    {
        return [KernelEvents::REQUEST => 'onRequest'];
    }

    /**
     * @param GetResponseEvent $event
     */
    public function onRequest(GetResponseEvent $event)
    {
        if (!$event->isMasterRequest()) {
            return;
        }

        if ($token = $this->tokenStorage->getToken()) {
            $user = $token->getUser();
            if ($user instanceof MyUserClass) { // use your own class here :)
                $this->router->getContext()->setParameter('user', $user->getId());
            }
        }
    }
}

服务配置:

services:
    app.listener.user_aware_router_context:
        class: AppBundle\Listener\UserAwareRouterContextSubscriber
        arguments:
            - '@security.token_storage'
            - '@router'
        tags:
            - {name: kernel.event_subscriber}

答案 2 :(得分:1)

有了这个,你有两个主要问题:

问题1

您设置网址的方式需要有2条路线。 http://symfony.com/doc/current/book/routing.html#required-and-optional-placeholders

  

当然,您可以拥有多个可选占位符(例如/ blog / {slug} / {page}),但可选占位符后面的所有内容都必须是可选的。例如,/ {page} / blog是一个有效的路径,但总是需要页面(即简单/博客与此路线不匹配)。

含义即使你确实覆盖了/acme/demos请求进入时路由的生成方式,如果期望acmedemo_example_index,即使/{user}/acme/demos,它也不会与{user}匹配是可选的。

为此您有2个可选修复:

FIX 1

有2条路线,一条与用户匹配,另一条与out匹配。两者都指向同一个控制器动作:

acmedemo_example_index            ANY      ANY    ANY  /acme/demos
acmedemo_example_index_with_user  ANY      ANY    ANY  /{user}/acme/demos

FIX 2

将可选的{user}参数移至网址的末尾:

acmedemo_example_index            ANY      ANY    ANY  /acme/demos/{user}

问题2

您需要一种方法来生成路线。为此,我将创建一个Twig函数,它基本上会执行path()所做的事情,但会追加用户。

查看有关如何编写树枝扩展名的文档:http://symfony.com/doc/current/cookbook/templating/twig_extension.html

注册分机时,您需要传递一些额外的服务,以便生成路线,以便获得当前用户。

# src/Acme/DemoBundle/Resources/config/services.yml
services:
    acme.twig.acme_extension:
        class: Acme\DemoBundle\Twig\AcmeExtension
        arguments: ["@security.context","@router"]
        tags:
            - { name: twig.extension }

然后在扩展中你需要使用一个构造函数:

// src/Acme/DemoBundle/Twig/AcmeExtension.php
namespace Acme\DemoBundle\Twig;

class AcmeExtension extends \Twig_Extension
{
    protected $user;
    protected $router;

    public function __construct($security,$router)
    {
        $this->user = $security->getToken()->getUser();
        $this->router = $router;
    }

    /* Declare your function */

    public function acmePath($route,$params,$requirements)
    {
        if(strpos($route,'acmedemo_')===false){
            return $this->router->generate($route,$params,$requirements);
        }
        /** IF YOU USE FIX 1 **/
        array_merge($params,array('user'=>$this->user));
        $newRoute = $route.'_with_user';
        return $this->router->generate($newRoute ,$params,$requirements);

        /** IF YOU USE FIX 2 **/
        array_merge($params,array('user'=>$this->user));
        return $this->router->generate($route,$params,$requirements);
    }
}

然后在您的twig文件中使用acmePath()而不是path

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

而不是:

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