Doctrine Result Cache の使い方[Symfony]

SymfonyとDoctrineの記事になります。
今回の記事の各Versionは下記です。
Symfony 3.4
Doctrine 2.12

EC-CUBEというECサイトフレームワークを使用している案件で、改めてキャッシュ周りを見直したので備忘録になります。
発端はDoctrine 1.xのときにキャッシュを削除する際に、前方一致で削除する、deleteByPrefixがなくなっていたことです。
キャッシュ周りの変更をまとめると下記になります。(英文まま)

## Cache Changes
  
- Renamed old AbstractCache to CacheProvider

- Dropped the support to the following functions of all cache providers:
  - CacheProvider::deleteByWildcard
  - CacheProvider::deleteByRegEx
  - CacheProvider::deleteByPrefix
  - CacheProvider::deleteBySuffix

- CacheProvider::deleteAll will not remove ALL entries, it will only mark them as invalid
- CacheProvider::flushAll will remove ALL entries, namespaced or not
- Added support to MemcachedCache
- Added support to WincacheCache

deleteByXXXという関数が軒並み消えていたんですね。
Result Cacheを使用する際に、IDに接頭語を付けて、前方一致で管理していたものが使えないといった感じです。
まぁ、ID管理じゃなくて、きちんとCacheにnamespaceを付けて管理してねということですね。
namespaceごとにキャッシュクリア(deleteAll)したりできます。
今回は、Result Cacheに関して使い方を説明したいと思います。ほかのキャッシュ等の情報は下記参照。
Official(English)::doctrine-project — Caching

SymfonyでResultCacheを使用できるように設定する

まずは設定ですね。参考は下記です。
Official(English)::Configuration Reference — Cache Driver
Github::symfony / demo
様々カスタマイズできますが、サンプルとして、Githubのものをそのまま使用します。

doctrine:
    orm:
        auto_generate_proxy_classes: false
        metadata_cache_driver:
            type: pool
            pool: doctrine.system_cache_pool
        query_cache_driver:
            type: pool
            pool: doctrine.system_cache_pool
        result_cache_driver:
            type: pool
            pool: doctrine.result_cache_pool

framework:
    cache:
        pools:
            doctrine.result_cache_pool:
                adapter: cache.app
            doctrine.system_cache_pool:
                adapter: cache.system

上記をdoctrine.yamlに記載したら設定は完了です。

Result Cache を使用する

Result CacheはDQL queryの結果をキャッシュすることが可能です。DQL queryを作成することで、関連メソッドが使用可能になります。

// Case Repository (extends ServiceEntityRepository)

$qb = $this->createQueryBuilder('user')
    ->where('user.id = :id')
    ->setParameter('id', $userId);    
$qb->getQuery()
    ->useResultCache(true, 600, 'UserInfo')
    ->getResult();

getQuery()でDQL queryが作成された後、関連メソッドが使用可能になります。
ResultCacheに格納するためにuseResultCacheを使用しています。
引数が三つありますが、各説明は下記です。

[1]キャッシュドライバ
TRUE: 既定のキャッシュドライバを使う
FALSE: キャッシュを利用しない
キャッシュドライバ指定
[2]キャッシュの存続秒数
省略時はドライバのデフォルト値
[3]ID
省略時は自動生成されるMD5ハッシュ値

その他メソッドは下記を参照してください。
https://doctrine2.readthedocs.io/en/latest/reference/caching.html
今回はまだnamespaceを指定していないので、namespaceが無いキャッシュが作成されます。
例えば、namespaceの指定なしで、deleteAllすると、namespaceが無いキャッシュがすべて削除されます。

Result Cache を削除する

ResultCacheを削除する方法です。delete($id)deleteAllがあります。
DQL queryからドライバを呼び出して削除します。

// Case Repository (extends ServiceEntityRepository)

$qb = $this->getEntityManager()->createQuery();

$cacheDriver = $qb->getResultCacheDriver();
$cacheDriver->delete('UserInfo');

上記例だと、IDがUserInfoのキャッシュが削除されます。簡単ですね。

さて、次は今回の私のテーマだった、ある特定のキャッシュだけをまとめて削除したい場合です。
doctrine1.xのときは、IDに接頭語を付けて、前方一致でdeleteByPrefix()で削除していました。
その方法は使えなくなったので、namespaceでキャッシュを管理します。

namespaceを使用してresultCacheを管理する

簡単に言えば、namespaceという箱を作ってあげるだけですね。そこにキャッシュを格納していきます。
複数プロジェクトがあったり、User用のキャッシュ、Admin用のキャッシュなど区分けしたいときに便利です。
簡単なサンプルを紹介します。

// Case Repository (extends ServiceEntityRepository)

// use ResultCashe in namespace "User"
$qb = $this->createQueryBuilder('user')
    ->where('user.id = :id')
    ->setParameter('id', $userId)
    ->getQuery();

$cacheDriver = $qb->getResultCacheDriver();
$cacheDriver->setNamespace('User');

$result = $qb->useResultCache(true, 600, 'UserInfo')
    ->getResult();

// DeleteAll namespace "User"
$qb = $this->getEntityManager()->createQuery();
$cacheDriver = $qb->getResultCacheDriver();
$cacheDriver->setNamespace('User');
$cacheDriver->deleteAll();

キャッシュドライバを呼び出し、setNamespace()でDQL queryに対してnamespaceを設定しています。
上記コードでdeleteAllを実行した場合、namespaceが「User」であるキャッシュ群が削除されます。

以上簡単ですが、resultCacheの使い方とnamespaceの設定の仕方についてまとめてみました。
細かいところは、文中で紹介したURLから見ていただいたほうがよいと思います。

Symfonyおすすめ書籍

あまり種類がないのと、そもそもフレームワークとして情報が膨大なので、入門でも結構読み応えがあります。
「PHPフレームワーク Symfony 4入門」