🎻
[Symfony][Doctrine] STIのdiscriminatorを指定してWHERE句を書きたい
やりたいこと
- 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
だけでなく employees
と freelancers
も持たせておきましょう。
そうすれば、以下のように普通にJOINすることができます👍
$filteredWorkers = $this->createQueryBuilder('m')
->leftJoin('m.employees', 'e')
->andWhere('e.salary > :salary')
->setParameter('salary', 400000)
->getQuery()
->getResult()
;
INSTANCE OF
を使う
正解2:DQLの 何かの事情で派生エンティティとのリレーションシップがない場合には、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()
;
まとめ
- Doctrineで、Single Table Inheritanceを使っているエンティティのdiscriminator(識別子)を指定してWHERE句を書きたい場合は、以下のどちらかの方法で解決できる
- 派生エンティティごとにリレーションシップを張っておいて、派生エンティティとJOINする
-
DQLの
INSTANCE OF
を使ってWHERE句の中で絞り込む
Discussion