Кэширование фрагментов

Кэширование фрагментов относится к кэшированию фрагментов страницы. Например, если страница отображает в таблице суммарные годовые продажи, мы можем сохранить эту таблицу в кэше с целью экономии времени, требуемого для создания таблицы при каждом запросе. Кэширование фрагментов основано на кэшировании данных.

Для кэширования фрагментов используйте следующий код в представлении:

if ($this->beginCache($id)) {

    // ... здесь создаём содержимое ...

    $this->endCache();
}

Таким образом заключите то, что вы хотите закэшировать между вызовом beginCache() и endCache(). Если содержимое будет найдено в кэше, beginCache() отобразит закэшированное содержимое и вернёт false, минуя генерацию содержимого. В противном случае будет выполнен код генерации контента и когда будет вызван endCache(), то сгенерированное содержимое будет записано и сохранено в кэше.

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

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

Вызывая метод beginCache(), мы можем передать в качестве второго аргумента массив, содержащий параметры кэширования для управления кэшированием фрагмента. Заглядывая за кулисы, можно увидеть, что этот массив будет использоваться для настройки виджета yii\widgets\FragmentCache, который реализует фактическое кэширование фрагментов.

Срок хранения

Наверное, наиболее часто используемым параметром является duration. Он определяет какое количество секунд содержимое будет оставаться действительным (корректным). Следующий код помещает фрагмент в кэш не более, чем на час:

if ($this->beginCache($id, ['duration' => 3600])) {

    // ... здесь создаём содержимое ...

    $this->endCache();
}

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

Зависимости

Так же как и кэширование данных, кэшируемое содержимое фрагмента тоже может иметь зависимости. Например, отображение содержимого сообщения зависит от того, изменено или нет это сообщение.

Для определения зависимости мы устанавливаем параметр dependency, который может быть либо объектом yii\caching\Dependency, либо массивом настроек, который может быть использован для создания объекта yii\caching\Dependency. Следующий код определяет содержимое фрагмента, зависящее от изменения значения столбца updated_at:

$dependency = [
    'class' => 'yii\caching\DbDependency',
    'sql' => 'SELECT MAX(updated_at) FROM post',
];

if ($this->beginCache($id, ['dependency' => $dependency])) {

    // ... здесь создаём содержимое ...

    $this->endCache();
}

Вариации

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

Чтобы задать вариации кэша, установите параметр variations, который должен быть массивом, содержащим скалярные значения, каждое из которых представляет определенный коэффициент вариации. Например, чтобы кэшировать содержимое в зависимости от языка приложения, вы можете использовать следующий код:

if ($this->beginCache($id, ['variations' => [Yii::$app->language]])) {

    // ... здесь создаём содержимое ...

    $this->endCache();
}

Переключение кэширования

Иногда может потребоваться включать кэширование фрагментов только для определённых условий. Например, страницу с формой мы хотим кэшировать только тогда, когда обращение к ней произошло впервые (посредством GET запроса). Любое последующее отображение формы (посредством POST запроса) не должно быть кэшировано, потому что может содержать данные, введённые пользователем. Для этого мы задаём параметр enabled:

if ($this->beginCache($id, ['enabled' => Yii::$app->request->isGet])) {

    // ... здесь создаём содержимое ...

    $this->endCache();
}

Вложенное кэширование

Кэширование фрагментов может быть вложенным. Это значит, что кэшируемый фрагмент окружён более крупным фрагментом (содержится в нём), который также кэшируется. Например, комментарии кэшируются во внутреннем фрагменте кэша, и они же кэшируются вместе с содержимым сообщения во внешнем фрагменте кэша. Следующий код демонстрирует как два фрагмента кэша могут быть вложенными:

if ($this->beginCache($id1)) {

    // ...логика создания контента...

    if ($this->beginCache($id2, $options2)) {

        // ...логика создания контента...

        $this->endCache();
    }

    // ...логика создания контента...

    $this->endCache();
}

Параметры кэширования могут быть различными для вложенных кэшей. Например, внутренний и внешний кэши в вышеприведённом примере могут иметь разные сроки хранения. Даже когда данные внешнего кэша уже не являются актуальными, внутренний кэш может содержать актуальный фрагмент. Тем не менее, обратное не верно. Если внешний кэш актуален, данные будут отдаваться из него даже если внутренний кэш содержит устаревшие данные. Следует проявлять осторожность при выставлении срока хранения и задания зависимостей для вложенных кэшей. В противном случае вы можете получить устаревшие данные.

Динамическое содержимое

Когда используется кэширование фрагментов, вы можете столкнуться с ситуацией когда большой фрагмент содержимого статичен за исключением одного или нескольких мест. Например, заголовок страницы может отображаться в главном меню вместе с именем текущего пользователя. Еще одна проблема в том, что содержимое, которое было закэшировано, может содержать PHP код, который должен выполняться для каждого запроса (например код для регистрации в asset bundle). Обе проблемы могут быть решены с помощью, так называемой функции динамического содержимого.

Динамическое содержимое значит, что часть вывода не будет закэширована даже если она заключена в кэширование фрагментов. Чтобы сделать содержимое динамическим постоянно, оно должно быть создано, используя специальный PHP код.

Вы можете вызвать yii\base\View::renderDynamic() в пределах кэширования фрагмента для вставки динамического содержимого в нужное место, как в примере ниже:

if ($this->beginCache($id1)) {

    // ...логика создания контента...

    echo $this->renderDynamic('return Yii::$app->user->identity->name;');

    // ...логика создания контента...

    $this->endCache();
}

Метод renderDynamic() принимает некоторую часть PHP кода как параметр. Возвращаемое значение этого кода будет вставлено в динамическое содержимое. Этот PHP код будет выполняться для каждого запроса, независимо от того находится ли он внутри кэширования фрагмента или нет.