> For the complete documentation index, see [llms.txt](https://tips.laradoc.cn/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tips.laradoc.cn/routing.md).

# 路由

## 分组中的分组

在路由中，你可以在分组中创建分组，来实现仅仅为父分组中的某些路由分配中间件。

```php
Route::group(['prefix' => 'account', 'as' => 'account.'], function() {
    Route::get('login', 'AccountController@login');
    Route::get('register', 'AccountController@register');
    
    Route::group(['middleware' => 'auth'], function() {
        Route::get('edit', 'AccountController@edit');
    });
});
```

## 通配符子域名

你可以在分组中定义变量，来创建动态子域名分组，然后将这个变量传递给每一个子路由。

```php
Route::domain('{username}.workspace.com')->group(function () {
    Route::get('user/{id}', function ($username, $id) {
        //
    });
});
```

## routes 调用之后是什么

若你使用 [Laravel UI package](https://github.com/laravel/ui), 你可能想知道 `Auth::routes()` 定义之后真正的路由是什么?

查看 `/vendor/laravel/ui/src/AuthRouteMethods.php`.

```php
public function auth()
{
    return function ($options = []) {
        // Authentication Routes...
        $this->get('login', 'Auth\LoginController@showLoginForm')->name('login');
        $this->post('login', 'Auth\LoginController@login');
        $this->post('logout', 'Auth\LoginController@logout')->name('logout');
        // Registration Routes...
        if ($options['register'] ?? true) {
            $this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
            $this->post('register', 'Auth\RegisterController@register');
        }
        // Password Reset Routes...
        if ($options['reset'] ?? true) {
            $this->resetPassword();
        }
        // Password Confirmation Routes...
        if ($options['confirm'] ?? class_exists($this->prependGroupNamespace('Auth\ConfirmPasswordController'))) {
            $this->confirmPassword();
        }
        // Email Verification Routes...
        if ($options['verify'] ?? false) {
            $this->emailVerification();
        }
    };
}
```

默认调用:

```php
Auth::routes(); // no parameters
```

但是你可以提供参数来启用或禁用真正的路由

```php
Auth::routes([
    'login'    => true,
    'logout'   => true,
    'register' => true,
    'reset'    => true,  // for resetting passwords
    'confirm'  => false, // for additional password confirmations
    'verify'   => false, // for email verification
]);
```

[suggestion](https://github.com/LaravelDaily/laravel-tips/pull/57) 由 [MimisK13](https://github.com/MimisK13) 提供

## 路由模型绑定-你可以定义一个 Key

你可以像 `Route::get('api/users/{user}', function (App\User $user) { … }` 这样来进行路由模型绑定，但不仅仅是 ID 字段，如果你想让 {user} 是 username，你可以把它放在模型中：

```php
public function getRouteKeyName() {
    return 'username';
}
```

## 快速从路由导航到控制器

在 `Laravel 8` 之前，这件事情是可选的。在 `Laravel 8` 中这将成为路由的标准语法

不用这么写:

```php
Route::get('page', 'PageController@action');
```

你可以将控制器标识为 `class` 类名:

```php
Route::get('page', [\App\Http\Controllers\PageController::class, 'action']);
```

这样，你就可以在 `PhpStorm` 中点击 `PageController` 来跳转到控制器定义，而不是手动去搜索它

或者你想要让路由的定义更简洁，你可以在路由文件的开始提前引入控制器的类。

```php
use App\Http\Controllers\PageController;

// Then:
Route::get('page', [PageController::class, 'action']);
```

## 备选路由-当没有匹配到任何路由时

如果你想为未找到的路由指定其它逻辑，而不是直接显示 404 页面，你可以在路由文件的最后为其创建一个特殊的路由。

```php
Route::group(['middleware' => ['auth'], 'prefix' => 'admin', 'as' => 'admin.'], function () {
    Route::get('/home', 'HomeController@index');
    Route::resource('tasks', 'Admin\TasksController');
});

// Some more routes....
Route::fallback(function() {
    return 'Hm, why did you land here somehow?';
});
```

## 使用正则进行路由参数验证

我们可以在路由中使用 `where` 参数 来直接验证参数。一个典型的例子是，当使用语言区域的参数来作为路由前缀时，像是 `fr/blog` 和 `en/article/333` 等，这时我们如何来确保这两个首字母没有被用在其他语言呢？

`routes/web.php`:

```php
Route::group([
    'prefix' => '{locale}',
    'where' => ['locale' => '[a-zA-Z]{2}']
], function () {
    Route::get('/', 'HomeController@index');
    Route::get('article/{id}', 'ArticleController@show');
});
```

## 限流-全局配置与按用户配置

你可以使用 `throttle:60,1` 来限制一些 URL 在每分钟内最多被访问 60 次。

```php
Route::middleware('auth:api', 'throttle:60,1')->group(function () {
    Route::get('/user', function () {
        //
    });
});
```

另外，你也可以为公开请求和登录用户分别配置：

```php
// maximum of 10 requests for guests, 60 for authenticated users
Route::middleware('throttle:10|60,1')->group(function () {
    //
});
```

此外，你也可以使用数据库字段 `users.rate_limit` 为一些特殊用户设定此值。

```php
Route::middleware('auth:api', 'throttle:rate_limit,1')->group(function () {
    Route::get('/user', function () {
        //
    });
});
```

## 路由中的 URL 参数

如果你在路由中使用数组传入了其它参数，这些键 / 值将会自动配对并且带入 `URL` 查询参数中。

```php
Route::get('user/{id}/profile', function ($id) {
    //
})->name('profile');

$url = route('profile', ['id' => 1, 'photos' => 'yes']); // Result: /user/1/profile?photos=yes
```

## 按文件为路由分类

如果你有一组与某些功能相关的路由，你可以将它们放在一个特殊的文件 `routes/XXXXX.php` 中，然后在 `routes/web.php` 中使用 include 引入它。\`

Taylor Otwell 在 [Laravel Breeze](https://github.com/laravel/breeze/blob/1.x/stubs/routes/web.php) 中的例子： `routes/auth.php`

```php
Route::get('/', function () {
    return view('welcome');
});

Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware(['auth'])->name('dashboard');

require __DIR__.'/auth.php';
```

然后，在 `routes/auth.php` 中:

```php
use App\Http\Controllers\Auth\AuthenticatedSessionController;
use App\Http\Controllers\Auth\RegisteredUserController;
// ... more controllers

use Illuminate\Support\Facades\Route;

Route::get('/register', [RegisteredUserController::class, 'create'])
                ->middleware('guest')
                ->name('register');

Route::post('/register', [RegisteredUserController::class, 'store'])
                ->middleware('guest');

// ... A dozen more routes
```

但是，你应该只在路由都各自具有相同的前缀 / 中间件配置时使用 `include()` 来引入路由，否则，更好的选择是将他们分类在 `app/Providers/RouteServiceProvider` 中。

```php
public function boot()
{
    $this->configureRateLimiting();

    $this->routes(function () {
        Route::prefix('api')
            ->middleware('api')
            ->namespace($this->namespace)
            ->group(base_path('routes/api.php'));

        Route::middleware('web')
            ->namespace($this->namespace)
            ->group(base_path('routes/web.php'));

        // ... Your routes file listed next here
    });
}
```

## 翻译资源中的动词

当你使用了资源控制器，但希望变更 `URL` 谓词以适应非英语语言环境下的 `SEO` ，以在路由中用 `/crear` 替换 `/create`，你可以使用 `App\Providers\RouteServiceProvider 中的 Route::resourceVerbs()` 配置。

```php
public function boot()
{
    Route::resourceVerbs([
        'create' => 'crear',
        'edit' => 'editar',
    ]);

    // ...
}
```

## 自定义资源路由名称

当使用资源路由时，你可以在 `routes/web.php` 中指定 `->names() 参数`，这样一来，在整个 Laravel 项目中，浏览器中的 URL 前缀和路由名称前缀可能会不同。

```php
Route::resource('p', ProductController::class)->names('products');
```

这行代码将会生成像 `/p`, `/p/{id}`, `/p/{id}/edit` 这样的路由，但是你可以在代码中使用 `route('products.index')`, `route('products.create')` 等方式来调用它们。

## 可读性更强的路由列表

你有没有运行过 `php artisan route:list` ，然后发现这个列表又长，可读性又很差。 另一个方法是： `php artisan route:list --compact` 这样只会输出 3 列，而非 6 列：只展示方法名、 URI 和方法

```
+----------+---------------------------------+-------------------------------------------------------------------------+
| Method   | URI                             | Action                                                                  |
+----------+---------------------------------+-------------------------------------------------------------------------+
| GET|HEAD | /                               | Closure                                                                 |
| GET|HEAD | api/user                        | Closure                                                                 |
| POST     | confirm-password                | App\Http\Controllers\Auth\ConfirmablePasswordController@store           |
| GET|HEAD | confirm-password                | App\Http\Controllers\Auth\ConfirmablePasswordController@show            |
| GET|HEAD | dashboard                       | Closure                                                                 |
| POST     | email/verification-notification | App\Http\Controllers\Auth\EmailVerificationNotificationController@store |
| POST     | forgot-password                 | App\Http\Controllers\Auth\PasswordResetLinkController@store             |
| GET|HEAD | forgot-password                 | App\Http\Controllers\Auth\PasswordResetLinkController@create            |
| POST     | login                           | App\Http\Controllers\Auth\AuthenticatedSessionController@store          |
| GET|HEAD | login                           | App\Http\Controllers\Auth\AuthenticatedSessionController@create         |
| POST     | logout                          | App\Http\Controllers\Auth\AuthenticatedSessionController@destroy        |
| POST     | register                        | App\Http\Controllers\Auth\RegisteredUserController@store                |
| GET|HEAD | register                        | App\Http\Controllers\Auth\RegisteredUserController@create               |
| POST     | reset-password                  | App\Http\Controllers\Auth\NewPasswordController@store                   |
| GET|HEAD | reset-password/{token}          | App\Http\Controllers\Auth\NewPasswordController@create                  |
| GET|HEAD | verify-email                    | App\Http\Controllers\Auth\EmailVerificationPromptController@__invoke    |
| GET|HEAD | verify-email/{id}/{hash}        | App\Http\Controllers\Auth\VerifyEmailController@__invoke                |
+----------+---------------------------------+-------------------------------------------------------------------------+
```

你还可以特别地指定所需要的列：

`php artisan route:list --columns=Method,URI,Name`

```
+----------+---------------------------------+---------------------+
| Method   | URI                             | Name                |
+----------+---------------------------------+---------------------+
| GET|HEAD | /                               |                     |
| GET|HEAD | api/user                        |                     |
| POST     | confirm-password                |                     |
| GET|HEAD | confirm-password                | password.confirm    |
| GET|HEAD | dashboard                       | dashboard           |
| POST     | email/verification-notification | verification.send   |
| POST     | forgot-password                 | password.email      |
| GET|HEAD | forgot-password                 | password.request    |
| POST     | login                           |                     |
| GET|HEAD | login                           | login               |
| POST     | logout                          | logout              |
| POST     | register                        |                     |
| GET|HEAD | register                        | register            |
| POST     | reset-password                  | password.update     |
| GET|HEAD | reset-password/{token}          | password.reset      |
| GET|HEAD | verify-email                    | verification.notice |
| GET|HEAD | verify-email/{id}/{hash}        | verification.verify |
+----------+---------------------------------+---------------------+
```

## 预加载

如果你使用了路由模型绑定，并且你认为不会在绑定关系中使用预加载，请你再想一想。 所以当你用了这样的路由模型绑定:

```php
public function show(Product $product) {
    //
}
```

但是你有一个从属关系，这时候就不能使用 `$product->with('category')` 预加载了吗？ 你当然可以，使用 `->load()` 来加载关系

```php
public function show(Product $product) {
    $product->load('category');
    //
}
```

## 本地化资源 URI

如果你使用了资源控制器，但是想要将 URL 谓词变为非英语形式的，比如你想要西班牙语的 `/crear` 而不是 `/create` ，你可以使用 `Route::resourceVerbs()` 方法来配置。

```php
public function boot()
{
    Route::resourceVerbs([
        'create' => 'crear',
        'edit' => 'editar',
    ]);
    //
}
```

## 资源控制器命名

在资源控制器中，你可以在 `routes/web.php` 中指定 `->names()` 参数，这样 `URL` 前缀与路由前缀可能会不同 . 这样会生成诸如 `/p`, `/p/{id}`, `/p/{id}/edit` 等等，但是你可以这样调用它们：

`route('products.index)` `route('products.create)` 等等

```php
Route::resource('p', \App\Http\Controllers\ProductController::class)->names('products');
```

## 更简单地高亮你的导航栏

使用 `Route::is('route-name')` 来更简单的高亮你的导航栏

```html
<ul>
    <li @if(Route::is('home')) class="active" @endif>
        <a href="/">Home</a>
    </li>
    <li @if(Route::is('contact-us')) class="active" @endif>
        <a href="/contact-us">Contact us</a>
    </li>
</ul>
```

由 [@anwar\_nairi](https://twitter.com/anwar_nairi/status/1443893957507747849) 提供

## 使用 route 辅助函数生成绝对路径

```php
route('page.show', $page->id);
// http://laravel.test/pages/1

route('page.show', $page->id, false);
// /pages/1
```

由 [@oliverds\_](https://twitter.com/oliverds_/status/1445796035742240770) 提供

## 为你的每个模型重写路由绑定解析器

你可以为你的所有模型重写路由绑定解析器。在这个例子里，我们没有对`URL` 中的 `@` 符号做任何处理，所以 用\`\`resolveRouteBinding 我可以移除`@`符号 然后解析模型

```php
// Route
Route::get('{product:slug}', Controller::class);

// Request
https://nodejs.pub/@unlock/hello-world

// Product Model
public function resolveRouteBinding($value, $field = null)
{
    $value = str_replace('@', '', $value);
    
    return parent::resolveRouteBinding($value, $field);
}
```

由 [@Philo01](https://twitter.com/Philo01/status/1447539300397195269) 提供

## 如果你需要一个公共 URL 但是你想让他们更安全

如果你需要一个公共URL但是你想让他们更安全，使用 `Laravel signed URL`

```php
class AccountController extends Controller
{
    public function destroy(Request $request)
    {
        $confirmDeleteUrl = URL::signedRoute('confirm-destroy', [
            $user => $request->user()
        ]);
        // Send link by email...
    }
    
    public function confirmDestroy(Request $request, User $user)
    {
        if (! $request->hasValidSignature()) {
            abort(403);
        }
        
        // User confirmed by clikcing on the email
        $user->delete();
        
        return redirect()->route('home');
    }
}
```

由 [@anwar\_nairi](https://twitter.com/anwar_nairi/status/1448239591467589633) 提供

## 在中间件中使用 Gate

你可以在中间件中使用在 `App\Providers\AuthServiceProvider` 设置的 `Gate`

怎么做呢?你可以在路由中添加 `can:` 和必要 `gate` 的名字

```php
Route::put('/post/{post}', function (Post $post) {
    // The current user may update the post...
})->middleware('can:update,post');
```

## 简单路由-使用箭头函数

在路由中你可以使用PHP的箭头函数 而不需要用匿名函数。

要做到这一点 你可以使用 `fn() =>` 这样看起来更简单。

```php
// Instead of
Route::get('/example', function () {
    return User::all();
});
// You can
Route::get('/example', fn () => User::all());
```

## 路由视图

使用 `Route::view($uri , $bladePage)` 直接返回 view 而不需要控制器方法

```php
//this will return home.blade.php view
Route::view('/home', 'home');
```

## 路由目录代替路由文件

你可以创建一个 */routes/web/* 目录，只在 */routes/web.php* 中填写。

```php
foreach(glob(dirname(__FILE__).'/web/*', GLOB_NOSORT) as $route_file){
    include $route_file;
}
```

现在，*/routes/web/* 内的每个文件都作为一个路由文件，你可以将你的路由组织到不同的文件中。

## 路由资源分组

如果你的路由有很多资源控制器，你可以将它们分组并调用一个Route::resources()，而不是许多单一的Route::resource()语句。

```php
Route::resources([
    'photos' => PhotoController::class,
    'posts' => PostController::class,
]);
```

## 自定义路由绑定

你知道你可以在 Laravel 中自定义路由绑定吗?

在这个例子中，我需要通过 slug 来解析职务。但是 slug 不是唯一的，因为多个用户可以有名为 'Foo' 的职务

所以我配置了 Laravel 应该如何从路由参数中解析它们

```php
class RouteServiceProvider extends ServiceProvider
{
    public const HOME = '/dashboard';
    
    public function boot()
    {
        Route::bind('portfolio', function (string $slug) {
            return Portfolio::query()
                ->whereBelongsto(request()->user())
                ->whereSlug($slug)
                ->firstOrFail();
        });
    }
}
```

```php
Route::get('portfolios/{portfolio}', function (Portfolio $portfolio) {
    /*
     * The $portfolio will be the result of the query defined in the RouteServiceProvider
     */
})
```
