【CakePHP3】CounterCacheを使ってhasManyで紐づくレコードの件数をキャッシュする

【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の件数が保存されるようになります。

20160329_fukasawa_01.png

実際に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ではいろいろと違いますね。

  • このエントリーをはてなブックマークに追加

この記事を読んだ人にオススメ