Yii CActiveDataProvider查询缓存工作不正常


Yii CActiveDataProvider query caching not working correctly

我想缓存CActiveDataProvider实例的DB结果。

我在控制器的操作中准备数据提供程序,然后在CGridView中使用数据提供程序(稍后在某些视图中)。

我以前有这样的代码:

$cars=new CActiveDataProvider(
    'Car'
    ,
    array(
        'criteria'=>array(
            'condition'=>'brand_id=:brand_id',
            'params' => array(':brand_id'=>$model->id),
            'order' => 'price',
        ),
        'pagination'=>false,
    )
);

并且,按照yii wiki中的教程,我将其更改为:

$cars=new CActiveDataProvider(
        Car::model()->cache(3600)
        ,
        array(
            'criteria'=>array(
                'condition'=>'brand_id=:brand_id',
                'params' => array(':brand_id'=>$model->id),
                'order' => 'price',
            ),
            'pagination'=>false,
        )
    );

但无济于事:查询没有被缓存。

CActiveDataProvider的优点之一是不会立即执行查询。只有当您稍后使用CActiveDataProvider时,才会执行查询,例如在CGridView中,它将调用CActiveDataProvider::fetchData最终触发查询。

这很有用,因为您可以使用片段缓存,而且您不想在控制器中加载数据,却发现您不需要它,因为片段是缓存的。

这正是这里发生的情况:ActiveRecord ->cache()方法指示DB Connection缓存后续查询,但如果不立即执行该查询,则可以在该查询之前执行其他查询,并且该缓存将不起作用。

我解决了创建个性化ActiveDataProvider的问题,它将在查询发生之前设置模型缓存:

class CachedActiveDataProvider extends CActiveDataProvider
{
    public $cache_duration = null;
    public $cache_dependency = null;
    /**
     * The cache mechanism works by telling the DB Component to cache the next query.
     * When  fetchData() is called, the DB will cache the next query. 
     * There is a possibility that fetchData call calculateTotalItemCount()
     * and in that function, if this bit is active, we will tell the DB to cache
     * 2 queries, the count, and the actual fetch that will come next.
     * (if we don't do this, the DB will cache the count query, and will forget about
     * the instruction to cache the fetch query)
     * 
     * @var boolean
     */
    private $fetching_data = false;
    protected function fetchData()
    {
        if (!is_null($this->cache_duration ))
        {
            $this->model->cache($this->cache_duration, $this->cache_dependency);
        }
        $this->fetching_data = true;
        $ret = parent::fetchData();
        $this->fetching_data = false;
        return $ret;
    }
    protected function calculateTotalItemCount()
    {
        if (!is_null($this->cache_duration ))
        {
            $this->model->cache(
                $this->cache_duration, 
                $this->cache_dependency, 
                $this->fetching_data ? 2 : 1 //if fetching data, cache 2 queries: this count and the sequent fetching
            );
        }
        return parent::calculateTotalItemCount();
    }
}

我现在可以用称之为

$cars=new CachedActiveDataProvider(
    'Car'
    ,
    array(
        'criteria'=>array(
            'condition'=>'brand_id=:brand_id',
            'params' => array(':brand_id'=>$model->id),
            'order' => 'price',
        ),
        'pagination'=>false,
        'cache_duration' => 3600,
    )
);