Поправка: в первом сообщении даже не провайдер кэшируется, а только Query.
Исследование:
как оно работает: DataProvider - класс, куда мы передаем базовый query (не сам запрос, а только базовый объект ActiveQuery, т.е. User::find() без ->all()). Дальше с помощью пагинации, фильтров, сортировок DataProvider добавляет к query необходимые ->limit()->offset()->andWhere()->orWhere() итд. И перед самым рендерингом данных в методе prepareModels() добавляет ->all() и получает собственно модели (с учетом текущей страницы, сортировок и фльтров). Соответственно, чтобы закэшировать дата провайдер нам надо кэшировать объект провайдера с уже полностью сформированным объектом (страницы, фильтры итд) и сделанным запросом. Понятно, что для первой страницы и второй страницы кэш будет разный. Для первой страницы без фильтров и с фильтрами кэш тоже будет разный. Итд.
Изучаем код:
GridView::renderTableBody()
Код: Выделить всё
$models = array_values($this->dataProvider->getModels());
$keys = $this->dataProvider->getKeys();
Идем в ActiveDataProvider::getModels() (по факту BaseDataProvider::getModels()):
Код: Выделить всё
public function getModels()
{
$this->prepare();
return $this->_models;
}
сначала готовит модели (т.е. сам запрос, вносящий модели в переменную _models), потом возвращает
BaseDataProvider::prepare():
Код: Выделить всё
public function prepare($forcePrepare = false)
{
if ($forcePrepare || $this->_models === null) {
$this->_models = $this->prepareModels();
}
if ($forcePrepare || $this->_keys === null) {
$this->_keys = $this->prepareKeys($this->_models);
}
}
как видим, метод готовит модели и ключи, если $_models еще не заполнена.
Проверим ActiveDataProvider::prepareModels()
Код: Выделить всё
protected function prepareModels()
{
if (!$this->query instanceof QueryInterface) {
throw new InvalidConfigException('The "query" property must be an instance of a class that implements the QueryInterface e.g. yii\db\Query or its subclasses.');
}
$query = clone $this->query;
if (($pagination = $this->getPagination()) !== false) {
$pagination->totalCount = $this->getTotalCount();
$query->limit($pagination->getLimit())->offset($pagination->getOffset());
}
if (($sort = $this->getSort()) !== false) {
$query->addOrderBy($sort->getOrders());
}
return $query->all($this->db);
}
как видим именно этот метод применяет настройки грида и превращает query в сам запрос.
Делаю вывод: кэшировать DataProvider нужно ТОЛЬКО ПОСЛЕ ПОДГОТОВКИ МОДЕЛЕЙ.
Псевдокод:
Код: Выделить всё
$query = User::find();
$dataProvider = new ActiveDataProvider(['query' => $query]);
$cacheKey = md5(json_encode($dataProvider ));
if (!$dataProviderCache = $cache->get($cacheKey )) {
$dataProvider->prepare();
$cache->set($cacheKey , $dataProvider, 3600);
$dataProviderCache = $dataProvider;
}
Честно говоря, код писал на коленке, поэтому могут быть ошибки, причем критические. Но общий смысл понятен.