5

Laravel 路由群組:6 個組織路由的技巧

 1 year ago
source link: https://calvertyang.github.io/2022/10/27/laravel-route-grouping-6-techniques-to-organize-routes/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

原文:Povilas KoropLaravel Route Grouping: 6 Techniques to Organize Routes

Laravel 路由是開發者從一開始就學到的功能。但是隨著專案的成長,管理不斷增加的路由檔案及找到正確的 Route::get() 定義變得越來越困難。幸運的是,有一些技巧可以讓路由變得更簡短、易讀,用不同的方式對路由及其設定組成群組。讓我們來看看吧!

不過,我們會更深入一點,而不是只探討初學者等級的 Route::group()

群組 1. Route::resource 和 Route::apiResource

讓我們從一個常被忽視的問題開始吧,這應該是個眾所皆知的群組。如果你對模型有一組典型的 CRUD 操作,可以將它們用 resource 控制器組成群組。

這個控制器最多可以涵蓋 7 種方法(實際應用上可能更少):

  • index()
  • create()
  • store()
  • show()
  • edit()
  • update()
  • destroy()

所以,如果你有一組路由對應到這些方法,請不要這樣寫:

Route::get('books', [BookController::class, 'index'])->name('books.index');
Route::get('books/create', [BookController::class, 'create'])->name('books.create');
Route::post('books', [BookController::class, 'store'])->name('books.store');
Route::get('books/{book}', [BookController::class, 'show'])->name('books.show');
Route::get('books/{book}/edit', [BookController::class, 'edit'])->name('books.edit');
Route::put('books/{book}', [BookController::class, 'update'])->name('books.update');
Route::delete('books/{book}', [BookController::class, 'destroy'])->name('books.destroy');

你只需要一行:

Route::resource('books', BookController::class);

如果你的是 API 專案,則不需要用於建立或編輯的視覺化表單。因此, 你可以使用 apiResource() 語法,它涵蓋了 7 種方法中的 5 種:

Route::apiResource('books', BookController::class);

另外,即便你只有使用 2 到 4 種方法而不是完整的 7 個,我依然建議你使用 resource 控制器。因為它保留了用於 URL、方法和路由名稱的標準命名慣例。例如,在這種情況下,你不需要手動提供名稱:

Route::get('books/create', [BookController::class, 'create'])->name('books.create');
Route::post('books', [BookController::class, 'store'])->name('books.store');
 
// 相反地,這裡的 "books.create" 和 "books.store" 名稱是自動分配的
Route::resource('books', BookController::class)->only(['create', 'store']);

群組 2. 群組中的群組

當然,一般的路由群組大家都知道。但對於更複雜的專案,單一階層的群組可能不夠。

舉個實際的例子,你想要使用 auth 中介層對需要授權的路由組成群組,但是在裡面你需要區隔更多的子群組,例如管理員和一般用戶。

Route::middleware('auth')->group(function() {
 
    Route::middleware('is_admin')->prefix('admin')->group(function() {
        Route::get(...) // 管理員路由
    });
 
    Route::middleware('is_user')->prefix('user')->group(function() {
        Route::get(...) // 一般用戶路由
    });
});

群組 3. 將重複的中介層組成群組

如果你有很多中介層,其中一些在幾個路由群組重複出現怎麼辦?

Route::prefix('students')->middleware(['auth', 'check.role', 'check.user.status', 'check.invoice.status', 'locale'])->group(function () {
    // ... 學生路由
});
 
Route::prefix('managers')->middleware(['auth', 'check.role', 'check.user.status', 'locale'])->group(function () {
    // ... 管理員路由
});

如你所見,5 個中介層有 4 個重複。因此,我們可以在 app/Http/Kernel.php 檔案內將這 4 個中介層移動到獨立的中介層群組中:

