【Laravel】URLのパラメータをモデル結合して効率的に使用する【カラム指定も】

えび

LaravelのルーティングにはパラメータにIDを渡すとそれに紐づくモデルを自動で取得して渡してくれる便利機能がある
今回はそれのカスタマイズのメモ

ルートモデル結合とはなんぞ

今回フォーカスを当てるのは「ルートモデル結合

これがとても便利で
ルートモデル結合をしたルーティングの場合、
パラメータに対象のモデルIDを渡すだけで紐づくモデルインスタンスをルートに自動注入してくれる

実際書いてみるとこんな感じ

  • routes/web.php
use App\Models\User;

Route::get('/users/{user}', function (User $user) {
    return $user->email;
});

上記ルーティングを設定の上、例えば /users/10 にアクセスした場合、
usersテーブルID=10のレコードがあればそれを関数の引数に渡してくれて、
なければ404 Not Foundのエラーを吐いてくれる

パラメータで渡されたidを使用して対象のレコードを取得&存在チェックを行う従来の処理と比べるとかなりシンプルになる

  • web/routes.php
use App\Models\User;

// 従来の処理の場合
Route::get('/users/{id}', function (int $id) {
    $user = User::find($id);
    if (!$user) {
      abort(404);
    }

    return $user->email;
});

// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

// ルートモデル結合を活用した場合
Route::get('/users/{user}', function (User $user) {
    return $user->email;
});

ルートモデル結合の法則

/users/{user}{user}の部分を対象のモデル名の小文字にしてあげればOK

これだけでルーティング側がタイプヒントしてくれて、関数の中ではパラメータIDに対応する値と一致するIDを持つモデルインスタンスを自動的に挿入される

例えばUserモデルじゃなくてPostモデルを対象にしたい場合はこんな感じ
/posts/{post}

Controllerで使用する場合も同様

これまでの例ではroutes/web.php内で完結させてるけど
Controllerで使用する場合でも同じ

  • routes/web.php
use App\Http\Controllers\UserController;

Route::get('/user/{user}', [UserController::class, 'show']);
  • UserController
<?php

namespace App\Http\Controllers;

use App\Models\User;

class UserController extends Controller
{
  public function show(User $user)
  {
    return $user->email;
  }
}

URLの生成方法

従来のルーティングと同様にURLを生成すればOK

// urlを使用する場合
<a href="{{ url('/users/10')}}>リンク</a>
// routeを使用する場合
<a href="{{ route('users.show', ['user' => 10]) }}>リンク</a>
// モデル変数がある場合はそれをそのまま渡すでOK
<a href="{{ route('users.show', $user) }}>リンク</a>

IDのカラム以外でルートモデル結合するようにカスタマイズする

例えばポータルサイトのユーザープロフィール画面などで、
IDではなくユーザー名をパラメータにしたい場合

  • routes/web.php
use App\Http\Controllers\UserController;

Route::get('/user/{name}', [UserController::class, 'show']);

前述と同様、ルートモデル結合をしない場合はController内でいちいち取得しないといけない

  • UserController
<?php

namespace App\Http\Controllers;

use App\Models\User;

class UserController extends Controller
{
  public function show(string $name)
  {
    $user = User::where('name', $name)->first();
    if (!$user) {
      abort(404);
    }

    return $user->email;
  }

}

これをカスタマイズしてあげる

ルートパラメータ定義でカラムを指定

対象のルーティングだけで使用したいだけの場合はルート上で設定すればOK

  • routes/web.php
use App\Http\Controllers\UserController;

// パラメータに{モデル名:カラム名} で指定する
Route::get('/user/{user:name}', [UserController::class, 'show']);
  • UserController
<?php

namespace App\Http\Controllers;

use App\Models\User;

class UserController extends Controller
{
  public function show(User $user)
  {
    return $user->email;
  }

}

従来通り自動注入してくれる

そのモデルでは永続的にID以外のカラムをキーにしたい場合

対象のモデルに紐づくルーティングは全てカラムをID以外に変更したい場合、
EloquentのgetRouteKeyNameをオーバーライドする

  • app/Models/User.php
// ⭐️ 下記関数を追記
/**
 * モデルのルートキーの取得
 *
 * @return string
 */
public function getRouteKeyName()
{
    return 'name';
}

\ 案件のご依頼・ご相談はこちらから /