【问题标题】:Override router and add parameter to specific routes (before path/url used)覆盖路由器并将参数添加到特定路由(在使用路径/url之前)
【发布时间】:2014-03-19 15:25:53
【问题描述】:

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

例如,现在我有这条路线。

_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}) }}!

而“user”参数需要“\d+”。

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

谢谢!

【问题讨论】:

  • 我看不到这个问题中的问题(除了“你明白吗?”这不适合 SO :P)

标签: symfony routing


【解决方案1】:

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

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

$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}

【讨论】:

  • 我不知道这是否回答了这个问题,但这篇文章实际上救了我的命,我确实需要这个!在我的用例中,可能通过 IFrame 浏览站点,在这种情况下,必须更改所有生成的 URL 上下文才能动态更改页面布局。谢谢楼主!
  • @Pierre 谢谢 :) 我已经用一个实施示例更新了答案。
【解决方案2】:

对我来说是新的一天: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,因为我不想要额外的路由功能或其他东西。 我想使用&lt;a href="{{ path('acmedemo_example_index') }}"&gt;Show demos&lt;/a&gt; 和&lt;a href="{{ path('acmedemo_example_edit', {id: entity.id}) }}"&gt;Edit demo&lt;/a&gt;。

但如果我使用它,我会得到错误。 也让我们这样做。

这个服务的问题是我没有容器>.

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

问题 2 已解决!

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

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

【讨论】:

  • 请参阅this answer 以获得使用请求侦听器和路由器上下文的更清洁且非侵入性的解决方案:)
【解决方案3】:

这样你有两个主要问题:

问题 1

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

当然,您可以有多个可选占位符(例如 /blog/{slug}/{page}),但可选占位符之后的所有内容都必须是可选的。例如,/{page}/blog 是一个有效路径,但 page 始终是必需的(即 /blog 不会匹配此路由)。

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

为此,您有 2 个可选修复:

修复 1

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

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

修复 2

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

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

问题 2

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

查看有关如何编写 twig 扩展的文档: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);
    }
}

然后在你的树枝文件中使用acmePath() 而不是path:

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

代替:

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

【讨论】:

  • 请参阅this answer 以获得使用请求侦听器和路由器上下文的更清洁且非侵入性的解决方案:)
猜你喜欢
  • 2018-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-12
  • 2015-04-23
  • 1970-01-01
  • 2017-04-04
  • 2017-12-21
相关资源
最近更新 更多