Создание меток времени TimestampBehavior в yii2
  • 1276

TimestampBehavior в yii2 - сохранение меток времени.

Автор: admin | 02 мая (Ср.) 2018г. в 22ч.02м.

В yii2 имеется несколько классов поведений что называется из коробки.
Рассмотрим, как использовать на практике TimestampBehavior.
yii\behaviors\TimestampBehavior автоматически заполняет указанные атрибуты текущей меткой времени.
Такое поведение полезно, когда нужно создать новую запись или обновить существующую в базе данных.
Если в базе есть поля даты создания и/или обновления, то туда соответственно должна быть внесена
временная метка, когда запись создана или изенена.В контроллере это выглядет так:
<?php
public function actionCreate()
{
    $model = new BlogArticles();
 
    if ($model->load(Yii::$app->request->post())) {
        $model->createdAt = gmdate("Y-m-d H:i:s");
        $model->updatedAt = gmdate("Y-m-d H:i:s");
       if ($model->save()) {             
         return $this->redirect(['view', 'id' => $model->id]);             
       }  else {
         var_dump ($model->getErrors()); die();
       }
    }
    
   //другой код
}    
?>​

Реализация поведения Timestamp сделает это автоматически для нас и может быть
легко добавлена ​​ко всем моделям ActiveRecord в веб-приложении.
Применяется TimestampBehavior в классах моделей ActiveRecord.

Базовое использование:

<?php
namespace backend\models\blog;

use Yii;

use yii\behaviors\TimestampBehavior;
/**
 * @property int $id
 * ....other prop
 * @property string $message
 * @property string $createdAt
 * @property string $updatedAt
 */
class BlogArticles extends \yii\db\ActiveRecord
{

    public function rules()
    {
        return [
            [['message'], 'required'],
            [['message'], 'string'],
            [['created_at', 'updated_at'], 'safe']
        ];
    }
    
    public function behaviors()
    {
        return [
            //Использование поведения TimestampBehavior ActiveRecord
            'timestamp' => [
                'class' => TimestampBehavior::className(),
                'attributes' => [
                    \yii\db\BaseActiveRecord::EVENT_BEFORE_INSERT => ['createdAt'],
                    \yii\db\BaseActiveRecord::EVENT_BEFORE_UPDATE => ['updatedAt'],

                ],
                //можно использовать 'value' => new \yii\db\Expression('NOW()'),
                'value' => function(){
                                return gmdate("Y-m-d H:i:s");
                },


            ],

        ];
    }
    
}

?>

В зависимости от типа поля в бд устанавливается временная метка. Для типа поля
datatime в базе данных, удобно применять функцию вида gmdate("Y-m-d H:i:s");
Данная функция позволяет сохранять дату и время datetime без привязки к часовому поясу,а
потом при выводе из бд форматировать дату в соответствии с часовым поясом юзера. Работа с часовыми
поясами это тема отдельного поста.

В ключ 'value' устанавливается нужная дата. В примере выше дата возвращается из
анонимной функции, что позволяет сделать предварительную обработку. Можно установить дату и
так, как в примере в документации к yii2:

'value' => new \yii\db\Expression('NOW()'),//тип поля бд timestamp

Если 'value' не указан или null, то значение по умолчанию передается как time() и поле в бд
должно быть с типом UNIX timestamp.
Также по умолчанию заданы имена атребутов для установки временных меток:

public $createdAtAttribute = 'created_at';
public $updatedAtAttribute = 'updated_at';

Соответственно, если поля в таблице названы также как по умолчанию
и time() устраивает в качестве времнной метки
для данных полей, то код можно сократить до такого:
public function behaviors()
{
    return [
        TimestampBehavior::className(),
    ];
}​

Теперь можно убрать из контроллера присвоения временных меток:

$model->createdAt = gmdate("Y-m-d H:i:s");
$model->updatedAt = gmdate("Y-m-d H:i:s");

Поскольку значения атрибутов будут автоматически установлены этим поведением,
они обычно не являются входными данными пользователя и поэтому не должны быть проверены на
валидность,
Поэтому created_at и updated_at можно отметить как безопастные:
[['created_at', 'updated_at'], 'safe']​

Если в таблице есть только поле для даты например создания, то нужно указать в поведении,
что обновление не предусмотрено:

