【问题标题】:cannot binding a kernel.view EventListener to FOSUserBundle无法将 kernel.view EventListener 绑定到 FOSUserBundle
【发布时间】:2015-05-31 10:18:23
【问题描述】:

我在kernel.view Event Listener 的帮助下分离了移动和网络请求。

逻辑是这样的:

  • 如果请求来自手机,则加载xxx.mobile.twig
  • 如果请求来自网络,则加载xxx.html.twig

这与我的 CustomBundle 一起使用没有任何问题。除此之外,我还将 FOSUserBundle 和 HWIOAuthBundle 与他们的一些路由一起使用。我检查了var/logs/dev.log,但看不到有关这些捆绑路由的kernel.view 事件,最终我的听众无法使用这些捆绑。

您能否告诉我如何为这些捆绑包绑定到kernel.view 事件?

/**
 * @param GetResponseForControllerResultEvent $event
 * @return bool
 */
public function onKernelView(GetResponseForControllerResultEvent $event)
{
    if (!$this->isMobileRequest($event->getRequest()->headers->get('user-agent'))) {
        return false;
    }

    $template = $event->getRequest()->attributes->get('_template');
    if (!$template) {
        return false;
    }

    $templateReference = $this->templateNameParser->parse($template);

    if ($templateReference->get('format') == 'html' && $templateReference->get('bundle') == 'CustomBundle') {

        $mobileTemplate = sprintf(
            '%s:%s:%s.mobile.twig',
            $templateReference->get('bundle'),
            $templateReference->get('controller'),
            $templateReference->get('name')
        );

        if ($this->templating->exists($mobileTemplate)) {
            $templateReference->set('format', 'mobile');
            $event->getRequest()->attributes->set('_template', $templateReference);
        }
    }
}

