Русское сообщество разработки на PHP-фреймворке Laravel.
Ты не вошёл. Вход тут.
Страницы 1
Всем привет.
Народ нужна небольшая помощь, пытался разобраться сам но не получилось((
Задача следующая:
Для служебных целей пишу мини CRM. В CRM есть таблица со списком филиалов и в каждом филиале есть настройка timezone вида Europe/Moscow
Пытаюсь сделать так чтобы когда другой пользователь работая в каком-то филиале (добавлял посты, записи и другую информацию) добавлял время в поля created_at и updated_at или другие нужные поля с учетом временной зоны
Посоветовали для каждой модели с которой работает филиал добавить мутаторы... Хоть документацию прочитал но не особо понял как эту информацию применить((
Не в сети
- Посоветовали для каждой модели с которой работает филиал добавить мутаторы…
Это отличный способ выстрелить себе в ногу. Моё мнение: что с экранированием HTML, что с форматированием дат и чисел и других вещей, которые могут иметь разное представление в зависимости от разных настроек (пользователя) или контекста (plain text, веб-страница) — всё это надо делать только и исключительно при выводе, а в базе хранить сырые значения, т.е. plain text, а не экранированный код, числа без форматирования (1234567.89, а не 1,234,567.89), дату в UTC (либо как unsigned int, либо как datetime, последнее удобней). Если делать иначе, то будет постоянная проблема: «а я уже сконвертировал это или нет?» и «в каком формате вот это значение?». Мутаторы неявны, про них постоянно забываешь и получается, что где-то дата не сконвертирована, а где-то сконвертирована дважды. Для вывода можно написать пару глобальных функций и использовать в шаблонах.
Есть другие мнения на этот счёт, но это лично моё, из богатого опыта.
Laravel использует Carbon для дат, с ним легко можно конвертировать их в разные временные зоны и форматы для вывода. Тут пример с изменением TZ.
Не в сети
даты в timestamp в базе хранятся(2016-06-23 23:11:11)
Карбон читал, но блин ума не хватает доформатировать вывод под нужную timezone...
пример $message->created_at выводит объект Carbon
object(Carbon\Carbon)#236 (3) {
["date"]=>
string(26) "2016-06-23 19:11:59.000000"
["timezone_type"]=>
int(3)
["timezone"]=>
string(17) "America/Vancouver"
}
Хотел $message->created_at->timezone('тут зона') но не сработало
Хотя сработало, запутался уже))
Изменено deller (23.06.2016 23:28:53)
Не в сети
Кjроче в app.php поставил 'timezone' => 'Europe/Moscow', и зону меняю при выводе как выше написал $message->created_at->timezone('тут зона')
Вроде все сходится...
Не в сети
Не в сети
}%>Поставь 'Etc/UTC', меньше проблем будет.
А в чем разница между просто UTC и Etc/UTC?
Вроде разобрался...
В базу записывается время сервера(московское)
UTC настройка как я понял всегда отображает +0 от того что в базе записано т.е. записано 9,30(московское +3) покажет 6,30(+0)
Получается разделы отличные от филиалов будут отображать время в UTC зоне, не лучше ли будет поставить по умолчанию Europe/Moscow в конфиг, чтобы по московскому времени общий контент показывать?
Не в сети
- А в чем разница между просто UTC и Etc/UTC?
Если не ошибаюсь, Etc/UTC стандартный, а просто UTC специфичный для PHP. Но да, подходит любой из этих.
- Получается разделы отличные от филиалов будут отображать время в UTC зоне, не лучше ли будет поставить по умолчанию Europe/Moscow в конфиг, чтобы по московскому времени общий контент показывать?
Не надо лениться конвертировать значения при любом выводе. Лучше сделай новую настройку — зону для вывода, которая будет использоваться всегда, если нет причины использовать какую-то другую, и выстави там Москву, а зону для приложения (Laravel) используй UTC.
Почему? Представь, что у зоны, которая у тебя стоит по умолчанию, сменилось DST (летнее время). Несколько лет назад в России то в один год объявляли, что летнего времени больше не будет, то возвращали обратно. Допустим, снова меняется DST и теперь оно есть. Теперь представь — приложение работает, база наполнена, всё хорошо. Потом ты обновляешь PHP, которая идёт уже с новой базой зон, где DST включено. И магическим образом всё время в базе сбивается. Если заметил не сразу (а такие вещи ничего кардинально не ломают и замечаются ой как не сразу), то в базе будет полный бардак: некоторые значения записаны в станом формате, т.е. время отстаёт на 1 час, а некоторые в новом. Механически исправить это сложно и есть большой риск путаницы, т.к. некоторые даже старые значения код мог обновить и ещё сильнее сбить.
Последовательность должна быть такая:
Таким образом, всё что входит и выходит за периметр имеет свою зону, а всё что внутри — строго UTC.
Не в сети
По-порядку:
В базе храним даты сервера. Это наша точка отсчета. Естественно их не трогаем. Т.е все события происходящие на сайте пишутся по времени сервера. Временную зону ставите какую вам удобно.
Если нужно отобразить поправку на локальное время пользователя, делаем мутатор. Не люблю это слово, т.к не понятно что конкретно оно делает, предпочитаю более привычные геттер (мутатор который изменяет данные при выводе) и сеттер (мутатор, который изменяет данные при вводе). В вашем случае делаем геттер для полей created_at и updated_at, но, т.к оригинальные created_at и updated_at могут пригодиться, сделаем это через псевдополя, пусть будут created_utc и updated_utc.
Сначала добавим в модель наши псевдополя
protected $appends = [
'created_utc',
'updated_utc',
];
public function getCreatedUtcAttribute()
{
$timeZone = \\здесь получаем timezone пользователя.
return \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $this->getOriginal('created_at'))->timezone($timeZone);
}
public function getUpdatedUtcAttribute()
{
$timeZone = \\здесь получаем timezone пользователя.
return \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $this->getOriginal('updated_at'))->timezone($timeZone);
}
Все, теперь ваша модель имеет поля $model->created_utc и $model->updated_utc.
Теперь вот про это:
Вам не кажется что
- в базе хранить сырые значения
- принимаем дату/время от пользователя — тут же конвертируем и его зоны в UTC
- Мутаторы неявны, про них постоянно забываешь и получается, что где-то дата не сконвертирована, а где-то сконвертирована дважды. Для вывода можно написать пару глобальных функций и использовать в шаблонах
Вы просто их неправильно готовите
- Для вывода можно написать пару глобальных функций и использовать в шаблонах
Поздравляю, это называется геттер, и место ему в модели.
Изменено Cheshirrski (24.06.2016 12:29:28)
Не в сети
- противоречат друг другу?)
Каким образом? Ввод от пользователя нормализуется (переводится из его зоны в UTC) и сохраняется в базу. В базе хранятся сырые, т.е. нормализованные значения.
- Вы просто их неправильно готовите
У вас то же решение, которое описывал я. И да, тоже не мутаторы (в моём понимании это методы с тем же именем, что и само поле, т.е. прозрачно подменяющие его значение для вызывающего кода).
- Для вывода можно написать пару глобальных функций и использовать в шаблонах
- Поздравляю, это называется геттер, и место ему в модели.
Не в сети
ооо Спасибо Вам большое за разъяснения!! И спасибо Cheshirrski за пример кода, буду пробовать.
Не в сети
Все применил, работает!
С начало думал что 'Y-m-d H:i:s' это выводимый формат, потом дошло что это формат получаемый из базы, отформатировал добавив ->format('')
Еще 1 вопросик.
Можно ли создать мутатор(геттер) глобальным для определенных таблиц?
Конечно можно прописать все в каждой модели, но лично мне не нравится дублировать один и тот же код...
Не в сети
Хотя вот не состыковка какая-то...
время в базе записано как 17:25:10 это время сервера, оно же и Московское
прогоняю через геттер, timezone получает значение временной зоны филиала Europe/Moscow и прибавляет к времени в базе +3 часа
получается 20:25:10 что не правильно((
Не в сети
Скорее всего вы забыли поставить в config/app.php ’timezone’ ⇒ ’Europe/Moscow’, еще подозрение на ->format('') — по идее это вообще не должно работать. В каком формате вы храните время? Почитайте документацию на Carbon и выбирайте подходящий метод, ссылку вам выше давали.
- Можно ли создать мутатор(геттер) глобальным для определенных таблиц?
Можно, в Laravel можно все что можно в PHP. Но я бы не советовал. Смысл в том, чтобы сделать из модели единственную точку работы с данными. Чтобы не бегать по представлениям, хелперам, контроллерам, в поисках «откуда это взялось», и запись $model->created_utc означала ровно то, что написано.
Не в сети
- Хотя вот не состыковка какая-то…
- время в базе записано как 17:25:10 это время сервера, оно же и Московское
- прогоняю через геттер, timezone получает значение временной зоны филиала Europe/Moscow и прибавляет к времени в базе +3 часа
- получается 20:25:10 что не правильно((
Именно такие нестыковки у вас будут кругом и рядом, если в базе будет не UTC.
Не в сети
Да, поставил в app Europe/Moscow и все стало на свои места, вроде время в разных зонах везде совпадает.
Мне представляется следующий механизм такого результата:
1. В базу записывается время по серверному времени(на сервере оно равно Europe/Moscow)
2. Настройка в app говорит Carbon в какой зоне выводить время
3. Если стоит UTC то Carbon считает что в базе время записано как UTC и ничего не меняет, но оно же там серверное(Московское)
4. И получается когда делаем return \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $this->getOriginal('created_at'))->timezone($timeZone); зона применяется к серверному(Московскому), дважды Московское получается если можно так выразится))
5. Если ставим в app Europe/Moscow (как на сервере) говорим Carbon что у нас сервер по Московскому времени работает и отсюда уже пляшем, конвертим в другие зоны как надо.
Не в сети
в базу только в UTC, почему Вы противитесь этому ?
Да я не противлюсь)) оно само туда как Московское записывается)
Не в сети
Вы (народ) шаблонно думаете. в этом ваша проблема.
deller, работай на сервере от app::Europe/Moscow как и настроил, а в профиль клиента добавь зону и работай от неё.
если зоны не указано или это гость - подключай GeoIP на стороне операционки и/или пакажом от лары, - им и определяй какое время показать.
Карбонатом только выводи результаты во view, считать правильное время надо только во время отображения, ибо это вычисляемые данные, которые формируют информацию.
Изменено hzone (26.06.2016 14:59:50)
Не в сети
Столкнулся с похожей проблеммой... мозг сломал...
Вот наткнулся на просторах инета: Easy timezones in Laravel with Carbon https://andrew.cool/blog/49/Easy-timezo … ith-Carbon ,
Мне кажется толковое решение, попробую...
Не в сети
Столкнулся с похожей проблеммой... мозг сломал...
Вот наткнулся на просторах инета: Easy timezones in Laravel with Carbon https://andrew.cool/blog/49/Easy-timezo … ith-Carbon ,
Мне кажется толковое решение, попробую...
Вообщем я пошел по пути hzone(Спасибо!), на сервере от app::UTC...
Не в сети
php artisan config:cache
Не в сети
Не прямо по теме, но связано.
Если кто-то не в курсе, тип mysql datetime хранит буквальное время, как написали. В отличие от timestamp который как-бы в абсолютном значение по Гринвичу, а его текстовое представление транслируется туда и обратно согласно сессионной таймзоны. Это не очень важно пока не поменялась зона на сервере. А поменяться она может запросто если у вас production, staging и local не одно и то же и вы периодически копируете базу Или можно словить нежданчик при переезде.
У нас временные зоны для php и для mysql принудительно выставляются одинаковыми и могут быть переделаны через параметр в .env, как принято в Laravel
config/app.php
'timezone' => env('TIMEZONE', 'our default timezone'),
config/database.php
'connections' => [
'mysql' => [
...
'timezone' => env('TIMEZONE', 'our default timezone'),
Изменено artoodetoo (10.05.2018 15:35:25)
There are two hard things in computer science: cache invalidation, naming things, and off-by-one errors.
Не в сети
Страницы 1