Laravel8 表結合時に別名をつけるとソフトデリート機能が思い通りに動作しない

ソフトデリート設定済みのitemsテーブルとitem_childrenテーブルが存在し、こんなリレーションを設定したとして

items               item_children
    id -----┐        id
            └------- item_id
                     name

items.id=1に紐づくitem_childrenのレコードを抽出し、item_children.nameを列挙する場合
(実際にはこんなコードは書かないけど、例ということで。)
以下のようにすれば取得することができる。

$items = Item::from('items')
    ->select(
        'item_children.name',
    )
    ->join('item_children', 'items.id', '=', 'item_children.item_id')
    ->where('items.id', '=', 1)
    ->whereNull('item_children.deleted_at')  // 自力でやらないとダメ
    ->get();

結合した表のソフトデリートレコードを除く処理は自力でやらないとダメ

しかし、列名をつけると

$items = Item::from('items as i')
    ->select(
        'ic.name',
    )
    ->join('item_children as ic', 'i.id', '=', 'ic.item_id')
    ->where('i.id', '=', 1)
    ->get();
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'items.deleted_at'

というエラーが発生してしまう。
発行されたSQLは以下のようになっており

select
    `ic`.`name`
from
    `items` as `i`
    inner join
        `item_children` as `ic`
    on  `i`.`id` = `ic`.`item_id`
where
    `i`.`id` = ?
and `items`.`deleted_at` is null

ソフトデリート済みのレコードを除く”items.deleted_at is null”が別名ではなく、テーブル名になっているからである。

これを回避するには

$items = Item::from('items as i')
    ->select(
        'ic.name',
    )
    ->join('item_children as ic', 'i.id', '=', 'ic.item_id')
    ->where('i.id', '=', 1)
    ->withTrashed('items')           // 追加
    ->whereNull('i.deleted_at')      // 自前でソフトデリート済みレコードを除く
    ->whereNull('ic.deleted_at')     // 自前でソフトデリート済みレコードを除く
    ->get();

->withTrashed(‘items’)を記述して、items.deleted_at is nullを展開されるのを防ぎ、とりあえずソフトデリート済みのレコードも対象として、自前でwhereNull()によりソフトデリート済みレコードを除く処理にする必要がある。
結論は、リレーションのある構成ではソフトデリート機能は使わない方が良いと思う。
他にスマートなやり方や正しい方法があれば教えていただきたい^^

返信を残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です