路由
分组中的分组
在路由中,你可以在分组中创建分组,来实现仅仅为父分组中的某些路由分配中间件。
1 2 3 4 5 6 7 8
| 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'); }); });
|
通配符子域名
你可以在分组中定义变量,来创建动态子域名分组,然后将这个变量传递给每一个子路由。
1 2 3 4 5
| Route::domain('{username}.workspace.com')->group(function () { Route::get('user/{id}', function ($username, $id) { }); });
|
routes调用之后是什么
若你使用 Laravel UI package, 你可能想知道Auth::routes()定义之后真正的路由是什么?
查看 /vendor/laravel/ui/src/AuthRouteMethods.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public function auth() { return function ($options = []) { $this->get('login', 'Auth\LoginController@showLoginForm')->name('login'); $this->post('login', 'Auth\LoginController@login'); $this->post('logout', 'Auth\LoginController@logout')->name('logout'); if ($options['register'] ?? true) { $this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register'); $this->post('register', 'Auth\RegisterController@register'); } if ($options['reset'] ?? true) { $this->resetPassword(); } if ($options['confirm'] ?? class_exists($this->prependGroupNamespace('Auth\ConfirmPasswordController'))) { $this->confirmPassword(); } if ($options['verify'] ?? false) { $this->emailVerification(); } }; }
|
默认调用:
但是你可以提供参数来启用或禁用真正的路由
But you can provide parameters to enable o disable certain routes:
1 2 3 4 5 6 7 8
| Auth::routes([ 'login' => true, 'logout' => true, 'register' => true, 'reset' => true, 'confirm' => false, 'verify' => false, ]);
|
路由模型绑定-你可以定义一个Key
你可以像 Route::get('api/users/{user}', function (App\User $user) { … } 这样来进行路由模型绑定,但不仅仅是 ID 字段,如果你想让 {user} 是 username,你可以把它放在模型中:
1 2 3
| public function getRouteKeyName() { return 'username'; }
|
快速从路由导航到控制器
在 Laravel 8 之前,这件事情是可选的。在 Laravel 8 中这将成为路由的标准语法
不用这么写:
1
| Route::get('page', 'PageController@action');
|
你可以将控制器标识为 class 类名:
1
| Route::get('page', [\App\Http\Controllers\PageController::class, 'action']);
|
这样,你就可以在 PhpStorm中点击 PageController 来跳转到控制器定义,而不是手动去搜索它
或者你想要让路由的定义更简洁,你可以在路由文件的开始提前引入控制器的类。
1 2 3 4
| use App\Http\Controllers\PageController;
Route::get('page', [PageController::class, 'action']);
|
备选路由-当没有匹配到任何路由时
如果你想为未找到的路由指定其它逻辑,而不是直接显示 404 页面,你可以在路由文件的最后为其创建一个特殊的路由。
1 2 3 4 5 6 7 8 9
| Route::group(['middleware' => ['auth'], 'prefix' => 'admin', 'as' => 'admin.'], function () { Route::get('/home', 'HomeController@index'); Route::resource('tasks', 'Admin\TasksController'); });
Route::fallback(function() { return 'Hm, why did you land here somehow?'; });
|
使用正则进行路由参数验证
我们可以在路由中使用 where参数 来直接验证参数。一个典型的例子是,当使用语言区域的参数来作为路由前缀时,像是 fr/blog 和 en/article/333 等,这时我们如何来确保这两个首字母没有被用在其他语言呢?
routes/web.php:
1 2 3 4 5 6 7
| 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 次。
1 2 3 4 5
| Route::middleware('auth:api', 'throttle:60,1')->group(function () { Route::get('/user', function () { }); });
|
另外,你也可以为公开请求和登录用户分别配置:
1 2 3 4
| Route::middleware('throttle:10|60,1')->group(function () { });
|
此外,你也可以使用数据库字段 users.rate_limit 为一些特殊用户设定此值。
1 2 3 4 5
| Route::middleware('auth:api', 'throttle:rate_limit,1')->group(function () { Route::get('/user', function () { }); });
|
路由中的URL参数
如果你在路由中使用数组传入了其它参数,这些键 / 值将会自动配对并且带入 URL 查询参数中。
1 2 3 4 5
| Route::get('user/{id}/profile', function ($id) { })->name('profile');
$url = route('profile', ['id' => 1, 'photos' => 'yes']);
|
按文件为路由分类
如果你有一组与某些功能相关的路由,你可以将它们放在一个特殊的文件 routes/XXXXX.php 中,然后在 routes/web.php 中使用 include 引入它。`
Taylor Otwell 在 Laravel Breeze 中的例子:
routes/auth.php
1 2 3 4 5 6 7 8 9
| Route::get('/', function () { return view('welcome'); });
Route::get('/dashboard', function () { return view('dashboard'); })->middleware(['auth'])->name('dashboard');
require __DIR__.'/auth.php';
|
然后,在 routes/auth.php 中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| use App\Http\Controllers\Auth\AuthenticatedSessionController; use App\Http\Controllers\Auth\RegisteredUserController;
use Illuminate\Support\Facades\Route;
Route::get('/register', [RegisteredUserController::class, 'create']) ->middleware('guest') ->name('register');
Route::post('/register', [RegisteredUserController::class, 'store']) ->middleware('guest');
|
但是,你应该只在路由都各自具有相同的前缀 / 中间件配置时使用 include() 来引入路由,否则,更好的选择是将他们分类在 app/Providers/RouteServiceProvider 中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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'));
}); }
|
翻译资源中的动词
当你使用了资源控制器,但希望变更 URL 谓词以适应非英语语言环境下的 SEO ,以在路由中用 /crear 替换 /create,你可以使用 App\Providers\RouteServiceProvider 中的 Route::resourceVerbs() 配置。
1 2 3 4 5 6 7 8 9
| public function boot() { Route::resourceVerbs([ 'create' => 'crear', 'edit' => 'editar', ]);
}
|
自定义资源路由名称
当使用资源路由时,你可以在 routes/web.php 中指定 ->names() 参数,这样一来,在整个 Laravel 项目中,浏览器中的 URL 前缀和路由名称前缀可能会不同。
1
| 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 和方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| +----------+---------------------------------+-------------------------------------------------------------------------+ | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| +----------+---------------------------------+---------------------+ | 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 | +----------+---------------------------------+---------------------+
|
预加载
如果你使用了路由模型绑定,并且你认为不会在绑定关系中使用预加载,请你再想一想。
所以当你用了这样的路由模型绑定:
1 2 3
| public function show(Product $product) { }
|
但是你有一个从属关系,这时候就不能使用 $product->with('category') 预加载了吗?
你当然可以,使用 ->load() 来加载关系
But you have a belongsTo relationship, and cannot use $product->with(‘category’) eager loading?
You actually can! Load the relationship with ->load()
1 2 3 4
| public function show(Product $product) { $product->load('category'); }
|
本地化资源URI
如果你使用了资源控制器,但是想要将 URL 谓词变为非英语形式的,比如你想要西班牙语的 /crear 而不是 /create ,你可以使用 Route::resourceVerbs() 方法来配置。
1 2 3 4 5 6 7 8
| 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)
等等
1
| Route::resource('p', \App\Http\Controllers\ProductController::class)->names('products');
|
更简单地高亮你的导航栏
使用Route::is('route-name')来更简单的高亮你的导航栏
1 2 3 4 5 6 7 8
| <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>
|
使用route辅助函数生成绝对路径
1 2 3 4 5
| route('page.show', $page->id);
route('page.show', $page->id, false);
|
为你的每个模型重写路由绑定解析器
你可以为你的所有模型重写路由绑定解析器。在这个例子里,我们没有对URL 中的 @ 符号做任何处理,所以 用``resolveRouteBinding 我可以移除@符号 然后解析模型
1 2 3 4 5 6 7 8 9 10 11 12 13
| Route::get('{product:slug}', Controller::class);
https:
public function resolveRouteBinding($value, $field = null) { $value = str_replace('@', '', $value); return parent::resolveRouteBinding($value, $field); }
|
如果你需要一个公共URL但是你想让他们更安全
如果你需要一个公共URL但是你想让他们更安全,使用 Laravel signed URL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class AccountController extends Controller { public function destroy(Request $request) { $confirmDeleteUrl = URL::signedRoute('confirm-destroy', [ $user => $request->user() ]); } public function confirmDestroy(Request $request, User $user) { if (! $request->hasValidSignature()) { abort(403); } $user->delete(); return redirect()->route('home'); } }
|
在中间件中使用Gate
你可以在中间件中使用在 App\Providers\AuthServiceProvider设置的Gate
怎么做呢?你可以在路由中添加can:和必要gate的名字
1 2 3
| Route::put('/post/{post}', function (Post $post) { })->middleware('can:update,post');
|
简单路由-使用箭头函数
在路由中你可以使用PHP的箭头函数 而不需要用匿名函数。
要做到这一点 你可以使用 fn() => 这样看起来更简单。
1 2 3 4 5 6
| Route::get('/example', function () { return User::all(); });
Route::get('/example', fn () => User::all());
|
路由视图
使用Route::view($uri , $bladePage)直接返回view 而不需要控制器方法
1 2
| Route::view('/home', 'home');
|