Всем привет!
Хочу поделиться своим опытом кэширования результатов запроса на стороне сервера.
На практике часто бывает очень много статичных данных, например, какой-нибудь справочник, который очень редко изменяется, если изменяется вообще. Чтобы не делать лишние запросы, можно просто один раз подтянуть все данные из БД, сериализовать и закэшировать на определенное время. Плюсы очевидны, меньше запросов к БД.
В итоге я создал trait, который принимает параметры для запроса и сохраняет их в кэш, если еще нет данных, иначе данные просто подтягиваются из кэша.
<?php
namespace commonmytraits;
use Yii;
use yiidbQuery;
/**
* Class CachedKeyValueData
* @package commonmytraits
*/
trait CachedKeyValueData
{
public static function getCachedKeyValueData(
$table,
$fields,
$where,
$key,
$defaultValue = []
)
{
if (($data = unserialize(Yii::$app->cache->get($table . $key))) === false) {
$data = $defaultValue;
$rows = (new Query)
->select($fields)
->from($table)
->where($where)
->all();
if ($rows) {
foreach ($rows as $row) {
$data[$row[$fields[0]]] = $row[isset($fields[1]) ?
$fields[1] : $fields[0]];
}
}
Yii::$app->cache->set($table . $key, serialize($data), 86400);
}
return $data;
}
}
Для возможности использования этого trait'а во всех моделях на проекте лучше создать базовый класс ActiveRecord'а, в котором реализовать логику по инвалидации кэша. Так как данные могут измениться, кэш надо будет сбросить:
<?php
namespace commonmyyii2;
use Yii;
use yiidbActiveRecord as YiiActiveRecord;
use commonmytraitsCachedKeyValueData;
/**
* Class ActiveRecord
* @package commonmyyii2
*/
class ActiveRecord extends YiiActiveRecord
{
use CachedKeyValueData;
/**
* @return bool
*/
protected function deleteCache()
{
$tableName = trim(self::tableName(), '{%}');
if ($this->hasMethod('getAllForLists')) {
Yii::$app->cache->delete($tableName . 'getAllForLists');
}
return true;
}
/**
* @param bool $insert
* @return bool
*/
public function beforeSave($insert)
{
if (parent::beforeSave($insert)) {
$this->deleteCache();
return true;
} else {
return false;
}
}
/**
* @return bool
*/
public function beforeDelete()
{
if (parent::beforeDelete()) {
$this->deleteCache();
return true;
} else {
return false;
}
}
}
Затем можно использовать этот метод из trait'а во всех моделях-наследниках этого базового ActiveRecord'а и не парится об актуальности данных в кэше, если вся работа с данными в БД ведется через модели.
Простой пример использования в моделе Category — список всех категорий:
<?php
namespace cabinetmodulescategorymodels;
use commonmyyii2ActiveRecord;
class Category extends ActiveRecord
{
/* какой-то код */
public static function getAllForLists()
{
return self::getCachedKeyValueData(
self::tableName(),
['id', 'name'],
['status' => 1],
'getAllForLists'
);
}
}
Затем эти данные можно легко подтянуть в каком-нибудь контроллере, например, если нужен список всех категорий для новостей:
<?php
namespace cabinetmodulesnewscontrollers;
use Yii;
use cabinetmodulescategorymodelsCategory;
use cabinetmodulesnewsmodelssearchNewsSearch;
use cabinetmyyii2Controller;
/**
* NewsController implements the CRUD actions for News model.
*/
class NewsController extends Controller
{
/* какой-то код */
public function actionIndex()
{
$searchModel = new NewsSearch;
$dataProvider = $searchModel->search(Yii::$app->request->getQueryParams());
return $this->render('index', [
'dataProvider' => $dataProvider,
'searchModel' => $searchModel,
'categories' => Category::getAllForLists(),
]);
}
}
Всем спасибо за внимание. Надеюсь статья будет полезной.