Phalcon で Timestampable ビヘイビアを使う

# `created_at` や `updated_at` を追加したい

`craeted_at` や `updated_at` など，そのレコードが作成された時や更新された時の時間を記録しておきたい場合について．

保存した後や保存する前など特定のタイミングで処理を行いたいようなものはビヘイビアで実装できる． MySQL ではデフォルト値を設定できないためアプリケーションで SQL 発行時に now() などを使うようにする必要がある．

## `Timestampable` を使う

例えば下記のようなスキーマを使う場合について．

```sql
CREATE TABLE `post` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `message` varchar(1000) NOT NULL DEFAULT '',
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
```

`created_at` が新しく追加されたタイミング，`updated_at` が更新されたタイミングで 時間を更新したい場合は下記のように書く．
`field` が対象としたいカラムの名前（ `columnMap()` で名前を変更している場合は連想配列の値の方），`format` は php の `format_date()` で指定できる形式の文字列を指定する．
`field` は対象が一つの場合は単なる文字列でいいが，複数の場合は配列で渡すとよい．

```php
class Post extends Phalcon\Mvc\Model
{
    public function initialize()
    {
        $this->addBehavior(new Phalcon\Mvc\Model\Behavior\Timestampable(array(
            'beforeCreate' => array(
                'field' => array(
                    'created_at',
                    'updated_at'
                ),
                'format' => 'Y-m-d H:i:s'
            ),
            'beforeUpdate' => array(
                'field'  => 'updated_at',
                'format' => 'Y-m-d H:i:s'
            ),
        )));
    }
}
```

## `NOT NULL` が付いている場合

例えば下記のように対象となるカラムに `NOT NULL` が付いている場合．

```sql
CREATE TABLE `post` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `message` varchar(1000) NOT NULL DEFAULT '',
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8mb4;
```

Phalcon の Model ではカラムに `NOT NULL` が付いている場合，自動的に `Phalcon\Mvc\Model\Validator\PresenceOf` バリデーションが動く．
そのため前述のコードでは  `created_at is required` や `updated_at is required` のようなエラーメッセージが発生する．
これを防ぐためには `PresenceOf` バリデーションが発生しないようにするか，バリデーション前にビヘイビアの処理が動くようにする必要がある．

### `PresenceOf` の挙動を止める

どこかしらで下記の記述を追加する．ただしこれを追加すると他のカラムの `NOT NULL` の場合のバリデーションもなくなるので自身で `PresenceOf` バリデーションを追加する必要があるようになるはず．

```php
\Phalcon\Mvc\Model::setup(array(    
    'notNullValidations' => false
));
```


### バリデーション前にビヘイビアの処理を動かす

全体に影響させないようにしたい場合は，下記のようにバリデーションが動く前に `Timestampable` ビヘイビアの処理が動くようにしたものをつかう．
この[リスト](http://docs.phalconphp.com/en/latest/reference/models.html#events-and-events-manager)の中に `beforeValidation` が付いたものがあるのでこれを指定する．

```php
class Post extends Phalcon\Mvc\Model
{
    public function initialize()
    {
        $this->addBehavior(new Phalcon\Mvc\Model\Behavior\Timestampable(array(
            'beforeValidationOnCreate' => array(
                'field' => array(
                    'created_at',
                    'updated_at'
                ),
                'format' => 'Y-m-d H:i:s'
            ),
            'beforeValidationOnUpdate' => array(
                'field'  => 'updated_at',
                'format' => 'Y-m-d H:i:s'
            ),
        )));
    }
}
```

# 参考文献

ビヘイビアについて

> モデルの働き — Phalcon 1.3.1 documentation
>
> http://docs.phalconphp.com/ja/latest/reference/models.html#id27

`format` オプションで指定できる形式

> PHP: DateTime::format - Manual
>
> http://www.php.net/manual/ja/datetime.format.php

`NOT NULL` が付いた場合の挙動について

> Timestampable behavior - Discussion - Phalcon Framework
>
> http://forum.phalconphp.com/discussion/451/timestampable-behavior

モデルで発行されるイベントのリスト

> Working with Models — Phalcon 1.3.0 documentation
>
> http://docs.phalconphp.com/en/latest/reference/models.html#events-and-events-manager

`NOT NULL` の場合に `PresenceOf` で必須のバリデーションが働かないようにすることについての議論

> created_at and validation - Discussion - Phalcon Framework
>
> http://forum.phalconphp.com/discussion/283/created-at-and-validation

実際に `PresenceOf` を無効にする場合に設定する項目

> Working with Models — Phalcon 1.3.0 documentation
>
> http://docs.phalconphp.com/en/latest/reference/models.html#disabling-enabling-features