路由模型绑定 #
一、概述 #
1.1 什么是路由模型绑定 #
路由模型绑定允许你自动将路由参数解析为对应的Eloquent模型实例,无需手动查询数据库。
text
传统方式
┌─────────────┐
│ /users/1 │
└──────┬──────┘
│
▼
┌─────────────┐
│ 控制器方法 │
│ $id = 1 │
└──────┬──────┘
│
▼
┌─────────────┐
│ 手动查询 │
│ User::find($id)│
└──────┬──────┘
│
▼
┌─────────────┐
│ 返回用户 │
└─────────────┘
模型绑定方式
┌─────────────┐
│ /users/1 │
└──────┬──────┘
│
▼
┌─────────────┐
│ 自动解析 │
│ User模型 │
└──────┬──────┘
│
▼
┌─────────────┐
│ 控制器方法 │
│ $user = User实例│
└─────────────┘
1.2 绑定类型 #
| 类型 | 说明 |
|---|---|
| 隐式绑定 | 自动根据类型提示绑定 |
| 显式绑定 | 手动定义绑定规则 |
二、隐式绑定 #
2.1 基本用法 #
php
use App\Models\User;
use Illuminate\Support\Facades\Route;
Route::get('/users/{user}', function (User $user) {
return $user;
});
访问 /users/1 时,Laravel自动查询 id = 1 的用户。
2.2 在控制器中使用 #
php
// 路由定义
Route::get('/users/{user}', [UserController::class, 'show']);
// 控制器
class UserController extends Controller
{
public function show(User $user)
{
return view('users.show', compact('user'));
}
}
2.3 自定义键名 #
默认使用 id 字段,可以指定其他字段:
php
Route::get('/users/{user:slug}', function (User $user) {
return $user;
});
访问 /users/john-doe 时,使用 slug 字段查询。
2.4 自定义键名与参数名不同 #
php
// 参数名: user_slug
// 键名: slug
Route::get('/users/{user_slug}', function (User $user) {
return $user;
})->scopeBindings();
三、显式绑定 #
3.1 定义显式绑定 #
在 RouteServiceProvider 中定义:
php
// app/Providers/RouteServiceProvider.php
public function boot()
{
parent::boot();
Route::bind('user', function ($value) {
return User::where('slug', $value)->firstOrFail();
});
}
3.2 使用显式绑定 #
php
Route::get('/users/{user}', function (User $user) {
return $user;
});
3.3 绑定到模型 #
php
public function boot()
{
parent::boot();
Route::model('user', User::class);
}
四、软删除模型 #
4.1 包含软删除 #
默认情况下,软删除的模型不会被解析:
php
// 只返回未软删除的模型
Route::get('/users/{user}', function (User $user) {
return $user;
});
要包含软删除的模型:
php
Route::get('/users/{user}', function (User $user) {
return $user;
})->withTrashed();
4.2 仅软删除 #
php
Route::get('/users/{user}', function (User $user) {
return $user;
})->onlyTrashed();
五、自定义异常 #
5.1 自定义未找到异常 #
php
use App\Models\User;
use Illuminate\Support\Facades\Route;
Route::get('/users/{user}', function (User $user) {
return $user;
})->missing(function () {
return redirect()->route('users.index');
});
5.2 全局自定义 #
php
// app/Providers/RouteServiceProvider.php
public function boot()
{
parent::boot();
Route::bind('user', function ($value) {
return User::where('slug', $value)->first() ?? abort(404);
});
}
六、嵌套绑定 #
6.1 父子关系绑定 #
php
use App\Models\Post;
use App\Models\User;
Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
return $post;
})->scopeBindings();
6.2 作用域绑定 #
确保子模型属于父模型:
php
// 启用作用域绑定
Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
return $post;
})->scopeBindings();
// 或在路由组中启用
Route::scopeBindings()->group(function () {
Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
return $post;
});
});
6.3 嵌套绑定示例 #
php
// models
class User extends Model
{
public function posts()
{
return $this->hasMany(Post::class);
}
}
class Post extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
// 路由
Route::scopeBindings()->group(function () {
Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
// $post 自动验证属于 $user
return $post;
});
});
七、枚举绑定 #
7.1 枚举类定义 #
php
enum Category: string
{
case Fruits = 'fruits';
case People = 'people';
}
7.2 路由中使用枚举 #
php
use App\Enums\Category;
Route::get('/categories/{category}', function (Category $category) {
return $category->value;
});
访问 /categories/fruits 返回 fruits。
八、绑定接口 #
8.1 定义接口 #
php
interface OrderRepositoryInterface
{
public function find($id);
}
8.2 实现接口 #
php
class CacheOrderRepository implements OrderRepositoryInterface
{
public function find($id)
{
return Cache::get("order.{$id}");
}
}
8.3 绑定接口 #
php
// app/Providers/RouteServiceProvider.php
public function boot()
{
parent::boot();
Route::bind('order', function ($id) {
return app(OrderRepositoryInterface::class)->find($id);
});
}
九、自定义解析逻辑 #
9.1 使用resolve方法 #
php
Route::bind('user', function ($value) {
// 自定义解析逻辑
if (is_numeric($value)) {
return User::findOrFail($value);
}
return User::where('slug', $value)->firstOrFail();
});
9.2 带缓存的解析 #
php
Route::bind('user', function ($value) {
return Cache::remember("user.{$value}", 3600, function () use ($value) {
return User::where('slug', $value)->firstOrFail();
});
});
9.3 多字段解析 #
php
Route::bind('user', function ($value) {
return User::where('id', $value)
->orWhere('slug', $value)
->orWhere('email', $value)
->firstOrFail();
});
十、性能优化 #
10.1 预加载关联 #
php
Route::get('/users/{user}', function (User $user) {
$user->load('posts', 'comments');
return $user;
});
10.2 缓存模型 #
php
Route::bind('user', function ($value) {
return Cache::remember("user.{$value}", 60, function () use ($value) {
return User::findOrFail($value);
});
});
10.3 选择性字段 #
php
Route::get('/users/{user}', function (User $user) {
return $user->only(['id', 'name', 'email']);
});
十一、实战示例 #
11.1 博客文章路由 #
php
// 路由定义
Route::get('/posts/{post:slug}', [PostController::class, 'show']);
// 控制器
public function show(Post $post)
{
$post->load(['author', 'comments.user', 'tags']);
return view('posts.show', compact('post'));
}
11.2 用户资料路由 #
php
// 使用用户名访问
Route::get('/@{user:username}', [ProfileController::class, 'show']);
// 控制器
public function show(User $user)
{
return view('profiles.show', compact('user'));
}
// 访问 /@john
11.3 多语言路由 #
php
// 语言枚举
enum Language: string
{
case En = 'en';
case Zh = 'zh';
case Ja = 'ja';
}
// 路由
Route::get('/{language}/posts/{post}', function (Language $language, Post $post) {
app()->setLocale($language->value);
return view('posts.show', compact('post'));
});
11.4 API资源路由 #
php
Route::apiResource('users.posts', PostController::class)
->scoped(['post' => 'slug']);
// GET /users/1/posts/my-first-post
// 自动验证 post 属于 user
十二、常见问题 #
12.1 参数名与变量名不匹配 #
php
// 错误:参数名是 user_id
Route::get('/users/{user_id}', function (User $user) {
return $user;
});
// 正确:参数名与变量名一致
Route::get('/users/{user}', function (User $user) {
return $user;
});
12.2 自定义键名注意事项 #
php
// 正确:明确指定键名
Route::get('/users/{user:slug}', function (User $user) {
return $user;
});
// 错误:模型没有 slug 字段
12.3 作用域绑定失败 #
php
// 确保模型定义了关联关系
class User extends Model
{
public function posts()
{
return $this->hasMany(Post::class);
}
}
// 确保启用作用域绑定
Route::scopeBindings()->group(function () {
Route::get('/users/{user}/posts/{post}', ...);
});
十三、总结 #
13.1 核心要点 #
| 要点 | 说明 |
|---|---|
| 隐式绑定 | 类型提示自动解析 |
| 显式绑定 | 手动定义解析规则 |
| 自定义键 | 指定查询字段 |
| 作用域绑定 | 验证父子关系 |
| 软删除 | withTrashed/onlyTrashed |
| 枚举绑定 | PHP 8.1+ 枚举支持 |
13.2 最佳实践 #
- 使用语义化的参数名
- 合理使用作用域绑定
- 对频繁访问的模型使用缓存
- 预加载关联关系
13.3 下一步 #
掌握了路由模型绑定后,让我们继续学习 控制器基础,深入了解Laravel控制器!
最后更新:2026-03-28