我需要改进我在Symfony2中所做的工作的应用,因为我们正在扩展到国际水平,我们必须实施一个时区系统,以便每个用户都可以修改它们的日期接收通知和其他警报。我们的原始时区是UTC + 1(欧洲/马德里),因此我们必须使用此时区保存数据库中的日期。但是当涉及到应用程序时,它应该能够在设置中显示用户配置的时间。
如何在Symfony 2中实现它,以便我不必修改所有控制器和树枝模板?
可以在事件监听器中完成吗?
答案 0 :(得分:1)
最后,我找到了一个解决方案,获取您的信息并搜索类似的内容,特别是这个 - > how to get the type of a doctrine entity property帮助我制定了最终的代码。
这就是我所做的:
我已将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时区。
然后,在引导ORM之前,我已经覆盖了日期时间类型:
Type::overrideType('datetime', UTCDateTimeType::class);
Type::overrideType('datetimetz', UTCDateTimeType::class);
我编辑了我的用户实体以获得时区字段(PHP时区标识符)
在LoginListener上 - > onSecurityInteractiveLogin,我注入了Session,当用户登录时,我将“timezone”变量设置为Session与用户时区字段。
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event){
$user = $event->getAuthenticationToken()->getUser();
$this->session->set("timezone",new \DateTimeZone($user->getTimeZone()));
// ...
}
我制作了一个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"));
}
}
}
}
}
在 postLoad 方法中,我搜索 datetime 类型的每个属性,然后设置为登录用户timeZone(之前在登录会话时设置)
现在,每次加载实体时,到达日期时间字段时,在呈现阶段,都会成功应用日期时间偏移量,并且对于每个用户,它都会显示为已指定。
答案 1 :(得分:-1)
1)设置默认时区,即在侦听kernel.request
事件的事件侦听器中。
2)创建侦听security.interactive_login
事件的事件侦听器,并从事件中提取用户,然后获取自己的时区设置并应用。 (An example)