0

[Laravel] Giải ngố Modular: Bí mật đằng sau việc Laravel "đọc" được Route trong các Modules tự chế

Chào anh em, lại là mình đây.

Nếu anh em đang làm một dự án Laravel quy mô vừa và lớn (như một hệ thống e-commerce chẳng hạn), chắc chắn kiến trúc Monolithic (nhét tất cả vào app/) sẽ sớm trở thành một đống rác khổng lồ. Và thế là anh em quyết định chuyển sang kiến trúc Modular (chia nhỏ thành các module như Modules/Product, Modules/Order...).

Chia file xong xuôi, chia route đẹp đẽ. Bật Postman lên test thử và... 404 Not Found! Ủa alo? Rõ ràng đã viết route /api/orders trong Modules/Order/Routes/api.php rồi cơ mà? Tại sao Laravel lại báo không tìm thấy?

Sự thật là: Mặc định khi cài đặt, Laravel "mù tịt" về các thư mục do bạn tự tạo ra. Nó chỉ được lập trình sẵn để đọc đúng 2 file routes/web.phproutes/api.php ở thư mục gốc mà thôi.

Để Laravel "sáng mắt ra" và chạy được route trong Modules của bạn, chúng ta cần một cơ chế "đăng ký hộ khẩu". Bài viết này sẽ bóc trần cơ chế đó cho anh em.

1. Nguyên lý cốt lõi: Kẻ dẫn đường mang tên "Service Provider"

Trong thế giới Laravel, để framework biết đến sự tồn tại của một thành phần bên ngoài (như một thư viện, hay một Module tự chế), chúng ta cần một người đại diện. Kẻ đó chính là Service Provider.

Mọi module khi tách ra đều phải có ít nhất một file Service Provider riêng (ví dụ: ProductServiceProvider.php, OrderServiceProvider.php).

File này giống như một ông tổ trưởng dân phố. Khi Laravel khởi động hệ thống, nó sẽ gọi hàm boot() bên trong file này. Chính tại thời khắc này, anh em phải viết code để "nhắc nhở" Laravel: "Này framework, tao có mấy cái route nằm ở thư mục này này, nạp nó vào bộ nhớ giúp tao!".

2. Thực chiến: 2 Cách để Laravel "khai sáng"

Thực tế đi làm, anh em sẽ gặp 2 trường phái để giải quyết bài toán này:

Cách A: Hệ lười (Dùng thư viện)

Đây là cách phổ biến nhất. Đa số anh em sẽ cài một package "quốc dân" là nwidart/laravel-modules. Khi xài thằng này, anh em gần như không phải làm gì vì nó đã lo tận răng:

  1. Thư viện có sẵn cơ chế Autoload, nó tự động nội soi toàn bộ thư mục Modules/.
  2. Nó đọc file module.json trong từng Module để xác định vị trí của file Provider.
  3. Nó tự động kích hoạt các Provider đó lên.
  4. Và ẩn sâu bên trong Provider mà thư viện gen ra sẵn cho anh em, có một đoạn code nạp route thế này:
// Đoạn code ẩn bên trong module provider
public function boot() 
{      
    $this->loadRoutesFrom(__DIR__ . '/Routes/web.php');  
}

Cách B: Hệ Try-hard (Chạy bằng cơm)

Nếu anh em không thích phụ thuộc thư viện mà muốn tự tay cấu trúc thư mục, anh em phải tự làm "thủ tục hành chính" gồm 2 bước:

Bước 1: Viết code nạp route trong Provider của Module Tạo file Modules/Order/Providers/OrderServiceProvider.php và sử dụng hàm loadRoutesFrom thần thánh:

namespace Modules\Order\Providers;

use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;

class OrderServiceProvider extends ServiceProvider
{
    public function boot()
    {
        // Dòng lệnh thần thánh giúp Laravel nhận ra route
        $this->loadRoutesFrom(__DIR__ . '/../Routes/web.php');

        // Hoặc nếu anh em muốn cấu hình "kỹ" hơn (thêm middleware, namespace...):
        // Route::middleware('web')
        //      ->namespace('Modules\\Order\\Http\\Controllers')
        //      ->group(__DIR__ . '/../Routes/web.php');
    }
}

Bước 2: Khai báo "hộ khẩu" với Laravel

Viết Provider xong, nếu để im đó thì Laravel vẫn không biết nó tồn tại. Phải đi đăng ký! Anh em mở file config/app.php lên, tìm đến cái mảng 'providers' siêu to khổng lồ và nhét thêm class của mình vào:

'providers' => [
    // ... Một nùi các provider mặc định của Laravel

    // Provider của Module anh em mới tạo
    Modules\Order\Providers\OrderServiceProvider::class, 
],

Xong! Từ nay mỗi khi gọi route của Module, Laravel sẽ không còn báo 404 nữa.

3. Under the Hood: Quy trình chạy thực tế phía sau

Để hiểu sâu hơn (phục vụ mục đích đi khè lúc phỏng vấn), anh em cần biết chính xác khi một Request bay từ trình duyệt vào server, Laravel xử lý mấy cái file này theo thứ tự nào:

  1. Khởi động (Bootstrapping): Hệ thống thức dậy và đọc cái file config/app.php đầu tiên.
  2. Duyệt danh sách Providers: Nó thấy cái mảng providers và bắt đầu chạy vòng lặp qua tất cả các class trong đó.
  3. Kích hoạt boot(): Nó mò tới cái OrderServiceProvider của anh em và chạy hàm boot().
  4. Nạp Route (Gom bi): Lệnh loadRoutesFrom được thực thi. Toàn bộ route trong thư mục Module của anh em sẽ được bốc lên, cộng gộp vào cái danh sách route chung (Route Collection) của toàn bộ ứng dụng.
  5. Xử lý Request: Sau khi gom xong hết route của mọi module, lúc này Laravel mới lấy cái URL mà người dùng gửi lên, dò vào cái bảng danh sách tổng kia xem có trùng khớp không để đẩy về đúng Controller.

Chốt hạ

Làm việc với framework, đôi khi cái hay không nằm ở việc anh em viết được logic phức tạp, mà nằm ở chỗ anh em hiểu được "luật chơi" của nó. Laravel mặc định rất ngố, nhưng nó cung cấp đủ đồ chơi (như Composer autoload, config/app.php, và ServiceProvider) để anh em "dạy" nó cách thông minh hơn.

Hy vọng chút kiến thức "under the hood" này giúp anh em làm chủ kiến trúc Modular tốt hơn và không bị hoảng loạn mỗi khi dính lỗi 404 ở những route mới tạo. Nếu thấy hay, cho mình xin một upvote để lấy động lực ra lò tiếp các trick hay về Laravel nhé!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí