无法将 kernel.view EventListener 绑定到 FOSUserBundle
cannot binding a kernel.view EventListener to FOSUserBundle
我在 kernel.view
事件监听器的帮助下分离了移动和网络请求。
逻辑是这样的:
- 如果请求来自移动设备,则加载
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);
}
}
}
我找到了解决方案,不过有点变通,现在可以正常工作了。
我将以下函数放入我的 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
提供
但是我的子 bundle 已经扩展了 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);
}
基本上我的更改通过提供 $format
替换了模板名称中的 '.html.'
部分,该部分通过 onKernelRequest
EventListener 设置。
不要忘记在 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 }
在 Symfony2 上调试与事件相关的问题时,您应该考虑一些事项。
可以停止事件传播
- 可能是另一个侦听器正在侦听同一个事件并通过调用
$event->stopPropagation()
停止事件传播。在这种情况下,请确保首先执行您的侦听器(请参阅下面的第 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 }
The other optional tag attribute is called priority, which defaults to 0 and it controls the order in which listeners are executed (the highest the priority, the earlier a listener is executed). This is useful when you need to guarantee that one listener is executed before another. The priorities of the internal Symfony listeners usually range from -255 to 255 but your own listeners can use any positive or negative integer.
来源:http://symfony.com/doc/2.7/cookbook/event_dispatcher/event_listener.html#creating-an-event-listener
通常这些事件的调度在 bootstrap 文件中完成
- 确保重新生成您的 bootstrap 文件(并清除缓存!),尤其是当您使用优先级 and/or 您的配置时。
composer run-script post-update-cmd
php app/console cache:clear --env=dev
作曲家 post-update-cmd
将重新生成您的 bootstrap 文件,但它还会执行其他操作,例如重新安装您的资产,这可能是您不需要的。要重新生成 bootstrap 文件,请检查我的答案 here.
我在 kernel.view
事件监听器的帮助下分离了移动和网络请求。
逻辑是这样的:
- 如果请求来自移动设备,则加载
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);
}
}
}
我找到了解决方案,不过有点变通,现在可以正常工作了。
我将以下函数放入我的 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
但是我的子 bundle 已经扩展了 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);
}
基本上我的更改通过提供 $format
替换了模板名称中的 '.html.'
部分,该部分通过 onKernelRequest
EventListener 设置。
不要忘记在 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 }
在 Symfony2 上调试与事件相关的问题时,您应该考虑一些事项。
可以停止事件传播
- 可能是另一个侦听器正在侦听同一个事件并通过调用
$event->stopPropagation()
停止事件传播。在这种情况下,请确保首先执行您的侦听器(请参阅下面的第 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 }
The other optional tag attribute is called priority, which defaults to 0 and it controls the order in which listeners are executed (the highest the priority, the earlier a listener is executed). This is useful when you need to guarantee that one listener is executed before another. The priorities of the internal Symfony listeners usually range from -255 to 255 but your own listeners can use any positive or negative integer.
来源:http://symfony.com/doc/2.7/cookbook/event_dispatcher/event_listener.html#creating-an-event-listener
通常这些事件的调度在 bootstrap 文件中完成
- 确保重新生成您的 bootstrap 文件(并清除缓存!),尤其是当您使用优先级 and/or 您的配置时。
composer run-script post-update-cmd php app/console cache:clear --env=dev
作曲家
post-update-cmd
将重新生成您的 bootstrap 文件,但它还会执行其他操作,例如重新安装您的资产,这可能是您不需要的。要重新生成 bootstrap 文件,请检查我的答案 here.