📉

[Laravel] Eloquent(エロクアント)のselect文でAS idってエイリアスを付けたら一生idが0になってハマった話

2023/07/22に公開

SELECTとasとid:0の不思議な挙動

以下のようなテーブルがあったとします。

ここでAPIを叩くと、以下のようにusersテーブルのlogin_idがidという名前で返ってくるようにしたいとします。

login_idはlogin_idで返したほうが良いと思いますが、それは一旦おいておいて...

↓テーブルのデータ

↓期待値

{
    "id" : "the2ndloop"
}

そこで、コントローラーに↓こういう感じでコードを書きました。


use App\Models\User;

class UserController extends Controller
{
    public function show($id){
        $user = User::select("login_id AS id")->where("id", $id)->first();

        return response()->json($user);
    }
}

ファットコントローラーになりかねないことをしていますが、一旦無視してください。

ちなみにselect文内でAS hogeとすると、取得した後のカラム名を変更できるのは周知のとおりです。

このAPIを実際に叩いてみると...

クエリビルダでテーブルをjoinした時にidが上書きされる問題は、Laravelでよく起こしてしまうのですが、これは初めてでした。

なので基本的にJOINする必要がある場合は素のSQLを書くようにしている。

エイリアス名変えてみる

エイリアス名を別のものにしてみる。
今思うとキャメルケース気持ち悪いな...


use App\Models\User;

class UserController extends Controller
{
    public function show($id){
        $user = User::select("login_id AS loginId")->where("id", $id)->first();

        return response()->json($user);
    }
}

ちゃんと動いている...

modelを使わない(DBファサード)

use Illuminate\Support\Facades\DB;

class UserController extends Controller
{
    public function show($id){
        $user = DB::table('users')->select("login_id AS id")->where("id", $id)->first();

        return response()->json($user);
    }
}

DBファサードを使ったmodelを使わないクエリビルダ。

ちゃんと動いている...

個人的な結論

結局、SQL素書きがなんやかんや言って一番良い。
SQLインジェクションには気をつけないといけないけども、それも含めて勉強になる。

ちょっと真面目な話

実は全く同じ話が、本家大元のGitHubのissueに上がってました。

https://github.com/laravel/framework/issues/26687

Laravel的には...

idって基本integerだよね?
じゃあ、idって名前のものはとりあえず全部integerに型変換するね。
気が効くでしょ?

というノリ(超意訳)らしいです。

まあ、そもそもmodelというかEloquentはDBを扱いやすくする目的であるわけで、いろいろ影でやってくれてるみたいですね。

個人的にはあまりブラックボックス化やマジックメソッドみたいなのは好みではないですが...

なるべく、エロクアントモデルを使ったクエリビルダより、DBファサードを使ったクエリビルダの方が何かとバグを起こしにくいかもしれないですね。

処理も速いらしいし。

エロクアントの良いところ

エロクアントってのはLaravelが提供してくれてるORM関連機能の総称っていう理解をしているのですが、エロクアントモデルを使ってUser::select('*')->get();みたいに普通にクエリビルダも使えるというのは便利といえば便利です。

いちいち、DB::table('users)って書くのめんどくさいですし、ハードコーディングになってしまいがちなので。

テーブル名をあとから変えることは、そうそうないと思いますので、実際はそんなにメリットはないかもですが...

定数にテーブル名を入れておいて、それを使ってDB::table(定数名)みたいな風に使っても良いかもしれません。

最終的な結論

でもやっぱりDBファサードかSQLを素書きしましょう!

素書きする場合はSQLインジェクションには気をつけないとですが...

以上です。

Discussion