public function behaviors()
    {
        return [
            //Использование поведения TimestampBehavior ActiveRecord
            'timestamp' => [
                'class' => TimestampBehavior::className(),
                'attributes' => [
                    \yii\db\BaseActiveRecord::EVENT_BEFORE_INSERT => ['createdAt'],
                    \yii\db\BaseActiveRecord::EVENT_BEFORE_UPDATE => false,

                ],
                'value' => new \yii\db\Expression('NOW()'),
            ],

        ];
    }

В данном примере 'value' => new \yii\db\Expression('NOW()') устанавливает текущую дату
по средствам функции базы данных 'NOW()'. То есть \yii\db\Expression применяется, если
обработка даты производится в sql бд. Если нужно с помощью php сделать расчет, то как ранее
я отметил - нужно применять анонимную функцию.
Вот еще пример с \yii\db\Expression:

  'value' => new \yii\db\Expression('CURDATE()'),//текущая дата в виде чисел года,месяца,дня
    'value' => new \yii\db\Expression('DATE_ADD(NOW(), INTERVAL 31 DAY)'),//текущая дата + 31 день

Обычно в базу данных устанавливают время создания и время обновления.Но может
быть еще поле с той же меткой времени. Таким образом нужно добавить имя поля в
массив. В примере это lastVisit

'attributes' => [
        \yii\db\BaseActiveRecord::EVENT_BEFORE_INSERT => ['createdAt', 'lastVisit'],
        \yii\db\BaseActiveRecord::EVENT_BEFORE_UPDATE => false,

    ],

Однако может быть, когда понадобится поле с другим видом даты.
Например, какая-нибудь дата дня рождения
автора или дата актуальности записи (пусть через месяц от времени создания).
Таким образом нам нужно одно поле с датой, отличной от текущей, как .
Тогда просто добавляем еще одно поле в событии.

    //было это поведение
    'timestamp' => [
                'class' => TimestampBehavior::className(),
                'attributes' => [
                    \yii\db\BaseActiveRecord::EVENT_BEFORE_INSERT => ['createdAt'],
                    \yii\db\BaseActiveRecord::EVENT_BEFORE_UPDATE => ['updatedAt'],

                ],
                'value' => new \yii\db\Expression('NOW()'),
            ],

        ],
        
    //добавили отдельное поведение для установки отдельной даты
    'timestamp' => [
                'class' => TimestampBehavior::className(),
                'attributes' => [
                    
                    \yii\db\BaseActiveRecord::EVENT_BEFORE_UPDATE => false,
                    \yii\db\BaseActiveRecord::EVENT_BEFORE_INSERT => ['activeDate'],
                ],
                'value' =>  new \yii\db\Expression('DATE_ADD(NOW(), INTERVAL 31 DAY)'),


            ], 

Теперь при создании будет еще заполняться поле activeDate с датой + 31 день от текущей.

Ну и на конец у TimestampBehavior есть метод touch(). Этот метод
обновляет атрибут timestamp для текущей метки времени.

Например, в контроллере так:

public function actionPage($alias){
    $page = PageModel::find()->where(['alias' => $alias])->one();
    
    if($page){
        $model->touch('lastVisit');
    }

    //...
}

Тут при попадании на страницу обновляется поле с датой timestamp 'lastVisit', в которое
попадает текущее время по средствам функции time()

Данный метод работает только при обновлении записи. При создании записи вызов этого метода
генерирует исключение:

Вот фрагмент из реализации поведения, отвечающий за эту работу:

/**
* {@inheritdoc}
*
* In case, when the [[value]] is `null`, the result of the PHP function [time()](http://php.net/manual/en/function.time.php)
* will be used as value.
*/
protected function getValue($event)
{
   if ($this->value === null) {
       return time();
   }

   return parent::getValue($event);
}

/**
* Updates a timestamp attribute to the current timestamp.
*
* ```php
* $model->touch('lastVisit');
* ```
* @param string $attribute the name of the attribute to update.
* @throws InvalidCallException if owner is a new record (since version 2.0.6).
*/
public function touch($attribute)
{
   /* @var $owner BaseActiveRecord */
   $owner = $this->owner;
   if ($owner->getIsNewRecord()) {
       throw new InvalidCallException('Updating the timestamp is not possible on a new record.');
   }
   $owner->updateAttributes(array_fill_keys((array) $attribute, $this->getValue(null)));
}

На этом все. Всем добра!

Приветствую!

Меня зовут Сергей. Я - автор этого блога.

Если Вам был полезен материал на моем сайте, поддержите пожалуйста мой проект, чтобы о нем узнали другие люди - кликните plizz :) на иконку в соц. сети, чтобы поделиться материалом с другими.