简体   繁体   English

Symfony2中的beforeFilter函数未重定向

[英]beforeFilter function not redirecting in Symfony2

I have implemented following code to run a code on before any action of any controller. 我实现了以下代码,以便在执行任何控制器的任何操作之前先运行代码。 However, the beforeFilter() function not redirecting to the route I have specified. 但是, beforeFilter()函数不会重定向到我指定的路由。 Instead it takes the user to the location where the user clicked. 而是将用户带到用户单击的位置。

//My Listener
namespace Edu\AccountBundle\EventListener;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;

class BeforeControllerListener
{
    public function onKernelController(FilterControllerEvent $event)
    {
        $controller = $event->getController();
        if (!is_array($controller))
        {
            //not a controller do nothing
            return;
        }
        $controllerObject = $controller[0];
        if (is_object($controllerObject) && method_exists($controllerObject, "beforeFilter"))
        //Set a predefined function to execute Before any controller Executes its any method
        {
            $controllerObject->beforeFilter();
        }
    }
}
//I have registered it already

//My Controller
class LedgerController extends Controller
{        
    public function beforeFilter()
    {   
        $commonFunction = new CommonFunctions();
        $dm = $this->getDocumentManager();
        if ($commonFunction->checkFinancialYear($dm) == 0 ) {
            $this->get('session')->getFlashBag()->add('error', 'Sorry');
            return $this->redirect($this->generateUrl('financialyear'));//Here it is not redirecting
        }
    }
}

public function indexAction() {}

Please help, What is missing in it. 请帮助,其中缺少什么。 Thanks Advance 谢谢前进

$this->redirect() controller function simply creates an instance of RedirectResponse . $this->redirect()控制器函数仅创建RedirectResponse的实例。 As with any other response, it needs to be either returned from a controller, or set on an event. 与其他任何响应一样,它需要从控制器返回或在事件上设置。 Your method is not a controller, therefore you have to set the response on the event. 您的方法不是控制器,因此您必须设置事件的响应。

However, you cannot really set a response on the FilterControllerEvent as it is meant to either update the controller, or change it completely ( setController ). 但是,您实际上不能在FilterControllerEvent上设置响应,因为它意味着要么更新控制器,要么完全更改它( setController )。 You can do it with other events, like the kernel.request . 您可以使用其他事件来完成它,例如kernel.request However, you won't have access to the controller there. 但是,您将无法在那里访问控制器。

You might try set a callback with setController which would call your beforeFilter() . 您可以尝试使用setController设置一个回调,该回调将调用beforeFilter() However, you wouldn't have access to controller arguments, so you won't really be able to call the original controller if beforeFilter didn't return a response. 但是,您将无权访问控制器参数,因此,如果beforeFilter没有返回响应,则您实际上将无法调用原始控制器。

Finally you might try to throw an exception and handle it with an exception listener. 最后,您可能会尝试引发异常并使用异常侦听器进行处理。

I don't see why making things this complex if you can simply call your method in the controller: 如果您可以简单地在控制器中调用方法,我看不出为什么使事情变得如此复杂:

public function myAction()
{
    if ($response = $this->beforeFilter()) {
        return $response;
    }

    // ....
}

I would suggest you follow the Symfony suggestions for setting up before and after filters , where you perform your functionality within the filter itself, rather than trying to create a beforeFilter() function in your controller that is executed. 我建议您遵循Symfony的建议,在过滤器之前和之后进行设置,在过滤器本身中执行功能,而不要尝试在执行的控制器中创建beforeFilter()函数。 It will allow you to achieve what you want - the function being called before every controller action - as well as not having to muddy up your controller(s) with additional code. 它将使您实现所需的功能-在每次控制器操作之前都会调用该函数-且不必使您的控制器变得多余。 In your case, you would also want to inject the Symfony session to the filter: 对于您的情况,您还想将Symfony会话注入到过滤器中:

# app/config/services.yml
services:
    app.before_controller_listener:
        class: AppBundle\EventListener\BeforeControllerListener
        arguments: ['@session', '@router', '@doctrine_mongodb.odm.document_manager']
        tags:
            - { name: kernel.event_listener, event: kernel.controller, method: onKernelController }

Then you'll create your before listener, which will need the Symony session and routing services, as well as the MongoDB document manager (making that assumption based on your profile). 然后,您将创建之前的侦听器,这将需要Symony会话和路由服务以及MongoDB文档管理器(根据您的个人资料进行此假设)。

// src/AppBundle/EventListener/BeforeControllerListener.php
namespace AppBundle\EventListener;

use Doctrine\ODM\MongoDB\DocumentManager;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use AppBundle\Controller\LedgerController;
use AppBundle\Path\To\Your\CommonFunctions;

class BeforeControllerListener
{
    private $session;
    private $router;
    private $documentManager;
    private $commonFunctions;

    public function __construct(Session $session, Router $router, DocumentManager $dm)
    {
        $this->session = $session;
        $this->router = $router;
        $this->dm = $dm;
        $this->commonFunctions = new CommonFunctions();
    }

    public function onKernelController(FilterControllerEvent $event)
    {
        $controller = $event->getController();

        if (!is_array($controller)) {
            return;
        }

        if ($controller[0] instanceof LedgerController) {
            if ($this->commonFunctions->checkFinancialYear($this->dm) !== 0 ) {
                return;
            }

            $this->session->getFlashBag()->add('error', 'Sorry');
            $redirectUrl= $this->router->generate('financialyear');

            $event->setController(function() use ($redirectUrl) {
                return new RedirectResponse($redirectUrl);
            });
        }
    }
}

If you are in fact using the Symfony CMF then the Router might actually be ChainRouter and your use statement for the router would change to use Symfony\\Cmf\\Component\\Routing\\ChainRouter; 如果实际上使用的是Symfony CMF,则路由器实际上可能是ChainRouter并且您对路由器的use声明将更改为use Symfony\\Cmf\\Component\\Routing\\ChainRouter;

There are a few additional things here you might want to reconsider - for instance, if the CommonFunctions class needs DocumentManager , you might just want to make your CommonFunctions class a service that injects the DocumentManager automatically. 您可能还需要重新考虑一些其他事项-例如,如果CommonFunctions类需要DocumentManager ,则可能只想使CommonFunctions类成为自动注入DocumentManager的服务。 Then in this service you would only have to inject your common functions service instead of the document manager. 然后,在此服务中,您只需注入公共功能服务,而不是文档管理器。

Either way what is happening here is that we are checking that we are in the LedgerController , then checking whether or not we want to redirect, and if so we overwrite the entire Controller via a callback. 无论哪种方式,这里都在检查我们是否在LedgerController ,然后检查是否要重定向,如果要重定向,则通过回调覆盖整个Controller。 This sets the redirect response to your route and performs the redirect. 这会将重定向响应设置为您的路由并执行重定向。

If you want this check on every single controller you could simply eliminate the check for LedgerController . 如果要在每个控制器上执行此检查,则可以简单地取消对LedgerController的检查。 .

public function onKernelController(FilterControllerEvent $event)
{
    $request = $event->getRequest();
    $response = new Response();

    // Matched route
    $_route  = $request->attributes->get('_route');

    // Matched controller
    $_controller = $request->attributes->get('_controller');

    $params = array(); //Your params

    $route = $event->getRequest()->get('_route');
    $redirectUrl = $url = $this->container->get('router')->generate($route,$params);


    $event->setController(function() use ($redirectUrl) {
         return new RedirectResponse($redirectUrl);
    });
}

Cheers !! 干杯!

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

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