protected $middlewareGroups = [
    // Laravel 預設群組
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
 
    // Laravel 預設群組
    'api' => [
        // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
        'throttle:api',
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
 
    // 這是我們新的中介層群組
    'check_user' => [
        'auth',
        'check.role',
        'check.user.status',
        'locale'
    ],
];

我們將中介層群組命名為 check_user,現在我們可以縮短路由:

Route::prefix('students')->middleware(['check_user', 'check.invoice.status'])->group(function () {
    // ... 學生路由
});
 
Route::prefix('managers')->middleware(['check_user'])->group(function () {
    // ... 管理員路由
});

群組 4. 相同名稱但不同命名空間的控制器

還有一個常見的狀況是,用於不同用戶角色的 HomeController,例如 Admin/HomeControllerUser/HomeController。如果你在路由中使用完整路徑,看起來會像這樣:

Route::prefix('admin')->middleware('is_admin')->group(function () {
    Route::get('home', [\App\Http\Controllers\Admin\HomeController::class, 'index']);
});
 
Route::prefix('user')->middleware('is_user')->group(function () {
    Route::get('home', [\App\Http\Controllers\User\HomeController::class, 'index']);
});

使用完整路徑需要打很多字,對吧?這就是為什麼許多開發者更偏好在路由清單中只有一個 HomeController::class,並在最上面這樣寫:

use App\Http\Controllers\Admin\HomeController;

但問題是我們有相同的控制器類別名稱!所以,這樣行不通:

use App\Http\Controllers\Admin\HomeController;
use App\Http\Controllers\User\HomeController;

哪個才是主要的呢?好吧,其中一種方法是更改名稱並將別名指定給其中一個:

use App\Http\Controllers\Admin\HomeController as AdminHomeController;
use App\Http\Controllers\User\HomeController;
 
Route::prefix('admin')->middleware('is_admin')->group(function () {
    Route::get('home', [AdminHomeController::class, 'index']);
});
 
Route::prefix('user')->middleware('is_user')->group(function () {
    Route::get('home', [HomeController::class, 'index']);
});

但是,就我個人而言,在上面更改名稱會讓我很困惑,我喜歡另一種方法:為控制器的子資料夾加上一個 namespace()

Route::prefix('admin')->namespace('App\Http\Controllers\Admin')->middleware('is_admin')->group(function () {
    Route::get('home', [HomeController::class, 'index']);
    // ... Admin 命名空間中的其他控制器
});
 
Route::prefix('user')->namespace('App\Http\Controllers\User')->middleware('is_user')->group(function () {
    Route::get('home', [HomeController::class, 'index']);
    // ... User 命名空間中的其他控制器
});

群組 5. 獨立的路由檔案

如果你覺得 routes/web.phproutes/api.php 變得太大,你可以把一些路由放到隨你命名的獨立檔案中,例如 routes/admin.php

然後,你有兩種方式可以使用該檔案:我稱之為「Laravel 的方式」和「PHP 的方式」。

如果你想遵循 Laravel 建構其預設路由檔案的結構,它在 app/Providers/RouteServiceProvider.php 中:

public function boot()
{
    $this->configureRateLimiting();
 
    $this->routes(function () {
        Route::middleware('api')
            ->prefix('api')
            ->group(base_path('routes/api.php'));
 
        Route::middleware('web')
            ->group(base_path('routes/web.php'));
    });
}

可以看到,routes/api.phproutes/web.php 都在這,但設定略有不同。因此,你需要做的就是在這邊加上 admin 檔案:

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

但是如果你不想深入到服務提供者的話,有個簡單的方法,只需要將你的路由檔案 include 或 require 到另一個檔案中,就像在 Laravel 框架之外的任何 PHP 檔案中所做的那樣。

事實上,這是 Taylor Otwell 所做的,直接將 routes/auth.php 檔案放到 Laravel Breeze 路由中:



routes/web.php

Route::get('/', function () { return view('welcome'); }); Route::get('/dashboard', function () { return view('dashboard'); })->middleware(['auth', 'verified'])->name('dashboard'); require __DIR__.'/auth.php';

群組 6. Laravel 9 的新功能:Route::controller()

如果控制器中有些方法未遵循標準的資源結構,你仍然可以將它們組成群組,而無需為每個方法重複控制器名稱。

原本這樣的寫法:

Route::get('profile', [ProfileController::class, 'getProfile']);
Route::put('profile', [ProfileController::class, 'updateProfile']);
Route::delete('profile', [ProfileController::class, 'deleteProfile']);

可以改成以下寫法:

Route::controller(ProfileController::class)->group(function() {
    Route::get('profile', 'getProfile');
    Route::put('profile', 'updateProfile');
    Route::delete('profile', 'deleteProfile');
});

可以在 Laravel 9 和 Laravel 8 的最新次要版本中使用此功能。


就是這樣!無論你的專案有多大,希望這些群組技巧將有助於你組織和維護路由。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK