手動によるカウンターキャッシュの更新

counterCacheは便利だが、どうやら1モデルにつき1つしか登録できないようだ。

たとえばUserモデルとFriendモデルがあるとする。
Userモデルにはfriend_countフィールドがあり、Friendの数がカウンターキャッシュで更新できるようにする場合、Friendモデルには下記のような記述をする

    var $belongsTo = array(
        'User' => array(
            'className'    => 'User',
            'foreignKey'   => 'user_id',
            'conditions'   => '',
            'fields'       => '',
            'order'        => '',
            'counterCache' => 'friend_count',
            'counterScope' => array('Friend.status' => 1),
        ),
    );

単純に友達の数をカウントするだけならこれでいいが、さらに「特に親しい友達の数もカウントしたい」というような要望がある場合は別処理でカウントする必要がある(と思う)。

試しに次のように配列で入れてみたが、結果は上手くうごかなかった。
(Friendモデルにis_favoriteというフィールドを設け、このis_favoriteが1の要素の合計数を、Userモデルのfriend_favorite_countというフィールドに入れたいとする)

    var $belongsTo = array(
        'User' => array(
            array(
                'className'    => 'User',
                'foreignKey'   => 'user_id',
                'conditions'   => '',
                'fields'       => '',
                'order'        => '',
                'counterCache' => 'friend_count',
                'counterScope' => array('Friend.status' => 1),
            ),
            array(
                'className'    => 'User',
                'foreignKey'   => 'user_id',
                'conditions'   => '',
                'fields'       => '',
                'order'        => '',
                'counterCache' => 'friend_favorite_count',
                'counterScope' => array('Friend.status' => 1, 'Friend.is_favorite' => 1),
            ),
        ),
    );

で、結局friend_favorite_countの方は、別に処理を書いて更新させることにする。

Friend.php

    var $actsAs = array('CounterCache');
    var $_del_user_id;

    function afterSave ($created) {
        // 新規登録時
        if ($created) {
            $this->id = $this->getLastInsertId();
        }
        $user_id = $this->Field('user_id');
        $this->_updateFavoriteCount($user_id);
    }

    function beforeDelete () {
        $this->_del_user_id = $this->Field('user_id');
        return true;
    }

    function afterDelete () {
        if ($this->_del_user_id) {
            $this->_updateFavoriteCount($this->_del_user_id);
            return true;
        }
    }

    function _updateFavoriteCount($user_id = null) {
        if (is_null($user_id)) return false;
        $count = $this->find('count', array('conditions' => array('Friend.user_id' => $user_id, 'Friend.favorite' => 1, 'Friend.status' => 1)));
        App::import('Model', 'User');
        $User= new User();
        $User->id = $user_id;
        if ($User->saveField('friend_favorite_count', $count)) {
            return true;
        }
        return false;
    }

Leave a Response