简体   繁体   English

Symfony2:动态路由参数

[英]Symfony2: dynamic routing parameter

I'm turning to this forum because I can't find a valid solution to my problem. 我转向这个论坛,因为我无法找到解决问题的有效方法。

I have taken over the management of a Symfony2 application which processes orders, invoices... inside a company and the problem is that there isn't archiving functions on it. 我已经接管了一个Symfony2应用程序的管理,该应用程序处理公司内部的订单,发票......问题是它上面没有归档功能。 So, the manager asked me to add archiving 'by year' functionalities to the application (simply display data depending on a chosen year). 因此,经理要求我将“按年”存档功能添加到应用程序中(根据所选年份显示数据)。

So, I decided to prefix all application routes by /{year}/, parameter which will match the year the manager want to see and, as all the documents are dated, I just have to update Doctrine requests for picking those that match the chosen year. 所以,我决定将所有应用程序路由加上/ {year} /,这个参数将与经理想要查看的年份相匹配,并且由于所有文档都已过时,我只需更新Doctrine请求以选择与所选文档匹配的文档年。 So far no problems. 到目前为止没有问题。

routes.yml routes.yml

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

With this, I have created a Symfony Extension which fills the 'current_year' var by default in my route definition, with the actual year if no year is provided. 有了这个,我创建了一个Symfony扩展,它在我的路由定义中默认填充'current_year'var,如果没有提供年份,则为实际年份。

MyAppExtension.php 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"));
    }
}

Next, I have created a RouteListener that stores a previous route and its parameters inside a session var, when a user displays a new page (in order to display a same page but with a different year next) 接下来,我创建了一个RouteListener,当用户显示一个新页面时,它会在会话var中存储先前的路由及其参数(为了显示相同的页面但下一年有不同的年份)

LastRouteListener.php 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 services.yml

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

And finally, I have added a new controller which, via a dropdown menu in the application navbar, displays the current page the user is viewing, but with a different year 最后,我添加了一个新的控制器,通过应用程序导航栏中的下拉菜单,显示用户正在查看的当前页面,但具有不同的年份

ArchiveController.php 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));
    }
}

Arrived here, everything work. 到了这里,一切顺利。 If a user chose an other date, the application will display the same page but with the new date chosen. 如果用户选择了其他日期,则应用程序将显示相同的页面,但会选择新的日期。

However , and there is my problem, if, from a previous year, the user clicks on a link in the page, we come back to the actual year. 但是 ,我的问题是,如果从前一年开始,用户点击页面中的链接,我们就会回到实际年份。 Quite normal, because Twig paths in the application doesn't fill the 'year' routing parameter, and the router provide the current year by default. 很正常,因为应用程序中的Twig路径没有填充'year'路由参数,并且路由器默认提供当前年份。

So, my question is : How can I keep the chosen year in memory, and use it as a route parameter ? 所以,我的问题是: 如何将所选年份保留在内存中,并将其用作路由参数?

First, I had thought about setting the local var 'current_year' when the application uses the switchYearAction(), but Symfony returns an error ('Frozen variable') 首先,我曾考虑过在应用程序使用switchYearAction()时设置局部var'current_year',但Symfony会返回错误('Frozen variable')

Next, I had thought about using a session var to store the chosen year, but I can't access to the session within my extension MyAppExtension. 接下来,我曾考虑使用session var来存储所选年份,但我无法访问我的扩展MyAppExtension中的会话。

There might be a third solution which consists in update all Twig paths and Controller redirect(), but it represents some much line to edit... 可能有第三个解决方案,包括更新所有Twig路径和Controller redirect(),但它代表了一些很多行来编辑......

Or maybe with a routeEventListener... but I don't know how to proceed. 或者也许使用routeEventListener ......但我不知道如何继续。

Thanks you in advance. 提前谢谢你。

You can access the application session in Twig using {{ app.session }} . 您可以使用{{ app.session }}访问Twig中的应用程序会话。 So something like this is possible: 所以这样的事情是可能的:

{{ url('myapp.whatever', {year: app.session.get('current_year')}) }}

Since you have a bit of logic around your current year stuff (if it's not set in the session fallback to the current year, etc), a twig extension that provides a funtion to fetch the current year may be a better way to go. 由于你有一些关于你当前年份的逻辑(如果它没有设置在会话回落到当前年份等),一个枝条扩展提供了获取当前年份的功能可能是一个更好的方法。 Quick, untested example: 快速,未经测试的例子:

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

Then add it to your container and tag it with the twig.extension tag. 然后将其添加到容器中并使用twig.extension标记twig.extension标记。

<service id="myapp.currentyear_extension" class="CurrentYearExtension" public="false">
    <argument type="service" id="session" />
    <argument>%current_year%</argument>
    <tag name="twig.extension" />
</service>

And use it in your route generation: 并在路由生成中使用它:

{{ url('myapp.whatever', {year: current_year()}) }}

If you need the current year other places than twig, then pull a re-usable object out of the twig extension and use that both with the extension and elsewhere. 如果您需要当前年份以外的其他地方,那么从树枝延伸部分中拉出一个可重复使用的物体,并将其与延伸部分和其他地方一起使用。

Thanks to the answer given by @chrisguitarguy : https://stackoverflow.com/a/13495302/1031898 I found a way to resolve my problem. 感谢@chrisguitarguy给出的答案: https ://stackoverflow.com/a/13495302/1031898我找到了解决问题的方法。

In fact, I could use my routeListener to do the job. 事实上,我可以使用routeListener来完成这项工作。 I just needed to implement the Router Interface. 我只需要实现路由器接口。

LastRouteListener.php (updated) 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);
    }
}

Don't forget to inject the @router argument in services.yml 不要忘记在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']

And then, no need to use 'current_year' default parameter in route config anymore. 然后,不再需要在路由配置中使用'current_year'默认参数。

routes.yml routes.yml

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

MyAppExtension.php 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');
}

} }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM