フラグメント・キャッシュ
========================

フラグメント・キャッシュは、ウェブ・ページの断片をキャッシュすることを指します。
例えば、ページ内の表に年間販売の概要が表示されている場合、リクエスト毎にこの表を生成するのにかかる時間を削減するために、キャッシュにこの表を格納することができます。
フラグメント・キャッシュは [データ・キャッシュ](caching-data.md) 上に構築されています。

フラグメント・キャッシュを使用するには [ビュー](structure-views.md) で以下の構文を使用します:

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

    // ... ここに生成するコンテントを書く ...

    $this->endCache();
}
```

つまり、コンテント生成ロジックを [[yii\base\View::beginCache()|beginCache()]] と [[yii\base\View::endCache()|endCache()]] の呼び出しのペアで囲みます。
コンテントがキャッシュ内で見つかった場合、[[yii\base\View::beginCache()|beginCache()]] はキャッシュされたコンテントをレンダリングして
`false` を返し、結果として、コンテント生成ロジックがスキップされます。
それ以外の場合はコンテント生成ロジックが呼ばれ、そして [[yii\base\View::endCache()|endCache()]] が呼ばれたときに、
生成されたコンテントがキャプチャされ、キャッシュに格納されます。

[データ・キャッシュ](caching-data.md) と同様に、キャッシュされるコンテントを識別するためにユニークな `$id` が必要になります。

次のようにすると、フラグメント・キャッシュを削除することが出来ます。
```php
Yii::$app->cache->delete(['yii\widgets\FragmentCache', $id]);
```


## キャッシュのオプション <span id="caching-options"></span>

[[yii\base\View::beginCache()|beginCache()]] メソッドの 2 番目のパラメータとしてオプションの配列を渡すことによって、
フラグメント・キャッシュに関する追加のオプションを指定することができます。
舞台の裏側では、このオプションの配列が、実際のフラグメント・キャッシュ機能を実装する
[[yii\widgets\FragmentCache]] ウィジェットを構成するために使用されます。

### 持続時間 <span id="duration"></span>

おそらくフラグメント・キャッシュで通常よく使われるであろうオプションは [[yii\widgets\FragmentCache::duration|duration]] でしょう。
このオプションはコンテントがどれだけの時間キャッシュ内において有効であるかを指定します。
以下のコードは最大で 1 時間コンテントの断片をキャッシュします:

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

    // ... ここに生成するコンテントを書く ...

    $this->endCache();
}
```

このオプションがセットされていない場合は、デフォルトである 60 が使われます。すなわち、キャッシュされたコンテントの有効期限は 60 秒後に切れることになります。


### 依存 <span id="dependencies"></span>

[データ・キャッシュ](caching-data.md#cache-dependencies) と同様に、キャッシュされたコンテントの断片は依存を持つことができます。
例えば、表示されている投稿の内容は、投稿が変更されたか否かに依存します。

依存を指定するには [[yii\widgets\FragmentCache::dependency|dependency]] オプションに [[yii\caching\Dependency]]
オブジェクトを指定するか、または依存オブジェクトを作成するための構成情報配列を指定します。
以下のコードはコンテントの断片が `updated_at` カラムの値の変化に依存していることを指定しています:

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

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

    // ... ここに生成するコンテントを書く ...

    $this->endCache();
}
```


### バリエーション <span id="variations"></span>

キャッシュされるコンテントには、何らかのパラメータによってバリエーションを持たせることが出来ます。
例えば、複数の言語をサポートしているウェブ・アプリケーションでは、ビュー・コードの同じ部分が言語によってさまざまに異なるコンテントを生成することが有り得ます。
従って、現在のアプリケーションの言語に応じて、キャッシュされるコンテントのバリエーションを持つ必要があります。

キャッシュのバリエーションを指定するには [[yii\widgets\FragmentCache::variations|variations]] オプションを指定します。
このオプションは、それぞれが特定のバリエーションの要素を表すスカラ値の配列でなければなりません。
例えば、言語によるキャッシュ・コンテントのバリエーションを持つためには、以下のコードを使います。

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

    // ... ここに生成するコンテントを書く ...

    $this->endCache();
}
```


### キャッシュをトグルする <span id="toggling-caching"></span>

時として、ある条件が満たされた場合にのみフラグメント・キャッシュを有効にしたい場合があるでしょう。
たとえば、フォームが表示されているページでは、フォームをキャッシュしたいのは最初の (GET リクエストによる) リクエストの場合だけです。
その後の (POST リクエストによる) フォームの表示では、フォームにユーザ入力が含まれている可能性があるため、キャッシュをすべきではありません。
これを行うには、以下のように [[yii\widgets\FragmentCache::enabled|enabled]] オプションをセットします:

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

    // ... ここに生成するコンテントを書く ...

    $this->endCache();
}
```


## キャッシュのネスト <span id="nested-caching"></span>

フラグメント・キャッシュはネストすることができます。つまり、キャッシュされる断片を、それ自体もキャッシュされる別の断片に入れることができます。
例えば、内側のフラグメント・キャッシュにはコメントがキャッシュされており、外側のフラグメント・キャッシュには記事内容と一緒にコメントもキャッシュされている、という形です。
以下のコードは 2 つのフラグメント・キャッシュをネストする方法を示すものです。

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

    // ...コンテント生成ロジック...

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

        // ...コンテント生成ロジック...

        $this->endCache();
    }

    // ...コンテント生成ロジック...

    $this->endCache();
}
```

ネストされたキャッシュには、異なるキャッシュ・オプションを設定することができます。
たとえば、上記の例における内側のキャッシュと外側のキャッシュに対して、異なる持続期間の値を設定する事が可能です。
これによって、外側のキャッシュでキャッシュされたデータが無効になった場合でも、内側のキャッシュが有効な内側の断片を提供することが可能になります。
しかし、その逆は真ではありません。
外側のキャッシュが有効であると判断された場合には、内側のキャッシュが無効になった後でも、外側のキャッシュが古くなったコンテントのコピーを提供し続けます。
従って、ネストされたキャッシュの持続時間や依存の設定を間違うと、無効になった内側のキャッシュ・データが外側のキャッシュに残り続けることになるので、注意が必要です。


## ダイナミック・コンテント <span id="dynamic-content"></span>

フラグメント・キャッシュを使用する際、出力全体が比較的静的で、一ヶ所ないし数ヶ所だけが例外的に動的であるというような状況に遭遇するでしょう。
例えば、ページのヘッダがメイン・メニュー・バーと現在のユーザ名を一緒に表示している場合です。
もう一つの問題は、キャッシュされるコンテントに、リクエスト毎に実行しなければいけない PHP のコード 
(例えば、アセット・バンドルを登録するためのコード) が含まれている場合です。
この両方の問題は、いわゆる *ダイナミック・コンテント* 機能によって解決することができます。

ダイナミック・コンテントは、それがフラグメント・キャッシュの中に含まれていても、キャッシュすべきではない出力の部分を意味します。
このコンテントを常に動的にするためには、外側のコンテントがキャッシュから提供されている場合でも、
すべてのリクエストに対して、何らかの PHP コードを実行することにより生成しなければいけません。

ダイナミック・コンテントを目的の場所に挿入するには、以下のように、キャッシュされる断片内で
[[yii\base\View::renderDynamic()]] を呼び出します。

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

    // ...コンテント生成ロジック...

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

    // ...コンテント生成ロジック...

    $this->endCache();
}
```

[[yii\base\View::renderDynamic()|renderDynamic()]] メソッドはパラメータとして PHP コードを取ります。
この PHP コードの戻り値が、ダイナミック・コンテントとして扱われます。
囲んでいる断片がキャッシュから提供されるか否かにかかわらず、同じ PHP コードがすべてのリクエストに対して実行されます。

> Note: バージョン 2.0.14 以降、[[yii\base\DynamicContentAwareInterface]] インタフェイスとその [[yii\base\DynamicContentAwareTrait]] トレイトによって、ダイナミック・コンテント API が公開されています。.
その一例としては、 [[yii\widgets\FragmentCache]] クラスを参照して下さい。
