🔖

[CakePHP]Entityの仮想フィールドでselect文を発行したらN+1になった

2022/09/29に公開

CakePHPにはテーブルに存在しないフィールドにアクセスする機能があります(どうでもいいけど公式ドキュメントの和訳は"フィールド"じゃなくて"プロパティ"になってる)

https://book.cakephp.org/4/ja/orm/entities.html#entities-virtual-fields

他のテーブル(エンティティ)の情報を仮想フィールドに利用したい場合にその情報をselect文で取得して利用しようとして以下のようにしてみた。

class Article extends Entity
{
    protected function _getAuthorBirthYear()
    {
        $author_id = $this->_fields['author_id'];
        $authorsTable = TableRegistry::getTableLocator()->get('Authors');
        $author = $authorsTable->get($author_id);
        
	return (new FrozenTime($author->birth_date))->format('Y');
    }
}

そしてAtriclesの一覧を取得して該当の仮想フィールドにアクセスしたら当たり前ですが大量のauthorのselect文(いわゆるn+1)が流れるようになって著しくパフォーマンスが落ちました。

なのであらかじめ関連するデータをロード(この場合だとauthorのデータ)して仮装フィールド内で_fieldsプロパティから情報を取得するようにしたら上記の問題が解消しました。

protected function _getAuthorBirthYear()
    {
        if (isset($this->_fields['author'])) {
            // あらかじめauthor情報を取得して、そちらを使用する
            $author = $this->_fields['author'];
        } else {
            $authorsTable = TableRegistry::getTableLocator()->get('Author');
            $author = $usersTable->get($this->_fields['author_id']);
        }

        return (new FrozenTime($author->birth_date))->format('Y');
    }

Discussion