Temporäre Lokalisierung auf Symfony 4 + Twig

Die Notwendigkeit einer vorübergehenden Lokalisierung des Produkts entsteht, wenn das Produkt in einem Ausmaß wächst, das Arbeiten in verschiedenen Zeitzonen erfordert (Nachweise). Ich möchte eine Variante einer einfachen Idee zur Lösung dieses Falls beschreiben.

Der Hintergrund ist folgender: Wir haben ein Nischen-CRM / ERP-System entwickelt, und dann wurde uns gesagt, dass sie morgen mit diesem System in einem Franchise von Wladiwostok nach Kaliningrad arbeiten würden. Leider wurde ein solches Szenario ursprünglich nicht durchdacht, und wir begannen zu lernen, wie dies mit minimalen Kosten und maximalem Komfort zu tun ist.

Insgesamt wurden drei Aufgaben erweitert: Wie wir die Daten ausgeben und wie wir sie eingeben, und dazwischen die Aufgabe, wie wir alles speichern. Da die Zeit, wie Sie wissen, relativ wörtlich und im übertragenen Sinne ist, wurde beschlossen, die Zeit wie zuvor in Moskau UTC + 3 zu speichern, sie jedoch am Ein- und Ausgang zu verarbeiten (und zu beachten, dass der Referenzpunkt UTC + 3 ist). Natürlich haben wir verstanden, dass es in dieser und anderen Richtungen andere Lösungen gibt. Sie können alle vorhandenen Einträge in UTC + -0 konvertieren und spezielle Typen im DBMS verwenden, in denen die Zeitzone gespeichert ist. Sie können diesen benutzerdefinierten Typ selbst schreiben, wenn die Datenbank solche Funktionen plötzlich nicht mehr vollständig unterstützt. Aber nach dem Prinzip der Einfachheit gingen wir den vorgeschlagenen Weg, umso mehr verlor er auf den ersten Blick nicht viel an den Rest.und die Logik zum Bestimmen der gewünschten Zeitzone war ziemlich einfach.

Nachdem Moskau zu einem Referenzpunkt geworden war, haben wir jedem Benutzer einen benutzerdefinierten Zeitzonenparameter sowie eine Reihe verwandter Entitäten (Organisation, Stadt, Anwendungen, Geschäfte usw.) hinzugefügt. Dann konnte eindeutig festgestellt werden, in welcher Zeitzone der Benutzer oder die Entität, mit der er arbeitet, arbeitet. Die Logik dort ist Standard und genau oft projektspezifisch. Wir haben diese Logik in den Service eingebunden und eine Zeitzone erhalten, in der Sie sie benötigen

$localizationService->getTimezone();

Die Entscheidung, Daten in den Vorlagen zu lokalisieren, war wie folgt: Beim Initialisieren von Twig-Erweiterungen wurde die Zeitzone auf die gewünschte geändert:

function __construct(Environment $twig, LocalizationService $localizationService) {
    $twig->getExtension('Twig_Extension_Core')->setTimezone($localizationService->getTimezone());
}

Unsere Situation wurde durch die Tatsache weiter erschwert, dass nach Anzeige einer Datums- und Uhrzeitangabe ein Index „01.01.2020 12:30 (Moskau)“ erstellt werden muss, damit beispielsweise in einer an die Zeitzone gebundenen bedingten Reihenfolge / Aufgabe / Transaktion Informationen über die Uhrzeit vorliegen Gürtel. Aus praktischen Gründen ist dies erforderlich, damit ein einzelnes Callcenter im Rahmen einer Aufgabe / Anwendung / Transaktion bequem mit verschiedenen Zeitzonen arbeiten kann.

Die gesamte Logik zum Bestimmen der Priorität von Zeitzonen wurde in die oben erwähnte getTimezone eingebunden.

Außerdem waren wir mit der Tatsache konfrontiert, dass Sie, wenn Sie Ihren Zweigfilter oder Ihre Zweigfunktion erstellen, eine Reihe von Vorlagen ändern müssen, dies jedoch vermeiden wollten. Aus diesem Grund haben wir uns nach einigem Nachfragen entschlossen, das Standard-Zweigfilterdatum neu zu definieren

