Symfony2:动态路由参数

时间:2016-07-10 15:09:48

标签: php symfony routes

我转向论坛,因为我无法找到解决问题的有效方法。

我已经接管了Symfony2应用程序的管理,该应用程序处理公司内部的订单,发票......问题在于它没有归档功能。所以,经理让我要按年份添加存档'应用程序的功能(只根据所选年份显示数据)。

所以,我决定在所有应用程序路径前加上/ {year} /,这个参数将与经理想要查看的年份相匹配,并且由于所有文件都已过时,我只需更新Doctrine请求以选择那些匹配所选年份。到目前为止没有任何问题。

routes.yml

mes_routes:
    resource: "mes_routes.yml"
    prefix:   /{year}
    defaults: {'year': %current_year%}

有了这个,我创建了一个Symfony扩展,填补了当前的年份。默认情况下,在我的路线定义中,如果没有提供年份,则为实际年份。

MyAppExtension.php

class MyAppExtension extends Extension
{
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);

        $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services.yml');

        // Fill parameter with the current year
        $container->setParameter('current_year', date("Y"));
    }
}

接下来,我创建了一个RouteListener,当用户显示一个新页面时(以显示相同的页面但下一年有不同的年份),它会在会话var中存储先前的路由及其参数。

LastRouteListener.php

class LastRouteListener
{
    public function onKernelRequest(GetResponseEvent $event)
    {
        // Don't store subrequests
        if ($event->getRequestType() !== HttpKernel::MASTER_REQUEST) {
            return;
        }

        $request = $event->getRequest();
        $session = $request->getSession();

        $routeName = $request->get('_route');
        $routeParams = $request->get('_route_params');
        if ($routeName[0] == "_") {
            return;
        }
        $routeData = ['name' => $routeName, 'params' => $routeParams];

        // Don't store the same route twice
        $thisRoute = $session->get('this_route', []);
        if ($thisRoute == $routeData) {
            return;
        }
        $session->set('last_route', $thisRoute);
        $session->set('this_route', $routeData);
    }
 }

services.yml

myapp.last_route_event_listener:
    class:  MyApp\EventListener\LastRouteListener
    tags:
        - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: 30 }

最后,我添加了一个新控制器,通过应用程序导航栏中的下拉菜单,显示用户正在查看的当前页面,但年份不同

ArchiveController.php

class ArchiveController extends Controller
{
    public function switchYearAction(Request $request, $year)
    {
        $session = $request->getSession();
        $lastRoute = $session->get('last_route');
        $route = $lastRoute["name"];
        $routeParams = $lastRoute["params"];

        if (array_key_exists("year", $routeParams)) {
            $routeParams["year"] = $year;
            $session->set("current_year", $year);
        }

        return $this->redirect($this->generateUrl($route, $routeParams));
    }
}

到了这里,一切正常。如果用户选择了其他日期,则应用程序将显示相同的页面,但选择了新日期。

然而,并且存在我的问题,如果从上一年开始,用户点击页面中的链接,我们就会回到实际年份。很正常,因为应用程序中的Twig路径并没有填满“年份”。路由参数,路由器默认提供当前年份。

所以,我的问题是:如何将所选年份保留在内存中,并将其用作路由参数?

首先,我考虑过设置本地var' current_year'当应用程序使用switchYearAction()时,但Symfony返回错误(' Frozen变量')

接下来,我考虑过使用session var存储所选年份,但我无法访问我的扩展MyAppExtension中的会话。

可能有第三种解决方案,包括更新所有Twig路径和Controller redirect(),但它代表了一些很多行来编辑......

或者也许使用routeEventListener ......但我不知道如何继续。

提前谢谢你。

2 个答案:

答案 0 :(得分:0)

您可以使用<my-app>访问Twig中的应用程序会话。所以这样的事情是可能的:

user

由于您在当前年份中有一些逻辑(如果它没有在会话回退到当前年份等等),twig extension提供获取当前功能的功能一年可能是一个更好的方式。快速,未经测试的例子:

{{ app.session }}

然后将其添加到您的容器中并使用{{ url('myapp.whatever', {year: app.session.get('current_year')}) }} 标记对其进行标记。

<?php
use Symfony\Component\HttpFoundation\Session\SessionInterface;

class CurrentYearExtension extends \Twig_Extension
{
    private $session;
    private $defaultYear;

    public function __construct(SessionInterface $session, $defaultYear)
    {
        $this->session = $session;
        $this->defaultYear = $defaultYear;
    }

    public function getFunctions()
    {
        return [
            new \Twig_SimpleFunction('current_year', [$this, 'currentYear']),
        ];
    }

    public function currentYear()
    {
        return $this->session->get('current_year') ?: $this->defaultYear;
    }
}

并在路线生成中使用它:

twig.extension

如果您需要当前年份以外的其他地方,请从树枝延伸部分中取出可重复使用的物品,并将其与扩展部分和其他地方一起使用。

答案 1 :(得分:0)

感谢@chrisguitarguy给出的答案:https://stackoverflow.com/a/13495302/1031898我找到了解决问题的方法。

事实上,我可以使用routeListener来完成这项工作。 我只需要实现路由器接口。

LastRouteListener.php(已更新)

class LastRouteListener
{
    private $router;

    public function __construct(RouterInterface $router)
    {
        $this->router = $router;
    }

    public function onKernelRequest(GetResponseEvent $event)
    {
        // Don't store subrequests
        if ($event->getRequestType() !== HttpKernel::MASTER_REQUEST) {
            return;
        }

        $request = $event->getRequest();
        $session = $request->getSession();
        $context = $this->router->getContext();

        // If current_year exists in session, replace route parameter with it
        if ($session->has('current_year')) {
            $context->setParameter('year', $session->get('current_year'));
        }
        // Else, we set the current year by default 
        else {
            $context->setParameter('year', date('Y'));
        }

        $routeName = $request->get('_route');
        $routeParams = $request->get('_route_params');
        if ($routeName[0] == "_") {
            return;
        }
        $routeData = ['name' => $routeName, 'params' => $routeParams];

        // On ne sauvegarde pas la même route plusieurs fois
        $thisRoute = $session->get('this_route', []);
        if ($thisRoute == $routeData) {
            return;
        }
        $session->set('last_route', $thisRoute);
        $session->set('this_route', $routeData);
    }
}

不要忘记在services.yml中注入@router参数

myapp.last_route_event_listener:
    class:  MyApp\EventListener\LastRouteListener
    tags:
        - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: 30 }
    arguments: ['@router']

然后,不再需要在路由配置中使用'current_year'默认参数。

routes.yml

mes_routes:
    resource: "mes_routes.yml"
    prefix:   /{year}

MyAppExtension.php

class MyAppExtension extends Extension
{
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);

        $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services.yml');
}

}