【CakePHP3】CounterCacheを使ってhasManyで紐づくレコードの件数をキャッシュする
満員電車の中でダウンジャケットを着ている人と密着すると、布団を思い出して反射的に眠くなるfukasawaです。こんにちは。 着ている本人は眠くならないのでしょうか。強靭な精神力で耐えているのでしょうか。不思議です。
さて、今回はCakePHP3のCounterCacheについてです。CounterCacheはCakePHP2にも存在する機能で、hasManyで紐づくレコードの件数を自動的にDBに保存してくれる機能です。CakePHP3ではCakePHP2と使い方が異なるようなのでCakePHP3での使い方を見てみたいと思います。
※CakePHP3.2.5で検証しています。
CounterCacheキャッシュとは
まず最初にCounterCacheの動きについてザックリと見てみます。公式ページのブログチュートリアルで使用している テーブル定義を見ると、users、bookmarksというテーブルを使用しているようです。この2つのテーブルを例に見てみます。
UserモデルとBookmarkモデルは1対多の関係になっています。機能を実装する中で画面上に「UserがいくつBookmarkを持っているか表示したい!」となった場合、データ取得時に件数をカウントすることになると思いますが、CounterCacheを使用するとUserモデルに紐づくBookmarkの件数を、usersテーブルの中にキャッシュしておくことができます。
※usersテーブルに[モデル名の単数形]_countという名前でカラムを追加します。今回の例ではbookmark_countというカラムを作っておくことで、紐づくBookmarkの件数が保存されるようになります。
実際にbookmark_countの値が更新されるのは、CakePHPの処理の中でsave()やdelete()が行われたとき(afterSave()、afterDelete()のタイミング)です。updateAll()、deleteAll()のときは更新されなかったり、DBをSQLで直接操作したときには更新がかからなかったりするので、厳密に件数を取得したい場合は使いづらい雰囲気ですが、件数をちょっと表示しておきたいという場合には便利です。
使い方
CakePHP2では、belongsToアソシエーションを設定するときに"counterCache"キーにtrueを指定することでCounterCacheを有効にすることができました。
- counterCache - count() 結果をキャッシュする
CakePHP3では、CounterCacheBehaviorというビヘイビアとして実装されています。CounterCacheを使用する場合は"多"の方のテーブルクラスのinitialize()で、下記のようにビヘイビアを追加しておきます。
class BookmarksTable extends Table
{
public function initialize(array $config)
{
$this->addBehavior('CounterCache', [
'Users' => ['bookmark_count']
]);
}
}
「Userモデルに紐づくBookmarkの件数を、usersテーブルのbookmark_countカラムに保存する」という指定になります。実際にsave()やdelete()を実行すると、bookmark_countの値が更新されていることを確認できると思います。
いろいろな使い方
カウントする条件を指定する
例えば、削除されていない(delete_flag = false)レコードのみをカウントする場合は以下のようになります。 ※ブログチュートリアルのbookmarksテーブルにはdelete_flagカラムが無いので、カラムを追加したという想定です。
$this->addBehavior('CounterCache', [
'Users' => [
'bookmark_count' => [
'conditions' => ['Bookmarks.delete_flag' => false]
]
]
]);
複数のCounterCacheを持つ
削除されていないレコードと、削除されているレコード両方の件数をCounterCacheとして持っておきたい場合は以下のように指定します。(usersテーブルにdeleted_bookmark_countというカラムを追加した想定。)
$this->addBehavior('CounterCache', [
'Users' => [
'bookmark_count' => [
'conditions' => ['Bookmarks.delete_flag' => false]
],
'deleted_bookmark_count' => [
'conditions' => ['Bookmarks.delete_flag' => true]
]
]
]);
- bookmark_count:削除されていないレコードの件数が格納される
- deleted_bookmark_count:削除されているレコードの件数が格納される
カスタム Finder メソッドを使って条件を指定する
カスタム Finder メソッドを使用して条件を指定することもできます。findActive()という削除されていないレコードを取得するためのfinderメソッドを用意し、bookmark_countの条件として指定してみます。
public function initialize(array $config)
{
$this->addBehavior('CounterCache', [
'Users' => [
'bookmark_count' => [
'finder' => 'active'
]
]
]);
}
public function findActive(Query $query, array $options)
{
return $query->where(['delete_flag' => false]);
}
bookmark_countには削除されていないBookmarkの件数が格納されるはずです。
CakePHP3にupdateCounterCache()が無い…?
CounterCacheが更新されるのはsave()やdelete()が行われたタイミングですが、CakePHP2ではupdateCounterCache()というメソッドを実行することで任意のタイミングでカウンターキャッシュを更新することができました。ちょっと探してみたのですがCakePHP3の中にこのメソッドが見当たりませんでした。他に更新する方法が用意されていたりするのでしょうか。
まとめ
CakePHP2とCakePHP3ではいろいろと違いますね。