我想缓存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,
)
);