FuelPHPのORMにはキャッシュ機能があるが,場合によっては邪魔になってしまう.
キャッシュ機能については
http://tech.respect-pal.jp/fuelphp-orm-cache/
に詳しい解説がある.
今回,DBへのアクセスが発生するユニットテストで
「テストケースごとにテーブルをTRUNCATEしているのに,ORMのキャッシュ機能のせいで前のテストケースで行われた変更が次のテストケースに影響を与えてしまう」
という問題に遭遇した.
(テストは,https://phpunit.de/manual/current/ja/database.html を参考にし,テストケースごとに自動でテーブルのTRUNCATEとフィクスチャのロードが行われるようにしている)
例えば,テストケースAで
$obj = Model_Foo::forge(array('id' => 1));
$obj->save();
したあと,
テストケースBで
if (Model_Foo::find(1))
{
echo 'exists';
}
などとすると,テストケースごとにTRUNCATEしているのにテストケースBでModel_Foo
のエンティティが取り出されてしまう.
この問題に対し,ORMのキャッシュをクリアすることで解決を図った.
#リフレクションによる解決
Orm\Model
は$_cached_objects
という静的プロパティにオブジェクトをキャッシュしているが,
Orm\Model
にはこの変数をクリアするメソッドが提供されていない.
http://tech.respect-pal.jp/fuelphp-orm-cache/ ではOrm\Model
を継承した独自クラスを作り,$_cached_objects
をクリアするメソッドを追加して解決している.
しかし,既に多くのクラスがOrm\Model
を継承するようになっているのを,独自クラスを継承するよう書きなおすのはしたくなかったので,
(テストでのみ利用される独自クラスを定義してテスト用のbootstrapで読み込むようにしてもよかったのだが,)
今回はリフレクションを使ってキャッシュをクリアするようにした.
これによりメインのコードには変更を加えず,テスト用コードのみの変更で済む.
具体的には,
// Orm\Model::$_cached_objectsをクリアする
$model_reflection = new \ReflectionClass('Orm\\Model');
$prop = $model_reflection->getProperty('_cached_objects');
$prop->setAccessible(true);
$prop->setValue(null, array());
のようにする.
これを各テストケース実行時に呼べば良い.
私はDB接続があるテストが共通して継承する親クラスのsetup()
に書いている.
protected function setup()
{
parent::setup();
// Orm\Model::$_cached_objectsをクリアする
$model_reflection = new \ReflectionClass('Orm\\Model');
$prop = $model_reflection->getProperty('_cached_objects');
$prop->setAccessible(true);
$prop->setValue(null, array());
}