Laravel Routing là tính năng mà các nhà phát triển học ngay từ đầu. Nhưng khi các dự án của họ phát triển, ngày càng khó quản lý các tệp route ngày càng tăng.

Để tìm các Route rất mất thời gian. May mắn thay, có những kỹ thuật để làm cho các tệp route ngắn hơn và dễ đọc hơn, nhóm các route và cài đặt của chúng theo những cách khác nhau. Chúng ta hãy xem xét.

Và không, mình sẽ không chỉ nói về đơn giản chung chung Route::group(), đó là cấp độ mới bắt đầu. Hãy đi sâu hơn một chút về điều đó.

Phân nhóm 1. Route::resource và Route::apiResource

Hãy bắt đầu với con voi trong phòng: đây có lẽ là cách phân nhóm nổi tiếng nhất. Nếu bạn có một tập hợp các hành động CRUD điển hình xung quanh một Model, bạn nên nhóm chúng thành một Resource Controller

Resource Controllercó thể bao gồm tối đa 7 phương thức (nhưng có thể có ít hơn):

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

Vì vậy, nếu tập hợp các route của bạn tương ứng với các phương thức đó, thay vì:

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');

… bây giờ có thể chỉ có một dòng:

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

Nếu bạn làm việc với một dự án API, bạn không cần các Form để tạo / chỉnh sửa, vì vậy bạn có thể có một cú pháp khác với cú pháp apiResource() sẽ bao gồm 5 trong số 7 phương thức:

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

Ngoài ra, mình khuyên bạn nên xem xét các Resource Controller ngay cả khi bạn có 2-4 phương thức chứ không phải đầy đủ 7. Chỉ vì nó giữ quy ước đặt tên tiêu chuẩn – cho URL, phương thức và tên cho route. Ví dụ: trong trường hợp này, bạn không cần phải viết tên theo cách thủ công:

Route::get('books/create', [BookController::class, 'create'])->name('books.create');
Route::post('books', [BookController::class, 'store'])->name('books.store');

// Thay vào đó, ở đây tên "books.create" và "books.store" được chỉ định tự động
Route::resource('books', BookController::class)->only(['create', 'store']);

Phân nhóm 2. Nhóm trong một Group

Tất nhiên, mọi người đều biết về Route groupping. Nhưng đối với các dự án phức tạp hơn, một cấp độ nhóm có thể không đủ.

Ví dụ thực tế: bạn muốn các route được authorized được nhóm với middleware auth , nhưng bên trong bạn cần phải tách nhiều nhóm con hơn, như quản trị viên và người dùng.

Route::middleware('auth')->group(function() {
 
    Route::middleware('is_admin')->prefix('admin')->group(function() {
    	Route::get(...) // administrator routes
    });
 
    Route::middleware('is_user')->prefix('user')->group(function() {
    	Route::get(...) // user routes
    });
});

Phân nhóm 3. Lặp lại middleware vào Group

Điều gì sẽ xảy ra nếu bạn có khá nhiều middleware, một số trong số chúng lặp lại trong một vài route groupping?

Route::prefix('students')->middleware(['auth', 'check.role', 'check.user.status', 'check.invoice.status', 'locale'])->group(function () {
    // ... student routes
});
 
Route::prefix('managers')->middleware(['auth', 'check.role', 'check.user.status', 'locale'])->group(function () {
    // ... manager routes
});

Như bạn thấy, có 5 middleware, 4 trong số chúng lặp lại. Vì vậy, chúng ta có thể chuyển 4 cái đó vào một nhóm middleware riêng biệt, trong tệp App/Http/Kernel.php:


protected $middlewareGroups = [
    
    // ĐÂY LÀ NHÓM PHẦN MỀM TRUNG GIAN MỚI CỦA CHÚNG TA

    'check_user' => [
        'auth',
        'check.role',
        'check.user.status',
        'locale'
    ],
];

bây giờ chúng ta có thể rút ngắn các route :

Route::prefix('students')->middleware(['check_user', 'check.invoice.status'])->group(function () {
    // ... student routes
});
 
Route::prefix('managers')->middleware(['check_user'])->group(function () {
    // ... manager routes
})

Nhóm 4. Giống tên Controllers & khác Namespaces

Một tình huống khá phổ biến là có, ví dụ, HomeController cho các vai trò người dùng khác nhau, như Admin/HomeController User/HomeController. Và nếu bạn sử dụng đường dẫn đầy đủ trong các route của mình, nó trông giống như sau:

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']);
});

Khá nhiều code để gõ với những đường dẫn đầy đủ đó, phải không? Đó là lý do tại sao nhiều dev thích chỉ có lớp HomeController trong danh sách route và thêm một cái gì đó như thế này lên trên:

use App\Http\Controllers\Admin\HomeController;

Nhưng vấn đề ở đây là chúng ta có cùng tên Controller! Vì vậy, điều này sẽ không hoạt động:

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

Cái nào sẽ là cái “chính thức”? Có một cách là thay đổi tên và gán bí danh :

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']);
});

Tuy nhiên, theo cá nhân tôi, việc thay đổi tên của Class trên cùng là khá khó hiểu , tôi thích một cách tiếp cận khác: thêm một namespace() cho các thư mục con của Controllers:

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

Phân nhóm 5. Tệp Route riêng

Nếu bạn cảm thấy rằng routes/web.php hoặc routes/api.php quá lớn, bạn có thể lấy một số route và đặt chúng vào một tệp riêng, đặt tên chúng theo cách bạn muốn, chẳng hạn như routes/admin.php.

Sau đó, để cho phép đưa tệp đó vào, bạn có hai cách: Tôi gọi là “cách Laravel” và “cách PHP“.

Nếu bạn muốn theo dõi cấu trúc về cách Laravel cấu trúc các tệp tuyến đường mặc định của nó, thì nó đang diễn ra trong 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'));
    });
}

Như bạn có thể thấy, cả hai routes/api.php và routes/web.php đều ở đây, với một chút cài đặt khác nhau. Vì vậy, tất cả những gì bạn cần làm là thêm tệp admin của mình tại đây:

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'));
    });
}

Nhưng nếu bạn không muốn đi sâu vào các Provider , có một cách ngắn hơn – chỉ cần include / require tệp của bạn vào một tệp khác giống như bạn làm trong bất kỳ tệp PHP nào, bên ngoài khuôn khổ Laravel.

Trên thực tế, nó được thực hiện bởi chính Taylor Otwell,require routes/auth.php tệp trực tiếp vào các tuyến Laravel Breeze :

route/web.php :

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

Phân nhóm 6. Laravel 9: Route::controller ()

Nếu bạn có một vài phương thức trong Controller nhưng chúng không tuân theo cấu trúc Resource , bạn vẫn có thể nhóm chúng lại mà không cần lặp lại tên Controller cho mọi phương thức.

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

Chức năng này có sẵn trong Laravel 9 và các phiên bản mới nhất của Laravel 8.

Kết luận

Đây là những kỹ thuật phân nhóm, hy vọng sẽ giúp bạn tổ chức và duy trì các route của mình, bất kể dự án của bạn phát triển lớn đến mức nào.

Tác giả

Gim