【问题讨论】:

    标签: symfony fosuserbundle event-listener hwioauthbundle


    【解决方案1】:

    在 Symfony2 上调试与事件相关的问题时,您应该考虑一些事项。

    1. 可以停止事件传播

      • 可能是另一个侦听器正在侦听同一事件,并通过调用$event->stopPropagation() 来停止事件传播。在这种情况下,请确保首先执行您的侦听器(请参阅下面的第 2 点)。
    2. 事件监听器有优先级

      • 定义侦听器时,您可以设置其优先级,如下所示:

      view_response_listener:
          class: AppBundle\EventListener\ViewResponseListener
          tags:
              # The highest the priority, the earlier a listener is executed
              # @see http://symfony.com/doc/2.7/cookbook/event_dispatcher/event_listener.html#creating-an-event-listener
              - { name: kernel.event_listener, event: kernel.view, method: onKernelView, priority: 101 }
      

      另一个可选的标签属性叫做priority,默认为0,它控制监听器的执行顺序(优先级越高,监听器越早执行)。当您需要保证一个侦听器在另一个侦听器之前执行时,这很有用。 Symfony 内部侦听器的优先级通常在 -255 到 255 之间,但您自己的侦听器可以使用任何正整数或负整数。

      来源http://symfony.com/doc/2.7/cookbook/event_dispatcher/event_listener.html#creating-an-event-listener

    3. 通常这些事件的分发是在引导文件中完成的

      • 确保重新生成您的引导文件(并清除缓存,因为您正在使用它!),尤其是在您使用优先级和/或配置时。

      composer run-script post-update-cmd
      php app/console cache:clear --env=dev
      

      作曲家post-update-cmd 将重新生成您的引导文件,但它还会执行其他操作,例如重新安装您的资产,这可能是您不需要的。要重新生成引导文件,请检查我的回答 here

    【讨论】:

      【解决方案2】:

      我找到了解决办法,不过有点变通方法,现在可以正常工作了。

      我将以下函数添加到我的MobileTemplateListener.php 文件中。 更多细节在这里 -> http://www.99bugs.com/handling-mobile-template-switching-in-symfony2/

          /**
       * @param GetResponseEvent $event
       */
      public function onKernelRequest(GetResponseEvent $event)
      {
          $request = $event->getRequest();
      
          if ($this->isMobileRequest($request->headers->get('user-agent')))
          {
              //ONLY AFFECT HTML REQUESTS
              //THIS ENSURES THAT YOUR JSON REQUESTS TO E.G. REST API, DO NOT GET SERVED TEXT/HTML CONTENT-TYPE
              if ($request->getRequestFormat() == "html")
              {
                  $request->setRequestFormat('mobile');
              }
          }
      }
      /**
       * Returns true if request is from mobile device, otherwise false
       * @return boolean mobileUA
       */
      private function isMobileRequest($userAgent)
      {
          if (preg_match('/(android|blackberry|iphone|ipad|phone|playbook|mobile)/i', $userAgent)) {
              return true;
          }
      
          return false;
      }
      

      结果当kernel.request事件监听器开始处理时,它正在设置值为mobile的格式

      我通过子包使用 FOSUserBundle 来满足我的需要。您可以在这里找到更多详细信息 -> https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/overriding_controllers.md

      例如:我们假设SecurityController.php 我在我的 UserBundle 下创建了一个名为 SecurityController.php 的文件,如下所示。

      <?php
      
      namespace Acme\UserBundle\Controller;
      
      use Symfony\Component\HttpFoundation\RedirectResponse;
      use FOS\UserBundle\Controller\SecurityController as BaseController;
      use Symfony\Component\HttpFoundation\Request;
      use Acme\UserBundle\Overrides\ControllerOverrideRenderTrait;
      
      class SecurityController extends BaseController
      {
          use ControllerOverrideRenderTrait;
      
          public function loginAction(Request $request)
          {
              $securityContext = $this->container->get('security.context');
              $router = $this->container->get('router');
      
              if ($securityContext->isGranted('IS_AUTHENTICATED_FULLY')) {
                  return new RedirectResponse($router->generate('my_profile_dashboard'), 307);
              }
              return parent::loginAction($request);
          }
      }
      

      对于所有其他 FOS 控制器,我必须覆盖渲染功能。由smyfony提供Symfony\Bundle\FrameworkBundle\Controller

      但是我的子包已经扩展了 FOSUserBundle 的控制器,在不重复代码的情况下覆盖它的唯一方法是使用特征。

      我创建了一个特征如下。

          <?php
      namespace Acme\UserBundle\Overrides;
      
      use Symfony\Component\HttpFoundation\Response;
      
      trait ControllerOverrideRenderTrait {
          /**
           * This overrides vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php
           * Renders a view.
           *
           * @param string   $view       The view name
           * @param array    $parameters An array of parameters to pass to the view
           * @param Response $response   A response instance
           *
           * @return Response A Response instance
           */
          public function render($view, array $parameters = array(), Response $response = null)
          {
              $format = $this->getRequest()->getRequestFormat();
              $view = str_replace('.html.', '.' . $format . '.', $view);
              return $this->container->get('templating')->renderResponse($view, $parameters, $response);
          }
      }
      

      symfony提供的原函数如下。

          /**
       * Renders a view.
       *
       * @param string   $view       The view name
       * @param array    $parameters An array of parameters to pass to the view
       * @param Response $response   A response instance
       *
       * @return Response A Response instance
       */
      public function render($view, array $parameters = array(), Response $response = null)
      {
          return $this->container->get('templating')->renderResponse($view, $parameters, $response);
      }
      

      基本上我的更改通过提供通过onKernelRequest EventListener 设置的$format 来替换模板名称中的'.html.' 部分。

      别忘了在 services.yml 中添加你的服务定义

      services:
          acme.frontend.listener.mobile_template_listener:
              class: Acme\FrontendBundle\EventListener\MobileTemplateListener
              arguments: ['@templating', '@templating.name_parser']
              tags:
                  - { name: kernel.event_listener, event: kernel.view, method: onKernelView }
                  - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-05-01
        • 1970-01-01
        • 2021-02-20
        • 2012-03-18
        • 2012-07-04
        • 2011-08-27
        • 1970-01-01
        相关资源
        最近更新 更多