Laravel 应用的入口:路由系列之参数、命名和分组篇


路由参数

必选参数

有时我们需要在路由中获取 URI 请求参数。例如,如果要从 URL 中获取用户ID,需要通过如下方式定义路由参数:

Route::get('user/{id}', function ($id) {
    return 'User ' . $id;
});

这样我们在浏览器中访问 http://blog.dev/user/1,就会得到以下输出:

User 1

可以根据需要在路由中定义多个路由参数:

Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) {
    return $postId . '-' . $commentId;
});

根据上面的示例,路由参数需要通过花括号 {} 进行包裹并且是拼音字母,这些参数在路由被执行时会被传递到路由的闭包。路由参数名称不能包含 - 字符,如果需要的话可以使用 _ 替代,比如如果某个路由参数定义成 {post-id} 则访问路由会报错,应该修改成 {post_id} 才行。路由参数被注入到路由回调/控制器取决于它们的顺序,与回调/控制器名称无关。

可选参数

有必选参数就有可选参数,这可以通过在参数名后加一个 ? 标记来实现,这种情况下需要给相应的变量指定默认值,当对应的路由参数为空时,使用默认值:

Route::get('user/{name?}', function ($name = null) {
    return $name;
});

Route::get('user/{name?}', function ($name = 'John') {
    return $name;
});

这时如果定义的路由是下面这个的话,访问 http://blog.dev/user 会返回 John

正则约束

可以通过路由实例上的 where 方法来约束路由参数的格式。where 方法接收参数名和一个正则表达式来定义该参数如何被约束:

Route::get('user/{name}', function ($name) {
    // name 必须是字母且不能为空
})->where('name', '[A-Za-z]+');

Route::get('user/{id}', function ($id) {
    // id 必须是数字
})->where('id', '[0-9]+');

Route::get('user/{id}/{name}', function ($id, $name) {
    // 同时指定 id 和 name 的数据格式
})->where(['id' => '[0-9]+', 'name' => '[a-z]+']);

使用正则约束还有一个好处就是避免了 user/{id}user/{name} 的混淆。

全局约束

如果想要路由参数在全局范围内被给定正则表达式约束,可以使用 pattern 方法。需要在 RouteServiceProvider 类的 boot 方法中定义这种约束模式:

/**
 * 定义路由模型绑定,模式过滤器等
 *
 * @param  \Illuminate\Routing\Router  $router
 * @return void
 * @translator  https://laravel.geekai.co
 */
public function boot()
{
    Route::pattern('id', '[0-9]+');
    parent::boot();
}

一旦模式被定义,将会自动应用到所有包含该参数名的路由中:

Route::get('user/{id}', function ($id) {
    // 只有当 {id} 是数字时才会被调用
});

除此之外,该模式还会被应用到诸如下面这些路由参数上:

Route::get('post/{id}', function ($id) {
    // 只有当 {id} 是数字时才会被调用
});

Route::get(`product/{id}', function ($id) {
    // 只有当 {id} 是数字时才会被调用
});

很显然这种方式让代码更简洁,也为我们实现同一参数统一约束带来了方便。

命名路由

命名路由为生成 URL 或重定向提供了方便,实现起来也很简单,在路由定义之后使用 name 方法链的方式来定义该路由的名称:

Route::get('user/profile', function () {
    // 通过路由名称生成 URL
    return 'my url: ' . route('profile');
})->name('profile');

还可以为控制器动作指定路由名称:

Route::get('user/profile', 'UserController@showProfile')->name('profile');

这样我们就可以通过以下方式定义重定向:

Route::get('redirect', function() {
    // 通过路由名称进行重定向
    return redirect()->route('profile');
});

为命名路由生成 URL

正如上面代码所展示的,为给定路由分配名称之后,就可以通过辅助函数 route 为该命名路由生成 URL:

$url = route('profile');

如果命名路由定义了参数,可以将该参数作为第二个参数传递给 route 函数。给定的路由参数将会自动插入到 URL 中:

Route::get('user/{id}/profile', function ($id) {
    $url = route('profile', ['id' => $id]);
    return $url;
})->name('profile');

这样,当我们访问 http://blog.dev/user/123/profile 页面输出内容也是 http://blog.dev/user/123/profile

检查当前路由

如果你想要判断当前请求是否被路由到给定命名路由,可以使用 Route 实例上的 named 方法,例如,你可以从路由中间件中检查当前路由名称:

/**
 * 处理输入请求
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Closure  $next
 * @return mixed
 */
public function handle($request, Closure $next)
{
    if ($request->route()->named('profile')) {
        //
    }

    return $next($request);
}

路由分组

路由分组的目的是让我们在多个路由中共享相同的路由属性,比如中间件和命名空间等,这样的话我们定义了大量的路由时就不必为每一个路由单独定义属性。共享属性以数组的形式作为第一个参数被传递给 Route::group 方法。

中间件

要给某个路由分组中定义的所有路由分配中间件,可以在定义分组之前使用 middleware 方法。中间件将会按照数组中定义的顺序依次执行:

Route::middleware(['first', 'second'])->group(function () {
    Route::get('/', function () {
        // Uses first & second Middleware
    });

    Route::get('user/profile', function () {
        // Uses first & second Middleware
    });
});

关于中间件的使用我们在后面单独讲中间件时再进行示例演示,这里我们先了解这样使用就行。

命名空间

路由分组另一个通用的例子是使用 namespace 方法分配同一个 PHP 命名空间给该分组下的多个控制器:

Route::namespace('Admin')->group(function () {
    // Controllers Within The "App\Http\Controllers\Admin" Namespace
});

默认情况下,RouteServiceProvider 在一个命名空间分组下引入所有路由文件,并指定所有控制器类所在的默认命名空间是 App\Http\Controllers,因此,我们在定义控制器的时候只需要指定命名空间 App\Http\Controllers 之后的部分即可。

关于命名空间后面我们单独讲控制器的时候还会再详细演示,这里先了解用法即可。

子域名路由

路由分组还可以被用于处理子域名路由,子域名可以像 URI 一样被分配给路由参数,从而允许捕获子域名的部分用于路由或者控制器,子域名可以在定义分组之前调用 domain 方法来指定:

Route::domain('{account}.blog.dev')->group(function () {
    Route::get('user/{id}', function ($account, $id) {
        return 'This is ' . $account . ' page of User ' . $id;
    });
});

比如我们设置会员子域名为 account.blog.dev,那么就可以通过 http://account.blog.dev/user/1 访问用户ID为 1 的会员信息了:

This is account page of User 1

路由前缀

prefix 方法可以用来为分组中每个路由添加一个给定 URI 前缀,例如,你可以为分组中所有路由 URI 添加 admin 前缀 :

Route::prefix('admin')->group(function () {
    Route::get('users', function () {
        // Matches The "/admin/users" URL
    });
});

这样我们就可以通过 http://blog.dev/admin/users 访问路由了。


Vote Vote Cancel Collect Collect Cancel

<< 上一篇: Laravel 应用的入口:路由系列之基础入门篇

>> 下一篇: Laravel 应用的入口:路由系列之路由模型绑定篇