Как работать с приложением Laravel в нескольких часовых поясах

Я читал о лучшем подходе к обработке локализованного времени, когда приложение Laravel используется в нескольких часовых поясах.

Насколько я понимаю, часовой пояс приложения должен оставаться установленным по умолчанию, то есть UTC.

Это означает, что все даты и времени / метки времени записываются в базу данных (в моем случае MySQL) как их значение в формате UTC - другими словами, последовательно.

Чтобы модели Eloquent имели правильные (локализованные) значения даты и времени, необходимо соблюдать часовой пояс пользователя. Именно здесь я не совсем понимаю, как действовать дальше - в частности, с точки зрения:

  • Как лучше всего определить часовой пояс пользователя
  • Как этот часовой пояс можно использовать способом прозрачный с Eloquent, чтобы
    • Все модельные даты выводятся по местному времени.
    • Все даты правильно записаны в базе данных (как UTC)

Редактировать Я должен упомянуть, что мое приложение поддерживает как анонимных, так и аутентифицированных пользователей, поэтому я не хочу заставлять пользователя явно выбирать свой часовой пояс.

Вы можете попробовать эти ответы stackoverflow.com/questions/36147824/…

Web Artisan 13.08.2018 16:35

Что я делаю, так это то, что часовой пояс в моем проекте / сервере установлен в UTC, и когда я выводю время / дату, я устанавливаю часовой пояс в экземпляре углерода (например, created_at), и у меня есть в профиле пользователя параметр для выбора местного часового пояса.

thefallen 13.08.2018 16:41

@thefallen, как вы устанавливаете часовой пояс в экземпляре углерода - вручную? Кроме того, мое приложение поддерживает анонимных и аутентифицированных пользователей, поэтому я не могу явно запросить их часовой пояс.

BrynJ 13.08.2018 16:51

Вы можете создать глобальное промежуточное ПО и реализовать что-то вроде полезныйангл.com/post/31/…

Rwd 13.08.2018 17:47
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Поиск нового уровня в Laravel с помощью MeiliSearch и Scout
Поиск нового уровня в Laravel с помощью MeiliSearch и Scout
Laravel Scout - это популярный пакет, который предоставляет простой и удобный способ добавить полнотекстовый поиск в ваше приложение Laravel. Он...
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
В последние годы архитектура микросервисов приобрела популярность как способ построения масштабируемых и гибких приложений. Laravel , популярный PHP...
Как построить CRUD-приложение в Laravel
Как построить CRUD-приложение в Laravel
Laravel - это популярный PHP-фреймворк, который позволяет быстро и легко создавать веб-приложения. Одной из наиболее распространенных задач в...
6
4
834
2

Ответы 2

Мне нравится добавлять вспомогательную функцию только для этого. Я сохраняю приложение в формате UTC, поэтому все даты хранятся одинаково ... а затем я передаю их через помощник либо в контроллере, либо в моем шаблоне лезвия.

public static function date($date, $format = 'n/j/Y g:i a T'){
    $timezone = empty(Auth::user()->timezone) ? 'America/New_York' : Auth::user()->timezone;
    if ( empty($date) ){
        return '--';
    }
    return Carbon::parse($date)->timezone($timezone)->format($format);
}

При этом используется поле часового пояса, которое было добавлено в модель пользователя, но по умолчанию будет использоваться восточное время, если они являются гостями. (Восточная, потому что это был бизнес, и большинство клиентов находятся здесь.)

В итоге я реализовал это с помощью моей собственной модели, в первую очередь потому, что мне нужно было реализовать это прозрачным образом.

Во-первых, я создал свой собственный метод getAttribute(), чтобы получить сохраненные значения (сохраненные как часовой пояс приложения по умолчанию - вероятно, UTC), а затем применить текущий часовой пояс.

Эта черта также изменяет методы модели create() и update(), чтобы поддерживать поля в свойстве модели dates, сохраняемые в качестве часового пояса приложения, когда они были установлены пользователем в текущем активном часовом поясе.

Статический метод self::getLocale() в трейте в моем случае предоставляется другим трейтом в моем приложении, хотя эту логику можно настроить в соответствии с вашим собственным приложением.

trait LocalTime
{
    /**
     * Override create() to save user supplied dates as app timezone
     * 
     * @param array      $attributes
     * @param bool|mixed $allow_empty_translations
     */
    public static function create(array $attributes = [], $allow_empty_translations=false)
    {
        // get empty model so we can access properties (like table name and fillable fields) that really should be static!
        // https://github.com/laravel/framework/issues/1436
        $emptyModel = new static;

        // ensure dates are stored with the app's timezone
        foreach ($attributes as $attribute_name => $attribute_value) {
            // do we have date value, that isn't Carbon instance? (assumption with Carbon is timezone value will be correct)
            if (!empty($attribute_value) && !$attribute_value instanceof Carbon && in_array($attribute_name, $emptyModel->dates)) {
                // update attribute to Carbon instance, created with current timezone and converted to app timezone
                $attributes[$attribute_name] = Carbon::parse($attribute_value, self::getLocale()->timezone)->setTimezone(config('app.timezone'));
            }
        }

        // https://github.com/laravel/framework/issues/17876#issuecomment-279026028
        $model = static::query()->create($attributes);

        return $model;
    }

    /**
     * Override update(), to save user supplied dates as app timezone
     *
     * @param array $attributes
     * @param array $options
     */
    public function update(array $attributes = [], array $options = [])
    {
        // ensure dates are stored with the app's timezone
        foreach ($attributes as $attribute_name => $attribute_value) {
            // do we have date value, that isn't Carbon instance? (assumption with Carbon is timezone value will be correct)
            if (!empty($attribute_value) && !$attribute_value instanceof Carbon && in_array($attribute_name, $this->dates)) {
                // update attribute to Carbon instance, created with current timezone and converted to app timezone
                $attributes[$attribute_name] = Carbon::parse($attribute_value, self::getLocale()->timezone)->setTimezone(config('app.timezone'));
            }
        }

        // update model
        return parent::update($attributes, $options);
    }

    /**
     * Override getAttribute() to get times in local time
     *
     * @param mixed $key
     */
    public function getAttribute($key)
    {
        $attribute = parent::getAttribute($key);

        // we apply current timezone to any timestamp / datetime columns (these are Carbon objects)
        if ($attribute instanceof Carbon) {
            $attribute->tz(self::getLocale()->timezone);
        }

        return $attribute;
    }
}

Мне было бы интересно узнать отзывы о вышеупомянутом подходе.

Это выглядит нормально, но требует, чтобы вы добавили черту ко всем моделям. Я думаю, что это могло бы быть альтернативным решением - прослушивать общее событие eloquent.saving и перебирать свойство $dates затронутой модели и работать над этими атрибутами. Конечно, это требует, чтобы у вас было правильно используемое свойство $dates. Примечание. Если вы используете пользовательские события в своей модели (массив $dispatchesEvents) и одно из них возвращает результат, общее событие не будет запущено.

Namoshek 05.09.2018 14:11

Другие вопросы по теме