CakePHPでLazy ローディング

今回はCakePHPでLazy ローディングを実装します。

Eager ローディング
できるだけ 少ない クエリーでDBから情報を取得できるようにJOINを(可能なときは)使います。 HasMany アソシエーションを使うような分割したクエリーが必要なときは、1つのクエリーで、 現在のオブジェクト一式に必要な 全て の関連データを取ってこようとします。

Lazy ローディング
絶対に必要になるまでアソシエーションのロードを遅延させます。 これにより、不要なデータがオブジェクト化されないので CPU 時間を節約できますが、 大量のクエリーがDBに送られることになるかもしれません。 例えば、 複数の記事 (articles) とそれに属するコメント (comments) を舐めるループでは、 イテレートされた記事の数だけクエリーが何度も送られることになります。
引用:CakePHP 公式ドキュメントより

CakePHPのORMにはLazy ローディングは含まれていないので実現するためにはLazyLoadプラグインを使うことが推奨されています。

今回はこのプラグインを使わずに簡単にLazyローディングを実現します。 次のようにエンティティに _getプロパティ名()関数 を実装することでアクセサ経由で暗黙的にデータをとってきてくれるので呼び出し側の責任をなくすことができます。

namespace App\Model\Entity;

use Cake\ORM\Entity;
use Cake\ORM\TableRegistry;

class Sale extends Entity {

    protected function _getDeposits() {
        $id = $this->id;
        $deposits = TableRegistry::get('Deposits');
        return $deposits->find('all')
            ->contain([
                'Sales',
                'Payees'
            ])
            ->matching('Sales', function ($sale) use ($id) {
                return $sale->where(['Sales.id' => $id]);
            })
            ->all();
    }

}
$sale = $this->Sales->findById($id);
foreach ($sale->deposits as $deposit) {
    echo $deposit->payee->name;
}

参考CakePHP3のORMを使う際に欠かせない概念について