Как известно, кэширование - это способ повышения производительности (скорости) WEB-приложения, при котором данные не генерируются кодом php и базой данных каждый раз, а сохраняются в определенном месте при первом запросе и потом только считываются. Место хранения кэша и время которое он хранится устанавливается разработчиком.

Подробнее об этом можно почитать тут, а в этой заметке, я приведу несколько практических приемов работы с кэшем.


Общий кэш для backend и frontend.

По-умолчанию кэш в Yii2 (Advanced) сохраняется в виде файла в папки:
  • backend\runtime\cache;
  • frontend\runtime\cache.
То есть у frontend и backend свои папки кэша. Это значит что из админки, например, нельзя удалить кэш frontenda. Посмотреть куда сохраняется кэш для текущего приложения можно вывев результат команды:
Yii::$app->cache
на экран. Чтобы иметь возможность работать с кэшем другого приложения (например из backend удалить кэш frontendа) можно указать для этих приложений общее хранилище в конфигурации. Например сделаем, чтобы backend хранил свой кэш там же где и frontend.
В файле common\config\main.php нужно дописать строку с "cachePath":
'components' => [
    'cache' => [    
            'class' => 'yii\caching\FileCache',
            'cachePath' => '@frontend/runtime/cache', 
    ],

Кэш в базе данных, миграция.

Среди вариантов выбора мест хранения кэша, есть такой как хранение его в базе данных. Для этого необходимо указать в все том же файле common\config\main.php класс работающий с БД:
'components' => [
    'cache' => [    
            'class' => 'yii\caching\DbCache',
    ],
Чтобы использовать этот кэш, нужно создать таблицу определенным образом. Если пользуетесь миграциями при работе с БД, или по-крайней мере умеете их запускать, вот готовый код миграции создающей такую таблицу:
public function safeUp()
{
    $tableOptions = null;
    if ($this->db->driverName === 'mysql') {
            $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE=InnoDB';
    }

    $this->createTable('{{%cache}}', [    
           'id' => $this->primaryKey(),
           'expire' => $this->integer(11),
            'data' => $this->text(),
    ], $tableOptions);
}

public function safeDown()
{
    $this->dropTable('{{%cache}}');
}




Автоматическое удаление кэша при обновлении записи в базе данных.

При внесении изменений в БД (например редактирование поста в админке), данные в кэше становятся не актуальными. Использование зависимостей (dependency) не всегда помогает (например если запись была изменена, а не добавлена). В этом случае можно к модели, которая отвечает за нужную таблицу используемую в кэшировании (например виджета), подключить поведение с событиями для ActiveRecord:
EVENT_AFTER_INSERT
EVENT_AFTER_UPDATE
EVENT_AFTER_DELETE
которые при каждом срабатывании будут удалять нужный кэш. Для того, чтобы удалить кэш frontenda из админки, используется общее хранилище кэша для backend и frontend (как сделать - читать выше).

Создаем файл CachedBehavior.php в папке common\components\behaviors
<?php
namespace common\components\behaviors;

use Yii;
use yii\base\Behavior;
use yii\db\ActiveRecord;

class CachedBehavior extends Behavior
{
    //id кэша - название в виде массива
    public $cache_id;

    public function events()
    {
        return [
            ActiveRecord::EVENT_AFTER_INSERT => 'deleteCache',
            ActiveRecord::EVENT_AFTER_UPDATE => 'deleteCache',
            ActiveRecord::EVENT_AFTER_DELETE => 'deleteCache',
        ];
    }
 
    public function deleteCache()
    {
        //Удаление массива кэшированных элементов (виджеты, модели...)
        Foreach ($this->cache_id as $id){
        Yii::$app->cache->delete($id);
        }
    }
}
Тут мы создали класс-поведение (подробнее про поведения тут), который с помощью метода events() устанавливает обработчики событий (про события тут) для класса работающего с БД (ActiveRecord). А именно - при вставке/обновлении/удалении данных, будет вызываться метод deleteCache(), который будет удалять нужный кэш (по переданному из модели названию кэша/id).

Подключение в модели, которая обновляет данные хранимые в кэше:
public function behaviors()
{
    parent::behaviors();
    return [
            'CachedBehavior' => [
            'class' => \common\components\behaviors\CachedBehavior::className(),
            'cache_id' => ['CloudWidget'],
            ],
    ];
}
тут в качестве элемента массива 'cache_id' нужно передать название кэша который нужно удалить (id кэша), в данном случае 'CloudWidget'. Можно передавать несколько значений.


Автоматическое удаление кэша при выполнении действий контроллера.

Если вы выполняете действия контроллера которые делают неактуальными файлы в кэше, стоит настроить автоматическое удаление таких файлов. Например в админке удаляете пост (действие actionDelete()) и желаете изменить файл карты сайта для поисковых служб, т.к. он становится неактуальным. Это можно сделать с помощью кода указанного в предыдущей главе, основываясь на событиях ActiveRecord, но данная модель с привязкой поведения используется и в frontend при выводе тех же постов, где привязка такого поведения не нужна. Поэтому есть вариант основанный на событии EVENT_BEFORE_ACTION контроллера. Причем того, который нужен - в backend.

Для того, чтобы удалить кэш frontenda из админки, используется общее хранилище кэша для backend и frontend (как сделать - читать выше).
Вставляем класс поведения в backend\controllers\PostController.php
    public function behaviors()
    {
        return [
            //Класс удаление кэша при выполнении указанных действий
            [
                'class' => '\common\components\behaviors\DelcacheBehavior',
                'cache_id' => ['CloudWidget'],
               'actions' => ['create', 'update', 'delete'],
             ],
        ];
Файл common\components\behaviors\DelcacheBehavior.php
<?php
/*
 * Класс удаляющий кэш по переданным в массиве $cache_id названиям (id) кэша
 * перед выполнением указанных действий контроллера в  массиве $actions
 */
namespace common\components\behaviors;

use Yii;
use yii\base\Behavior;
use yii\web\Controller;

class DelcacheBehavior extends Behavior {

     public $cache_id; //id кэша (названия в виде массива)
     public $actions; //для каких действий контроллера

     public function events()
     {
         return [
             Controller::EVENT_BEFORE_ACTION  => 'deleteCache',
         ];
     }
 
    public function deleteCache()
    {
        $action_name = $this->owner->action->id; //название текущего действия
        if(array_search($action_name, $this->actions)=== FALSE) return;

        //Удаление массива кэшированных элементов (виджеты, модели...)
        Foreach ($this->cache_id as $id){
            Yii::$app->cache->delete($id);
        }
    }
}
Все, файл кэша CloudWidget удален.


Примеры кэширования.

1. Кэширование фрагментов страницы.
Применяется, если нужно закэшировать часть страницы, например вывод виджетов:
<?php 
    //кеш на час
    if ($this->beginCache('TagListWidget', ['duration' => 3600])) { 
    echo common\widgets\TagListWidget::widget();     
    $this->endCache(); } 
?>

<?php 
    //Зависимость кеша от кол-ва записей
    $dependency = [
            'class' => 'yii\caching\DbDependency',
            'sql' => 'SELECT COUNT(*) FROM {{%comments}}',
    ];    
    if ($this->beginCache('LcommentsWidget', ['dependency' => $dependency])) { 
            echo common\widgets\LcommentsWidget::widget();     
            $this->endCache();     
    } 
?>

2. Кеширование данных (значения переменной).
$cache = Yii::$app->cache;
//Кешируем массив $arr_tags    
if (!$arr_tags = $cache->get('TaglistWidget_init')){
    //Получаем данные из таблицы (модель TagPost)
    $tag_post = TagPost::find()
            ->select('tag_id')
            ->where(['not',['post_id' => null]])
            ->all();
    $arr_tags = array();

    foreach($tag_post as $tag_id){
            $arr_tags[] = $tag_id->tag_id;
    }

    //Устанавливаем зависимость кеша от кол-ва записей в таблице
    $dependency = new \yii\caching\DbDependency(['sql' => 'SELECT COUNT(*) FROM {{%tag_post}}']);
    $cache->set('TaglistWidget_init', $arr_tags, null, $dependency);
}

3. Кэширование запросов к БД.
Кэширование запросов может быть использовано как для DAO, так и для ActiveRecord
$result = $db->cache(function ($db) { 
    // Результат SQL запроса будет возвращен из кэша если 
    // кэширование запросов включено и результат запроса присутствует в кэше 
    return $db->createCommand('SELECT * FROM customer WHERE id=1')->queryOne(); 
});

Не забудьте, что есть так же клиентское HTTP-кэширование и Gzip сжатие ресурсов на сервере перед отправкой клиенту. Подробнее тут.