我非常喜歡寫基於模組化設計的軟體和程式設計方式,但我不太喜歡依賴第三方軟體包和類別庫來處理一些瑣碎的事情,因為它們不會讓你的程式水平得到很好的提升。所以這兩年來,我一直在用 Laravel 編寫基於模組的軟體,現在我對這個結果非常滿意。
推動我走向基於模組化設計的軟體和程式設計方式的決定性因素是我想持續提升我的程式設計水平。想像一下,你建構了一個專案結構,6 個月後你發現這個專案有許多 bug。在不影響 6 個月現有程式碼的情況下,通常不會輕易改變專案架構。在分析這個專案時,我注意到了兩個要點:你要么在整個專案中都有一個標準,要么堅持下去,要么模組化並逐個模組地改進。
有些人傾向於不惜一切代價、固守標準地開發,即使這可能意味著要堅持一個你不再喜歡的標準。就我個人來言,我更喜歡持續地改進,若是第 20 個模組和第 1 個模組寫得完全不一樣也沒關係。如果某天我需要回到模組 1 修復 BUG 或重構,我可以將其改進為第 20 個模組使用的最新標準。
假設,你也像我一樣喜歡基於模組化開發 Laravel 應用、盡可能避免在專案中添加不必要的第三方依賴 —— 本文是我的一點經驗。
1- 路由服務提供者
Laravel 路由系統可以說是整個應用程式的入口。首先需要修改的是預設的 RouteServiceProvider.php 文件,它應該將現有路由模組化。
<?php namespace App\Providers; use Illuminate\Support\Facades\Route; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; class RouteServiceProvider extends ServiceProvider { /** * 定义应用路由。 * * @return void */ public function map() { $this->mapModulesRoutes(); } protected function mapModulesRoutes() { // 如果你在编写传统 Web 应用而非 HTTP API,请使用 `web` 中间件。 Route::middleware('api') ->group(base_path('routes/modules.php')); } }
如上,我們可以直接擺脫該檔案的整個樣板,只需設定一個模組化的路由檔案。
2- 模組檔案
Laravel 在 routes 資料夾中自帶了一些檔案。由於我們已經不在 RouteServiceProvider 中對應這些路由,所以可以直接刪除它們。接下來,我們建立一個 modules.php 路由檔案。
<?php use Illuminate\Support\Facades\Route; Route::group([], base_path('app/Modules/Books/routes.php')); Route::group([], base_path('app/Modules/Authors/routes.php'));
3- Books 模組
在 app 資料夾中,建立 Modules/Books/routes.php 檔案。在此文件中,我們可以定義該應用 Books 模組的路由規則。
<?php use App\Modules\Books\ListBooks; use Illuminate\Support\Facades\Route; Route::get('/books', ListBooks::class);
你可以使用基於控制器 —— 也就是 Laravel 中預設標準的路由方式,但我個人更喜歡 Good bye controllers, hello Request Handlers(放棄控制器,採用請求處理器) 的方式。如下是 ListBooks 的實作。
<?php namespace App\Modules\Books; use App\Eloquent\Book; use App\Modules\Books\Resources\BookResource; class ListBooks { public function __invoke(Book $book) { return BookResource::collection($book->paginate()); } }
以上程式碼中 BookResource 是 Laravel 的資源轉換層。按照官方對於命名空間的建議,我們可以在 app/Modules/Books/Resources 資料夾中建立它。
<?php namespace App\Modules\Books\Resources; use Illuminate\Http\Resources\Json\Resource; class BookResource extends Resource { public function toArray($request) { return [ 'id' => $this->resource->id, 'title' => $this->resource->title, ]; } }
4- Authors 模組
我們也可以透過 Routes 檔案來啟動 Authors 模組。
<?php use App\Modules\Authors\ListAuthors; use Illuminate\Support\Facades\Route; Route::get('/authors', ListAuthors::class);
注意: app/Modules/Authors 這個命名空間正表示我們所寫的文件,對於請求處理程序來說也是非常簡單的。
<?php namespace App\Modules\Authors; use App\Eloquent\Author; use App\Modules\Authors\Resources\AuthorResource; class ListAuthors { public function __invoke(Author $author) { return AuthorResource::collection($author->paginate()); } }
最後,我們將編寫的 Resource 類別轉變為響應式的 JSON 格式。
<?php namespace App\Modules\Authors\Resources; use App\Modules\Books\Resources\BookResource; use Illuminate\Http\Resources\Json\Resource; class AuthorResource extends Resource { public function toArray($request) { return [ 'id' => $this->resource->id, 'name' => $this->resource->name, 'books' => $this->whenLoaded('books', function () { return BookResource::collection($this->resource->books); }) ]; } }
注意資源是如何進入另一個模組以重複使用 BookResource 。這通常不是一個比較好的選擇,因為模組應該是完全自給自足的,並且只能重複使用標準類,例如 Eloquent Models 或設計用於在任何模組上通用的通用的組件。這個問題的解決方案通常是將 BookResource 複製到 Authors 模組中,因此可以在不使用另一個模組的情況下進行更改,反之亦然。我決定保留這個跨模組的用法,這個例子表現出一個很好的經驗方法,就是讓模組之間彼此隔離,但是如果你認為上面的例子很簡單並且不太可能帶來任何問題。始終確保編寫測試以涵蓋您編寫的功能,以避免其他人在不知不覺中修改您的應用程式。
5- 結語
雖然這是一個非常簡單的例子,但我希望它能夠讓人們根據自己的需求來輕鬆操作使用Laravel 框架的結構標準。您可以非常輕鬆地更改檔案的位置,以便建立基於模組化的應用程式。我的大多數專案都附帶了App / Components 模組,可以適用於任何模組可重用的泛類型的基礎類別; App / Eloquent ,Modules 資料夾可以用於保存Eloquent 模型和資料庫關係模型,我們可以在其中構建任何基於模組化的功能。這是我最近開始研究的應用程式的資料夾目錄結構:
我希望每個人都能從中得到這個概念,每個模組都有自己的需求,並且可以擁有自己的資料夾 / 實體 / 類別 / 方法 / 屬性。沒有必要將所有模組標準化完全相同,因為某些模組比其他模組簡單得多,並且不需要大量的結構設計。此範例顯示 AccountChurn 模組透過 HTTP 資料夾提供 API,同時仍透過控制台提供 Artisan 指令。另一方面,AccountOverview 則僅提供 HTTP API,並且依賴倉庫、值物件(bags)以及服務類別(paginators)來提供更大的資料價值。
以上是Laravel 是基於 Module 實作 API 架構的詳細內容。更多資訊請關注PHP中文網其他相關文章!