CASE文使うな委員会召喚の儀
実現したいこと
あるスーパータイプのテーブルに対して複数のサブタイプテーブルを定義するとします。
ここではスーパータイプ fruits に対して、サブタイプとして apples と bananas を定義します。
サブタイプが生成されるたびに fruits にレコードが生成され、1:1で紐づきます。
この時、fruits を条件に従って取得し、サブタイプごとに特有のカラムを対象として並び替えたいといった要件が存在するとします。
並び替えの条件として、サブタイプが apples の時は"fell_at"を、bananas の時は"froze_at"を基準として昇順/降順を柔軟に操作できるようにします。
ここで、そのスーパータイプがどのサブタイプにレコードを持っているかというのを、
スーパータイプ側で fruit_type を持って管理します。
並び替える際にサブタイプごとに異なるカラムを指定したい、というのはどうも Eloquent で気軽に達成できる目標ではありませんでした。
もっとスマートなやり方があればいいのになぁ
設計
entity fruits { + id [PK] -- fruit_type : tinyint deleted_at : datetime } entity apples { + id [PK] -- # fruit_id [FK(fruits, id)] fell_at : datetime deleted_at : datetime } entity bananas { + id [PK] -- # fruit_id [FK(fruits, id)] froze_at: datetime deleted_at : datetime }
実装
脳死で orderByRaw に CASE 文ぶっこむ。
(MySQL が statement って言ってたし文で合ってるよね?)
// いわゆる列挙体がPHPに存在しないので自分で Enum クラスを定義してるだけです class FruitType extends Enum { const APPLES = 1; const BANANAS = 2; }
class Fruit extends Model
{
public function scopeOrderByActedOn($query, string $orderDirection = 'DESC')
{
return $query
->orderByRaw('
CASE (
SELECT f.fruit_type
FROM fruits f
WHERE f.deleted_at IS NULL
)
WHEN '.FruitType::APPLES.' THEN (
-- apples
SELECT a.fell_at
FROM apples a
WHERE a.fruit_id = fruits.id
AND a.deleted_at IS NULL
)
WHEN '.FruitType::BANANAS.' THEN (
-- bananas
SELECT b.froze_at
FROM bananas b
WHERE b.fruit_id = fruits.id
AND b.deleted_at IS NULL
)
END '.$orderDirection);
}
}