Laravel 应用的入口:路由系列之路由模型绑定篇
路由模型绑定
注入模型 ID 到路由或控制器动作时,通常需要查询数据库才能获取相应的模型数据。Laravel 路由模型绑定让注入模型实例到路由变得简单,例如,你可以将匹配给定 ID 的整个 User
类实例注入到路由中,而不只是注入用户 ID。
隐式绑定
Laravel 会自动解析定义在路由或控制器动作(变量名匹配路由片段)中的 Eloquent 模型类型声明,例如(我们将这个路由定义在 routes/api.php
文件中):
Route::get('users/{user}', function (App\User $user) {
dd($user);
});
在这个例子中,由于类型声明了 Eloquent 模型 App\User
,对应的变量名 $user
会匹配路由片段中的 {user}
,这样,Laravel 会自动注入与请求 URI 中传入的 ID 对应的用户模型实例。
在演示本功能之前,我们需要先创建数据表,由于我是在 Valet 开发环境中开发,需要自己创建数据库,我们将数据库命名为 valet
,本地的数据库用户名为 root
,密码为空,对应地,修改 .env
文件配置如下:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=valet
DB_USERNAME=root
DB_PASSWORD=
具体配置值以你自己的开发环境设置为准。我们将基于 Laravel 强大的数据库迁移功能创建 users
表,关于数据库迁移后面在数据库部分会详细讨论,这里我们通过以下命令来生成 users
表即可:
php artisan migrate
进入数据库可以看到该表已经生成:

这时,users
数据表还没有任何记录,如果数据库中找不到对应的模型实例,会自动生成 HTTP 404 响应,提示页面不存在,所以我们需要在这张表中插入一条记录,这里我们基于 Laravel 强大的数据库填充器来快速完成数据填充功能,首先通过如下命令生成 users
对应的数据表填充器:
php artisan make:seeder UsersTableSeeder
该命令会在 database/seeds 目录下生成一个 UsersTableSeeder
文件,编辑该文件内容如下:

然后编辑同目录下的 DatabaseSeeder.php
文件如下(取消调用数据表填充器前的注释):

最后执行 php artisan db:seed
即可插入对应数据到 users
表了,这样我们在浏览器中再次访问 http://blog.dev/api/users/1
的时候就会显示 User
模型数据了:

接下来,你就可以在应用代码中直接拿 $user
模型去做你想做的事情了,而不需要自己去数据库查询,从而提高了开发的效率。
自定义键名
如果你想要在隐式模型绑定中使用数据表的其它字段而不是 id
字段,可以重写 Eloquent 模型类的 getRouteKeyName
方法,以 User
模型为例,可以在该模型类中添加这个方法 :
/**
* Get the route key for the model.
*
* @return string
*/
public function getRouteKeyName()
{
return 'name';
}
这样我们就可以通过 http://blog.dev/api/users/jroJoGP71W
访问同一个模型实例了。这里需要注意的点是如果该字段不是唯一键,则会返回结果集的第一条记录,对应的底层实现在这里:

显式绑定
有隐式绑定,就有显式绑定。要注册显式绑定,可以使用路由器的 model
方法来为给定参数指定绑定类。你需要在 RouteServiceProvider
类的 boot
方法中定义显式模型绑定:
public function boot()
{
parent::boot();
Route::model('user_model', \App\User::class);
}
接下来,在 routes/api.php
中定义一个包含 {user}
参数的路由:
$router->get('profile/{user_model}', function(App\User $user) {
dd($user);
});
由于我们已经绑定 {user_model}
参数到 App\User
模型,User
实例会被注入到该路由。因此,如果请求 URL 是 http://blog.dev/api/profile/1
,就会注入一个用户 ID 为 1
的 User
实例。
如果匹配的模型实例在数据库不存在,会自动生成并返回 HTTP 404 响应。
自定义解析逻辑
如果你想要使用自定义的解析逻辑,可以在 RouteServiceProvider
类的 boot
方法中使用 Route::bind
方法,传递到 bind
方法的闭包会获取到 URI 请求参数中的值,并且返回你想要在该路由中注入的类实例:
public function boot()
{
parent::boot();
Route::bind('user', function($value) {
return \App\User::where('name', $value)->first();
});
}
有了这些方法,基本上可以满足你对路由模型绑定的各种需求了。
访问当前路由
你可以使用 Route
门面上的 current
、currentRouteName
和 currentRouteAction
方法来访问处理当前输入请求的路由信息:
// 获取当前路由实例
$route = Route::current();
// 获取当前路由名称
$name = Route::currentRouteName();
// 获取当前路由action属性
$action = Route::currentRouteAction();
参考API文档了解路由门面底层类以及Route实例的更多可用方法。
路由缓存
注:路由缓存不会作用于基于闭包的路由。要使用路由缓存,必须将闭包路由转化为控制器路由。
如果你的应用完全基于控制器路由,可以使用 Laravel 的路由缓存,使用路由缓存将会极大降低注册所有应用路由所花费的时间开销,在某些案例中,路由注册速度甚至能提高100倍!想要生成路由缓存,只需执行 Artisan 命令 route:cache
:
php artisan route:cache
运行完成后,每次请求都会从缓存中读取路由,所以如果你添加了新的路由需要重新生成路由缓存。因此,只有在项目部署阶段才需要运行 route:cache
命令,本地开发环境完全无此必要。
想要移除缓存路由文件,使用 route:clear
命令即可:
php artisan route:clear
关于控制器路由,我们将在控制器部分详细探讨。
36 Comments
您好。这个 bindings 中间件,在web.php 里面不用绑定吗,在api.php里面就需要自己手动绑定吗?
你们有没有遇到这个问题 显式绑定 public function boot() { parent::boot(); Route::model('user_model', App\User::class); }
路由里面 $router->get('profile/{user_model}', function(App\User $user) { dd($user); }); 如果路由里面{user_model} 的user_model 和Route::model('user_model', App\User::class)里的user_model同名就不行 只要两个名字不一样就可以 我真是见鬼了
bind()之后怎么测试的?
Route::bind('user', function($value) { return \App\User::where('name', $value)->find(2); }); 给{user}参数绑定, Route::get('users/{user}',function (App\User $user){ dd($user); }); http://blog.test:8080/api/users/BanUN2TheM
会返回第二个name是Ban...的数据
我遇到了和你同样的问题,不知道你解决了没有
数据库迁移有稍微报错,SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 1000 bytes (SQL:alter table
users
add uniqueusers_email_unique
(email
)),这个对应文件里面的$table->string('email')->unique();需要修改嘛?数据库记录中有邮箱地址超过1000bytes?
没有呀,表都是空的,没记录,这个只是迁入表还没插入数据吧?
那就是在说指定的索引件名称太长了