CakePHP3を触ってみました 〜modifiedを更新したくない時〜

この記事はだいぶ前に書かれたものなので情報が古いかもしれません
新しい懐中時計、欲しいなぁ……

この記事を三行にまとめると

とある処理の時だけmodifiedを更新してほしくない
エンティティのdirty()という関数を使います
あまり良いイメージのなさそうな意味の単語なのだが……
CakePHP2の時はcreatedやmodified、updatedというカラムを作っておくと、データの新規登録時や更新時に自動的に入力時間をセットしてくれました。

CakePHP3ではデフォルトではその処理をやってくれないので、TimestampというBehaviorを使う必要があります。

$this->addBehavior('Timestamp', [
  'events' => [
    'Model.beforeSave' => [
      'created' => 'new',
      'modified' => 'always'
    ]
  ]
]);

この辺の詳細な使い方は以前に書いたことがあるので良かったらどーぞ。

CakePHP3を触ってみました 〜あれ、updatedは?〜



この設定をやっておくと自動的に時間を更新してくれるわけですが、ではここで、更新時間を更新してほしくない時が出てきたとする。わけあって、とある処理の時だけmodifiedを更新してほしくない。

例えばこんな風にmodifiedにfalseやnullをセットしても上手くいきません。unsetしてもダメ。

$save = $this->Posts->pathEntity($this->request->data, $post);

//時間が更新されちゃう
$save->modified = null;
unset($save->modified);

//エラーになる
$save->modified = false;

じゃあどうするかってーと、エンティティのdirty()という関数を使います。

$save = $this->Posts->pathEntity($this->request->data, $post);
$save->dirty('modified', true);
$this->Posts->save($save);

dirtyというのはデータが変更されたかどうかを判定するもの……なのかな?

例えばデータベースからデータを取得してきてそのままsaveを行うと、書き換わったデータが一つもないのでデータの更新処理が走りません。ログを見てもUPDATE文がない。この時、全てのフィールドのdirtyがfalseになっているという判定になります。

実際にエンティティの中身を見てみると分かると思います。

//titleを書き換える
$save->title = 'タイトルを変更しました';

//エンティティの一部
Cake\ORM\Entity Object
(
  [_properties:protected] => Array
  (
    [id] => 1
    [title] => タイトルを変更しました
    [created] => Cake\I18n\Time Object
    (
      [date] => 2017-07-16 00:00:00.000000
      [timezone_type] => 3
      [timezone] => Asia/Tokyo
    )
    [updated] => Cake\I18n\Time Object
    (
      [date] => 2017-07-17 00:00:00.000000
      [timezone_type] => 3
      [timezone] => Asia/Tokyo
    )
  )

  [_dirty:protected] => Array
  (
      [title] => 1
  )
)

例えばtitleというフィールドに変更を加えると、dirtyのプロパティのところでtitleに変更があったよー的なフラグが立ちます。一つも変更がないとこのdirtyの中が空になり、データが更新されないというすんぽーです。

このdirtyフラグを意図的に立ててしまうことで、Cakeさんにこちらで明示的に時間をセットしたからあんたの方で自動上書きしなくて良いよという意思を伝えることができるわけなのです。

//modifiedのdirtyフラグを立てる
$save->dirty('modified', true);

//エンティティの一部
Cake\ORM\Entity Object
(
  [_properties:protected] => Array
  (
    [id] => 1
    [title] => タイトルを変更しました
    [created] => Cake\I18n\Time Object
    (
      [date] => 2017-07-16 00:00:00.000000
      [timezone_type] => 3
      [timezone] => Asia/Tokyo
    )
    [updated] => Cake\I18n\Time Object
    (
      [date] => 2017-07-16 00:00:00.000000
      [timezone_type] => 3
      [timezone] => Asia/Tokyo
    )
  )

  [_dirty:protected] => Array
  (
      [title] => 1
      [modified] => 1
  )
)



逆に考えると一つでもdirtyフラグが立っているとデータは更新されるので、更新時間だけ更新したいって時には、自分で現在時刻を入れても良いですが、適当なフィールドのdirtyフラグを立ててしまうというやり方も考えられますね。

//UPDATE文が発行されない
$save = $this->Posts->get(1);
$this->Posts->save($save);

//UPDATE文が発行される
$save = $this->Posts->get(1);
$save->dirty('title', true);
$this->Posts->save($save);






何の変更もないのにデータを上書きするのはデータベースに不要な負荷をかけるだけですもんね。そう考えるとこのdirtyってやつは良い機能なんだと思います。F5連打みたいな操作で大量のUPDATE文が発生するのを防いだりとか、そういう効果も期待できるんでしょう。

しかし何でdirtyって言うんだろうね。dirtyって汚れたとか卑猥とか不正とか、そういうあまり良いイメージのなさそうな意味の単語なのだが……あれかな、汚らわしい意味ではなくて、真っ白なノートがいろいろ書き込まれて黒くなる的な感じでの汚れという意味合いなのかな。それかさっきのF5連打の話じゃないけど、不正にデータの書き込みなどをしようとする輩から守るための判定をしてるぞってことを表してるのかな。



その他のCakePHP3を触ってみましたの記事はこちら
まとめという名の箸休め
 もしかしたら何か関連しているかも? 
 質問や感想などお気軽にコメントしてください