AVIL13.com
Преобразование даты в Eloquent

Преобразование даты в Eloquent

И так. У меня сервер который работает на php5.6 + PostreSQL в качестве базы данных. Все это используется сайтом созданным на Laravel 4.2.

И мне понадобилось преобразовывать дату в другой формат, ибо время в БД сохраняется по Гринвичу.

Предположим что колонка с датой у нас называется Moment

Если из БД брать данные через SQL то можно использовать прямой запрос с функцией timezone

SELECT timezone('UTC', "Moment") AS "Moment" FROM table_name

Но так как мы используем Laravel, и соответственно, для работы с БД его ОРМ Eloquent, то будем использовать его модели для преобразования даты.

Для начала создадим отдельно функцию которую будем вызывать для преобразования даты, что бы везде на сайте даты выглядели одинаково.

Добавим файл с набором наших собственных функций хелперов. От корня сайта путь к нему будет выглядеть вот так: app/Acme/helpers.php.

Затем в файле composer.json пропишем его в автолоад

"autoload": {
        "classmap": [
            "app/commands",
            "app/controllers",
            "app/models",
            "app/database/migrations",
            "app/database/seeds",
            "app/tests/TestCase.php"
        ],
        "files": ["app/Acme/helpers.php"],
        "psr-4": {
            "Acme\\": "app/Acme"
        }
    }

Тут мы добавили строки files и psr-4.

Теперь с консоли выполняем composer dump-autoload либо php artisan dump-autoload для того что бы подгрузить все что указано в этом участке файла.

Теперь непосредственно к функции для преобразования даты в нашем файле helpers.php.

<?php
use Carbon\Carbon;

/**
 * Приведение к нужному формату времени
 * @param  [type] $value [description]
 * @return [type]        [description]
 */
function toDate($value){
    // если получили пустое значение, то вернем его же.
    if(empty($value)){
        return $value;
    }
    // на случай если передадим объект Carbon
    if( is_object($value)){
        return $value->timezone('Europe/Moscow')->toDateTimeString();
    }
    // Если получили строку со временем и датой
    return Carbon::createFromTimestamp(strtotime($value))
        ->timezone('Europe/Moscow')
        ->toDateTimeString();
}

Ну и для ясности изложения файл модели для связи с таблицей БД у нас будет Messages.php.

<?php

class Messages extends Eloquent {

    public $timestamps = false;
    protected $table = 'Messages';
}

Первый вариант решения, мутатор getMomentAttribute.

Первый и самый простой вариант, это использовать мутатор атрибутов класса.

Его нужно прописать в Messages.php, он выглядит примерно так.

public function getMomentAttribute($value) {
    return toDate($value);
}

В названии функции нужно указать как называется столбец который нужно преобразовать.

В случае если название столбца в вашей БД выглядит вот так create_time то его название в имени функции будет выглядеть CreateTime, а название самой функции getCreateTimeAttribute.

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

Второй вариант решения, преобразование даты.

Так же можно использовать и вариант с преобразованием даты.

Для этого, в файле Messages.php нужно указать какие поля считать датами.

public $dates = ['Moment'];

Далеё можно использовать функцию описания формата даты который получаем из БД.

protected function getDateFormat()
{
    return 'Y-m-d H:i:s';
}

Меня правда как то удивило то что в файле для преобразования даты, она возвращается в том же виде в каком и передали.

vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php

В функции fromDateTime в строке 2644 возвращается текст отформатированный из исходного вида. Не очень понял логику этого решения, если вы болеё проницательны, то поделитесь.

У меня БД возвращала строку с датой такого вида 2015-02-03 07:52:43.812659 поэтому нормально вышеуказанная функция не работала.

Так что был сделан такой хак.

В файле Messages.php добавлена функция:

protected function asDateTime($value){
    // преобразовываем значение в  DateTime
    $value = strtotime($value);
    // получаем объект Cabon для последующего преобразования
    $obj =  parent::asDateTime($value);
    // преобразовываем
    return toDate($obj);
}

Из нюансов, хотелось бы отметить то что в функции asDateTime не обязательно создавать объект, так же как и в функции хелпере. Просто мне так удобнеё потому что все преобразования можно легко абстрагировать от основной работы.