[laravel8] ルート・モデル・バインディング
ルート・モデル・バインディングとは?
いきなり例から見てみます。
routes/web.php
// /posts/(記事ID) 記事IDが$idになる
Route::get('/posts/{post}', function ($id) {
// 記事IDが$idの記事を取得
$post = Post::findOrFail($id);
// $post(記事)
return view('post', [
'post' => $post
]);
});
このルーティングは
の記事で説明したように、URLから記事IDを取得してからその記事IDを使い記事(Post)モデルをfindOrFailメソッドを使って探しています。
ルート・モデル・バインディングとは、記事IDをURLから取得するのでなく、直接記事(Post)モデルをURLから取得する機能のことです。
具体例
ではどのように使うのか見てみましょう。
routes/web.php
// /posts/(記事ID) 記事モデルが$postになる
Route::get('/posts/{post}', function (Post $post) {
// $post(記事)
return view('post', [
'post' => $post
]);
});
routeのコールバック関数のパラメータに$idではなく、直接
Post $post
と、Postモデルのインスタンスが入ってきています。
http://localhost:8000/posts/2
のアクセスが来たら、laravelは記事IDが2の記事(Post)モデルのインスタンスを$postとして扱うことができます。
注意点
routeのワイルドカードの名前が変数の名前と一致しなければなりません。
Route::get('/posts/{post}', function (Post $post) {...}
もし$post変数の名前を$fooに変えたりすると動きません。
Route::get('/posts/{post}', function (Post $foo) {...} // 動きません
具体的には
Route::get('/posts/{post}', function (Post $post) {...}
このようにPostとタイプヒントされた場合、Laravelは変数の名前($post)とワイルドカードの名前({post})が一致するか調べます。もし一致する場合はLaravelは『URLにあるIDのPostモデルを探しているのかな』と仮定し、そのIDのPostモデルインスタンスを自動的に返してくれます。
id以外のカラムを使う場合
上記の説明ではテーブルのidカラムを使用していましたが(デフォルト)、もちろんidカラム以外を使うこともできます。制限はカラムがuniqueである必要があるだけです。
の記事で使ったcreate_posts_tableのファイルを編集してslugカラムを追加します。
database/migrations/2021_05_07_111050_create_posts_table.php
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('slug')->unique(); // uniqueにする必要があります。
$table->string('title');
$table->text('excerpt');
$table->text('body');
$table->timestamps();
$table->timestamp('published_at')->nullable();
});
}
$php artisan migrate:fresh
migrate freshコマンドでslugを追加します。テーブルのデータは全て消えるので。コピーしてslugも追加します。
方法1
routes/web.php
Route::get('/posts/{post:slug}', function (Post $post) {
// $post(記事)
return view('post', [
'post' => $post
]);
});
ワイルドカードを設定するところで
{post:slug}
とslugカラムを使用すると指定しています。
ここで内部的にはどのようになってるかと言うと
Post::where('slug', $post)->firstOrFail();
が実行されてPostモデルを探しています。
方法2
Postモデルを変更し、slugを使うことを明示します。
app/Models/Post.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
protected $guarded = [];
public function getRouteKeyName()
{
return 'slug';
}
}
getRouteKeyNameメソッドを追加し、 slugをリターンして明示してます。
この場合はrouteのワイルドカードでslugを明示する必要はありませんので以下のようになります。
routes/web.php
// /posts/(記事ID)
Route::get('/posts/{post}', function (Post $post) {
// $post(記事)
return view('post', [
'post' => $post
]);
});
最後に
この段階でのソースコードはこちら
この記事が気に入ったらサポートをしてみませんか?