Или повесть о том, как можно нечаянно выстрелить себе в ногу.
Исходные данные
Сижу себе отлаживаю код, который затрагивает кеширование CActiveRecord.
Последовательность:
- Создаем модель
- Задаем условия кеширования
- Создаем CActiveDataProvider
- Используем его при выводе CGridView
Часть кода № 1, контроллер:
public function actions(){
return array(
'list' => array(
'view' => 'list',
'class' => 'appcomponentsactionsGridAction',
'columns' => array(
'display_name',
'email',
array(
'name' => 'role',
'filter' => array(
User::ROLE_ADMIN => Yii::t('admin','Administrator'),
User::ROLE_MANAGER => Yii::t('admin','Manager'),
User::ROLE_USER => Yii::t('admin','User')
)
)
),
'beforeRender' => function() {
$this->breadcrumbs[] = Yii::t('admin', 'Manage users');
},
'model' => function(){
$model = new User('search');
return $model->cache(new Tags('users'));
}
);
}
Часть кода № 2, экшн:
ApplicationHelpers::loadData($this->model);
$provider = $this->model->search();
$filter = $this->model;
$grid = $this->controller->createWidget('appwidgetsgridGridView',array(
'dataProvider' => $provider,
'columns' => !empty($this->columns)?$this->columns:array(),
'id' => ApplicationHelpers::safeHtmlName($this->controller)."_{$this->id}_grid",
'filter' => $filter
));
Грид вывелся, смотрю лог:
Querying SQL: SELECT * FROM `tbl_user` `t` LIMIT 50
in
protected/widgets/grid/GridView.php
(18)
in
protected/components/actions/GridAction.php
(34)
in admin.php (18)
кешированием и не пахнет.
Параметры кеширования сохраняются внутри CActiveRecord в public static $db; и слетают после того, как инициализируется CActiveDataProvider в $this->model->search(). «Вот чудеса», недоумеваю я.
После пары минут дебага вспоминаю, что у меня к CActiveDataProvider привязан behavior.
Вот такой:
namespace appcomponentsbehaviors;
class AutoPagingBehavior extends CBehavior{
public function attach($owner)
{
parent::attach($owner);
$pageSize = Yii::app()->params['defaultPageSize'];
if(isset($owner->id)){
$pageSize = Yii::app()->user->getState('pagesize_'.$owner->id,Yii::app()->params['defaultPageSize']);
}
if($requestPageSize = Yii::app()->request->getParam('pagesize',false)){
if(is_array($requestPageSize) && isset($requestPageSize[$owner->id])){
$pageSize = $requestPageSize[$owner->id];
Yii::app()->user->setState("pagesize_{$owner->id}",$pageSize);
}else{
$_GET['pagesize'] = [$owner->id => $pageSize];
}
}
$owner->pagination->setPageSize($pageSize);
if($pageSize == 'all')
$owner->pagination = false;
}
}
Дебаггер усиленно рапортует, что параметры кеширования слетают после Yii::app()->user->getState.
Смотрим наш WebUser.php, а там:
public function init(){
parent::init();
if(!$this->isGuest){
$this->model = User::model()->findByEmail($this->id);
if(empty($this->model)){
$this->logout(true);
Yii::app()->controller->redirect('/');
}
}
}
Вызов User::model()->findByEmail($this->id) это хелпер, в котором запускается кешированный поиск. И этот поиск благополучно перезаписывает параметры кеширования в public static $db, который в CActiveRecord.
Вывод
Получается, что в промежутке между заданием параметров кеширования для поиска и самим поиском через CActiveDataProvider нельзя выполнять другие кешированные вызовы любой модели, потому что CDbConnection один на всех. Путь исправления такого поведения очевиден — сохранять параметры кеширования непосредственно в экземпляре CActiveRecord и применять в момент выполнения запроса (CActiveRecord#query)
Итог
Автор: AMar4enko