🎻

[Symfony][Doctrine] STIのdiscriminatorを指定してWHERE句を書きたい

2020/07/23に公開

やりたいこと

  • Doctrineの Single Table Inheritance を使っているエンティティがある
  • このエンティティを所有する別のエンティティのRepositoryにおいて、discriminator(識別子)の値をWHERE句から参照したい

Single Table Inheritanceについては こちらの過去記事 でも紹介しているので参照してみてください✋

普通にやろうとすると…

こちらの過去記事 と同じ、

  • 労働者 というベースクラスを継承した 会社員 エンティティと フリーランス エンティティがある
  • 案件 エンティティが 労働者 エンティティをOneToManyで所有している

というケースを例にコードを見てみたいと思います。

まずはダメなパターンです🙅‍♂️

$filteredWorkers = $this->createQueryBuilder('m')
    ->leftJoin('m.workers', 'w')
    ->andWhere('w.type = :employee')
    ->andWhere('w.salary > :salary')
    ->setParameter('employee', 'employee')
    ->setParameter('salary', 400000)
    ->getQuery()
    ->getResult()
;

一見行けそうに見えますが、実行すると以下のようなエラーになりクエリの組み立てが失敗します。

[Semantical Error] line 0, col xx near 'type = ':employee'': Error: Class App\Entity\Worker has no field or association named type

正解1:派生エンティティごとにリレーションシップを張っておく

過去記事の #種類ごとにManyToOne のパートで紹介した方法で、あらかじめ Matter エンティティに workers だけでなく employeesfreelancers も持たせておきましょう。

そうすれば、以下のように普通にJOINすることができます👍

$filteredWorkers = $this->createQueryBuilder('m')
    ->leftJoin('m.employees', 'e')
    ->andWhere('e.salary > :salary')
    ->setParameter('salary', 400000)
    ->getQuery()
    ->getResult()
;

正解2:DQLの INSTANCE OF を使う

何かの事情で派生エンティティとのリレーションシップがない場合には、DQLの INSTANCE OF を使ってWHERE句の中で絞り込むこともできます👍

$filteredWorkers = $this->createQueryBuilder('m')
    ->leftJoin('m.workers', 'w')
    ->andWhere('w INSTANCE OF :employeeClass')
    ->andWhere('w.salary > :salary')
    ->setParameter('employeeClass', Employee::class)
    ->setParameter('salary', 400000)
    ->getQuery()
    ->getResult()
;

まとめ

GitHubで編集を提案

Discussion