Laravelのリレーションの削除方法

結論

Eagerロードで読み込んだリレーションは、unset することで削除することができます。

やりたかったこと

背景なども記載しておきます。

レスポンスはjson形式を想定しています。
リレーションデータと一緒にEloquentのコレクションを取得し、取得したデータのリレーションデータを処理。そのままEloquentのコレクションを返した場合、リレーションデータも一緒に返ります。すでに、リレーションデータを処理したのでリレーションデータを返す必要はありませんでした。

コードで表すとこんな感じです。

Models/Post.php
class Post extends Model
{
    public function comments(): HasMany
    {
        return $this->hasMany(Comment::class);
    }
}
PostController.php
// リレーションデータと一緒にEloquentのコレクションを取得
$posts = Post::with('comments')->get();

foreach ($posts as $post) {
    // 取得したデータのリレーションデータを処理
    $comments_count = $post->comments->count();
    $post->comments_count = $comments_count;
    // リレーションデータの削除
    unset($post->comments);
}

return $posts;

nullを代入ではリレーションは削除することができませんでした。

Laravelの実装の確認

unsetすることでリレーションデータも削除されます。

framework/src/Illuminate/Database/Eloquent/Model.php
public function __unset($key)
{
    $this->offsetUnset($key);
}

public function offsetUnset($offset): void
{
    unset($this->attributes[$offset], $this->relations[$offset]);
}

__unset 関数
https://github.com/laravel/framework/blob/v11.27.2/src/Illuminate/Database/Eloquent/Model.php#L2340-L2343

offsetUnset 関数
https://github.com/laravel/framework/blob/v11.27.2/src/Illuminate/Database/Eloquent/Model.php#L2318-L2321

$post->offsetUnset('comments');でも同じ結果になりますね。

なぜnull代入ではだめだったのか

実装を確認します。

framework/src/Illuminate/Database/Eloquent/Model.php
public function __set($key, $value)
{
    $this->setAttribute($key, $value);
}

https://github.com/laravel/framework/blob/v11.27.2/src/Illuminate/Database/Eloquent/Model.php#L2269-L2272

nullを代入した場合、__set関数が実行されます。内部的にはsetAttribute関数が実行されるだけなので、relationsが消えないためでした。