把locale信息“粘连”到用户的Session周期中

3.4 版本
维护中的版本

locale(即系统区域设置,国家、地区设置和语言环境设置)

symfony将locale设置,存储在Request中,这意味着该设置不可用在后续请求。在本章,你将学习如何保存这个locale到session中,以便相同的locale可以用在所有后续请求中。

创建LocaleListener 

为了模拟该locale被存储在session,你需要去创建和注册一个新的事件监听。监听器看起来像是这样的。通常情况下,_locale作为路由参数来表示locale,虽然他对你如何确定一个请求所需的locale无关紧要:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// src/AppBundle/EventListener/LocaleListener.php
namespace AppBundle\EventListener;
 
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
class LocaleListener implements EventSubscriberInterface
{
    private $defaultLocale;
 
    public function __construct($defaultLocale = 'en')
    {
        $this->defaultLocale = $defaultLocale;
    }
 
    public function onKernelRequest(GetResponseEvent $event)
    {
        $request = $event->getRequest();
        if (!$request->hasPreviousSession()) {
            return;
        }
 
        // try to see if the locale has been set as a _locale routing parameter
        if ($locale = $request->attributes->get('_locale')) {
            $request->getSession()->set('_locale', $locale);
        } else {
            // if no explicit locale has been set on this request, use one from the session
            $request->setLocale($request->getSession()->get('_locale', $this->defaultLocale));
        }
    }
 
    public static function getSubscribedEvents()
    {
        return array(
            // must be registered after the default Locale listener
            KernelEvents::REQUEST => array(array('onKernelRequest', 15)),
        );
    }
}

然后注册监听器:

1
2
3
4
5
6
services:
    app.locale_listener:
        class: AppBundle\EventListener\LocaleListener
        arguments: ['%kernel.default_locale%']
        tags:
            - { name: kernel.event_subscriber }
1
2
3
4
5
6
<service id="app.locale_listener"
    class="AppBundle\EventListener\LocaleListener">
    <argument>%kernel.default_locale%</argument>
 
    <tag name="kernel.event_subscriber" />
</service>
1
2
3
4
5
6
7
8
9
use Symfony\Component\DependencyInjection\Definition;
 
$container
    ->setDefinition('app.locale_listener', new Definition(
        'AppBundle\EventListener\LocaleListener',
        array('%kernel.default_locale%')
    ))
    ->addTag('kernel.event_subscriber')
;

就是这样!改变用户locale并看到他粘在整个请求中,现在庆祝一下吧。记住,去获取用户locale,请始终使用这个Request::getLocale方法:

1
2
3
4
5
6
7
// from a controller...
use Symfony\Component\HttpFoundation\Request;
 
public function indexAction(Request $request)
{
    $locale = $request->getLocale();
}

设置基于用户喜好的Locale 

你可能希望进一步提高这一技术并基于登录用户的用户实体定义locale。然而,由于LocaleListenerFirewallListener之前调用,你无法访问已登录用户,FirewallListener来负责处理认证并设置用户token在TokenStorage上。

假设你已经在你的User实体中定义了一个locale 属性并且你想要去使用它作为给定用户的locale。要做到这一点,你可以挂钩巧取到登录过程并在他们被重定向到第一页之前更新用户session的locale值。

要做到这一点,你需要一个security.interactive_login事件的事件监听器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// src/AppBundle/EventListener/UserLocaleListener.php
namespace AppBundle\EventListener;
 
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
 
/**
 * Stores the locale of the user in the session after the
 * login. This can be used by the LocaleListener afterwards.
 */
class UserLocaleListener
{
    /**
     * @var Session
     */
    private $session;
 
    public function __construct(Session $session)
    {
        $this->session = $session;
    }
 
    /**
     * @param InteractiveLoginEvent $event
     */
    public function onInteractiveLogin(InteractiveLoginEvent $event)
    {
        $user = $event->getAuthenticationToken()->getUser();
 
        if (null !== $user->getLocale()) {
            $this->session->set('_locale', $user->getLocale());
        }
    }
}

然后注册监听器:

1
2
3
4
5
6
7
# app/config/services.yml
services:
    app.user_locale_listener:
        class: AppBundle\EventListener\UserLocaleListener
        arguments: ['@session']
        tags:
            - { name: kernel.event_listener, event: security.interactive_login, method: onInteractiveLogin }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- app/config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        http://symfony.com/schema/dic/services/services-1.0.xsd">
 
    <services>
        <service id="app.user_locale_listener"
            class="AppBundle\EventListener\UserLocaleListener">
 
            <argument type="service" id="session"/>
 
            <tag name="kernel.event_listener"
                event="security.interactive_login"
                method="onInteractiveLogin" />
        </service>
    </services>
</container>
1
2
3
4
5
6
7
8
// app/config/services.php
$container
    ->register('app.user_locale_listener', 'AppBundle\EventListener\UserLocaleListener')
    ->addArgument('session')
    ->addTag(
        'kernel.event_listener',
        array('event' => 'security.interactive_login', 'method' => 'onInteractiveLogin'
    );

为了在用户更改语言偏好后立即更新语言,你需要在更新 User 实体后更新session。

本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。

登录symfonychina 发表评论或留下问题(我们会尽量回复)