...
new TwigFilter('date', [$this, 'date'], ['needs_environment' => true]),
...

function date(Twig_Environment $env, $date, $format = null, $timezone = null)
{
    $appendix = '';
    if (format && strpos($format, 'H:i') !== false)
        $appendix = ' ('.DateTimeFunctions::getRussianAbbrev($this->localizationService->getTimezone()).')';   
...
    //    date   $result
...
   return $result.$appendix;
}

Da wir den Standardfilter verwendet haben, wurde auch die alte Version neu definiert:

...
new TwigFilter('native_date', [$this, 'nativeDate'], [ 'needs_environment' => true]),
...
public function nativeDate(Twig_Environment $env, $date, $format = null, $timezone = null)
{
    //    date 
}

Der Standard-Datumsfiltercode befindet sich in / twig / twig / lib / Twig / Extension / Core :: twig_date_format_filter. Obwohl in den meisten Fällen eine einfache, nicht viel andere Option ausreicht:

$date->setTimeZone($timezone)
$result = $date->format($format);

Natürlich können Sie auch den wesentlicheren Teil von Twig gabeln oder neu definieren, aber wenn die Funktionalität des Standardfilters zu Ihnen passt, können Sie ihn einfach separat herausnehmen und nichts verlieren.

Es bleibt das Problem der Eingabe der Datums- und Uhrzeitzeit zu lösen. Eine Lösung:

private function getOffsetHours()
{
    if (!$this->isInit)
        $this->init();

    $local = new \DateTime('now', new \DateTimeZone($this->getTimezone()));
    $user = new \DateTime('now');

    $localOffset = $local->getOffset() / 3600;
    $globalOffset = $user->getOffset() / 3600;

    $diff = $globalOffset - $localOffset;
    return $diff;
}

public function toGlobalTime(\DateTimeInterface $dateTime): \DateTimeInterface 
{
    if (!$this->isInit)
        $this->init();

    $offsetHours = $this->getOffsetHours();

    if ($offsetHours > 0) {
        return $dateTime->modify('+ '.$offsetHours.' hours');
    } else  if($offsetHours < 0){
        return $dateTime->modify($offsetHours.' hours');
    }

    return $dateTime;
}

Rufen Sie dann an, bevor Sie Datum und Uhrzeit speichern, z. B. in Listenern. Diese Option kam zu uns, da im Grunde die Uhrzeit und das Datum im Projekt für bestimmte Ereignisse festgelegt und nicht manuell eingegeben wurden. In einem anderen Extremfall, in dem die Zeit ständig durch Formulare eingeführt wird, ist die Lösung möglicherweise nicht optimal.

Als Bonus. Das Projekt verwendet Omines Datatables-Bundle zur Ausgabe von Tabellen. Dort war die Lösung noch einfacher. Anstelle von DateTimeColumn zur Lokalisierung wurde Folgendes verwendet:

class CustomDateTimeColumn extends DateTimeColumn
{
    
    private $localizationService;
    private $timeZone;
    
    public function __construct(LocalizationService $localizationService)
    {
        $this->localizationService = $localizationService;
        $this->timeZone = $localizationService->getTimezoneObject();
    }
    
   
    public function normalize($value)
    {
        $value->setTimeZone($this->timeZone);
        return parent::normalize($value);
    }
}

Vielen Dank für Ihre Zeit. Wenn jemand hilft, die grundlegenden Dinge der Lösung zu verbessern, bin ich sehr dankbar. Wir sprechen von grundlegenden, da es klar ist, dass der Code Vakuum ist und in Wirklichkeit einen viel größeren DI und alle Arten von Extras für den internen Gebrauch im Projekt hat.

Zusammenfassend. Die Idee einer einfachen Lösung für eine schnelle temporäre Lokalisierung des Projekts wird vorgestellt. Es hängt nicht von den Versionen ab, oder wenn ja, ist es schwach. Diese Lösung wurde erfolgreich von Symfony 4.2 auf 5 migriert.

All Articles