如何在Symfony2中以编程方式设置DateTime时区

时间:2017-02-22 09:58:27

标签: symfony doctrine-orm timezone

我需要改进我在Symfony2中所做的工作的应用,因为我们正在扩展到国际水平,我们必须实施一个时区系统,以便每个用户都可以修改它们的日期接收通知和其他警报。我们的原始时区是UTC + 1(欧洲/马德里),因此我们必须使用此时区保存数据库中的日期。但是当涉及到应用程序时,它应该能够在设置中显示用户配置的时间。

如何在Symfony 2中实现它,以便我不必修改所有控制器和树枝模板?

可以在事件监听器中完成吗?

2 个答案:

答案 0 :(得分:1)

最后,我找到了一个解决方案,获取您的信息并搜索类似的内容,特别是这个 - > how to get the type of a doctrine entity property帮助我制定了最终的代码。

这就是我所做的:

  1. 我已将Doctrine的DateTime扩展为新类UTCDateTimeType:

    use Doctrine\DBAL\Platforms\AbstractPlatform;
    use Doctrine\DBAL\Types\ConversionException;
    use Doctrine\DBAL\Types\DateTimeType;
    
    class UTCDateTimeType extends DateTimeType {
    
        static private $utc;
    
        static function getUtc(){
    
            return self::$utc;
        }
    
        public function convertToDatabaseValue($value, AbstractPlatform $platform)
        {
            if ($value instanceof \DateTime) {
                $value->setTimezone(self::getUtc());
            }
    
            return parent::convertToDatabaseValue($value, $platform);
        }
    
        public function convertToPHPValue($value, AbstractPlatform $platform)
        {   
    
            if (null === $value || $value instanceof \DateTime) {
                return $value;
            }
    
            $converted = \DateTime::createFromFormat(
                $platform->getDateTimeFormatString(),
                $value,
                self::$utc ? self::$utc : self::$utc = new \DateTimeZone('Europe/Madrid')
            );
    
            if (! $converted) {
                throw ConversionException::conversionFailedFormat(
                    $value,
                    $this->getName(),
                    $platform->getDateTimeFormatString()
                );
            }
    
            return $converted;
        }
    } 
    

    因此,当获取或持久化datetime的数据时,它总是在我的UTC时区。

  2. 然后,在引导ORM之前,我已经覆盖了日期时间类型:

    Type::overrideType('datetime', UTCDateTimeType::class);
    Type::overrideType('datetimetz', UTCDateTimeType::class);
    
  3. 我编辑了我的用户实体以获得时区字段(PHP时区标识符)

  4. 在LoginListener上 - > onSecurityInteractiveLogin,我注入了Session,当用户登录时,我将“timezone”变量设置为Session与用户时区字段。

    public function onSecurityInteractiveLogin(InteractiveLoginEvent $event){
        $user = $event->getAuthenticationToken()->getUser();
        $this->session->set("timezone",new \DateTimeZone($user->getTimeZone()));
        // ...
    }
    
  5. 我制作了一个TimeZoneListener,用于监听 postLoad 学说事件(当实体从DDBB完全加载时触发)

    use Symfony\Component\Routing\RouterInterface;
    use Symfony\Component\HttpFoundation\Session\Session;
    use Symfony\Component\Translation\TranslatorInterface;
    use Doctrine\ORM\Event\LifecycleEventArgs;
    use Doctrine\Common\Annotations\AnnotationReader;
    
    class TimeZoneListener {
    
        protected $session;
    
        protected $container;
    
        protected $router;
    
        protected $securityContext;
    
        protected $translator;
    
        protected $docReader;
    
        public function __construct(RouterInterface $router, Session $session, TranslatorInterface $translator, $container){
    
            $this->session = $session;
            $this->router = $router;
            $this->translator = $translator;
            $this->container = $container;
            $this->docReader = new AnnotationReader();
        }
    
        public function postLoad(LifecycleEventArgs $args){
    
            $reader = $this->docReader;
            $entity = $args->getEntity();
            $reflect = new \ReflectionClass($entity);
    
            $props = $reflect->getProperties();
    
            foreach($props as $prop){
    
                $docInfos = $reader->getPropertyAnnotations($prop);
                foreach($docInfos as $info){
    
                    if(!$info instanceof \Doctrine\ORM\Mapping\Column) continue;
                    if($info->type !== "datetime") continue;
    
                    $getDateMethod = 'get'.ucfirst($prop->getName());
    
                    $val = $entity->{$getDateMethod}();
                    if($val){
                        $val->setTimeZone($this->session->get("timezone") ? $this->session->get("timezone") : new \DateTimeZone("Europe/Madrid"));
    
                    }
    
                }
            }
        }
    }
    
  6. postLoad 方法中,我搜索 datetime 类型的每个属性,然后设置为登录用户timeZone(之前在登录会话时设置)

  7. 现在,每次加载实体时,到达日期时间字段时,在呈现阶段,都会成功应用日期时间偏移量,并且对于每个用户,它都会显示为已指定。

答案 1 :(得分:-1)

1)设置默认时区,即在侦听kernel.request事件的事件侦听器中。

2)创建侦听security.interactive_login事件的事件侦听器,并从事件中提取用户,然后获取自己的时区设置并应用。 (An example