最近很火的Laravel存儲庫模式(Repository)

下面由laravel教程欄目帶大家推薦介紹關于laravel存儲庫模式(repository),希望對大家有所幫助!

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

最近很火的Laravel存儲庫模式(Repository)

1. Laravel 中的存儲庫模式(Repository)2. 為什么要在 Laravel 中使用存儲庫模式(Repository)?

在大多數 web 應用程序中,訪問數據庫占了代碼庫的很大一部分。為了避免在我們應用程序邏輯上摻雜 sql 查詢,我們依賴抽象,它隱藏了 php 方法背后的數據訪問機制。

有幾種模式可以結構化數據訪問,“Active Record” 和 “Repository” 是最著名的兩種。在這篇博文中,我將在 Laravel 框架 的背景下具體解釋它們。關于使用 Repository 模式的優點和缺點的討論將在單獨的博客文章中進行。

活動記錄

默認情況下,Laravel 使用 Active Record 模式。每個 Laravel 程序員都直觀地使用它,因為它是在抽象的 Model 基類中實現的,而模型通常從它繼承而來。讓我們來看一個例子:

use IlluminateDatabaseEloquentModel;  /**  * @property int    $id  * @property string $first_name  * @property string $last_name  */ class Person extends Model { }  // --- 使用:  $person = new Person(); $person->first_name = 'Jack'; $person->last_name = 'Smith'; $person->save();

當然,您可以讀寫您在 Person 上創建的屬性。 但是要保存模型,您也可以 直接在模型上調用方法。 不需要另一個對象——模型已經提供了訪問相應數據庫表的所有方法。

這意味著,域模型將您的自定義屬性和方法與同一類中的所有數據訪問方法相結合。 第二部分是通過繼承 Model 來實現的。

要點:

  • Active Record 結合 域模型與數據訪問功能。
  • Laravel 使用 Active Record 模式并通過 Model 類實現它。

Repository

Repository 模式是 Active Record 模式的替代方案。它還提供了處理數據訪問的抽象。但更廣泛地說,它可以被視為域對象的概念性存儲庫或集合。

與活動記錄模式相反,存儲模式將數據庫訪問與域模型分離。它提供了一個高級接口,你可以在其中創建、讀取、更新和刪除域模型,而不必考慮實際的底層數據存儲。

底層的存儲庫可以通過構建和執行 SQL 查詢訪問數據庫,通過 REST API 訪問遠程系統,或者僅僅管理包含所有域模型的內存數據結構。這對測試很有用。存儲庫模式關鍵部分是它為其余代碼提供的高級接口。

要點:

  • 存儲庫表示域對象的概念集合。
  • 它只負責用高級接口封裝數據訪問。
  • Laravel 沒有提供實現存儲庫模式的特定幫助程序

在 Laravel 中實現 Repository 模式時,我主要看到兩種變體。

變體1:特定方法

在第一個變體中,存儲庫方法是重點和特定的。名稱解釋了調用者獲得的內容,用于參數化底層查詢的選項是有限的。

class InvoiceRepository {      public function findAllOverdue(Carbon $since, int $limit = 10): Collection {         return Invoice::where('overdue_since', '>=', $since)             ->limit($limit)             ->orderBy('overdue_since')             ->get();     }      public function findInvoicedToCompany(string $companyId): Collection {         return Invoice::where('company_id', $companyId)             ->orderByDesc('created_at')             ->get();     } }

這種方法的優勢在于方法的表現力。閱讀代碼時,很清楚從方法中期望什么以及如何調用它們。這會導致更少的錯誤。 Repository 方法很容易測試,因為參數有限。

這種方法的一個缺點是,最終可能會在存儲庫中使用大量的方法。由于方法無法輕松重用,因此必須為新用例添加其他方法。

要點:

  • 存儲模式可以通過提供特定方法的類來實現
  • 每個方法包裝一個查詢,只公開必要的參數
  • 優點:?可讀性和可測試性
  • 缺點:? 缺乏靈活性和較低的可重用性

變式2: 一般方法

另一方面的方法是提供一般的方法。這導致了方法的減少。但是這些方法有一個很大的 API 曲面,因為每個方法都可以使用不同的參數組合來調用。

其中的關鍵問題是參數表示。這種表示應該引導調用方理解方法簽名并避免無效的輸入。為此,您可以引入一個特殊的類,例如使用 Query Object 模式。

但是我在實踐中經常看到的是標量參數和 PHP 數組的混合。調用方可以傳遞完全無效的輸入,僅類型數據并不能說明要傳遞什么。但是如果使用得當,這種輕量級的方法可以避免更繁瑣的抽象。

class InvoiceRepository {      public function find(array $conditions, string $sortBy = 'id', string $sortOrder = 'asc', int $limit = 10): Collection {         return Invoice::where($conditions)             ->orderBy($sortBy, $sortOrder)             ->limit($limit)             ->get();     } }  // --- 使用:  $repo = new InvoiceRepository(); $repo->find(['overdue_since', '>=', $since], 'overdue_since', 'asc'); $repo->find(['company_id', '=', $companyId], 'created_at', 'asc', 100);

這種方法減輕了第一種方法的問題:你可以得到更少的 Repository 方法,這些方法更靈活,并且可以更頻繁地重用。

從消極的方面看,Repository 變得更加難以測試,因為有更多的案例需要覆蓋。方法簽名更難理解,正因為如此,調用者可能會犯更多錯誤。此外,還將引入某種查詢對象表示形式。無論它是顯式的還是隱式的(比如數組),您的 Repository 實現及其調用者都將與它耦合。

要點:

  • 存儲庫模式可以通過提供通用方法的類實現。
  • 難點在于方法參數的表示。
  • 優點:?更大的靈活性和更高的可復用性。
  • 缺點:?更難測試,可讀性差,與參數表示耦合。

當然,這兩種方法可以結合起來使用。也許你想要一些特定的方法用于復雜的查詢,而一些通用的方法用于簡單的 where 查詢。

實現

現在,我們來談談如何實現方法體。

在上面的例子中,我使用了?Model?類的方法來獲得對?Eloquent 查詢構造器的訪問。所以 Repository 的實現實際上使用了 Active Record 模式作為實現。

你不需要這樣做。你可以使用?DB?facade 來獲得一個查詢構建器,同時避免使用 Model 類。或者你可以直接編寫 SQL 查詢:

class InvoiceRepository {      public function findAllOverdue(Carbon $since, int $limit = 10): Collection {         return DB::table('invoices')             ->where('overdue_since', '>=', $since)             ->limit($limit)             ->orderBy('overdue_since')             ->get();     }      public function findInvoicedToCompany(string $companyId): Collection {         return DB::select('SELECT * FROM invoices                            WHERE company_id = ?                            ORDER BY created_at                            LIMIT 100', [$companyId]);     } }

存儲模式的優點是,實現可以是任何東西,只要它滿足接口。你還可以管理內存中的對象或者包(和緩存)一個 API。

但是最常見的是,底層數據存儲是一個 SQL 數據庫。要訪問它,你可以根據每個方法選擇最佳實現。對于性能關鍵的或者復雜的查詢,你可能希望直接使用 SQL 語句。更簡單的查詢可以使用 Eloquent 查詢生成器。

當你沒有使用 模型 類來實現你的 Repository ,你可能會考慮在模型中不繼承它。但是這個方法違反了很多內置的 Laravel 魔術方法,在我看來并不是一個好的方法。

要點:

  • 存儲庫模式很靈活,允許使用各種實現技術。
  • 在 Laravel 中,當訪問數據庫時,Eloquent 查詢構建器是一個實用的選擇。

接口

你的另一個選擇是,是否要引入一個接口。上面的例子可以用一個接口和一個或多個實現來分隔:

// --- 接口:  public interface InvoiceRepositoryInterface {      public function findAllOverdue(Carbon $since, int $limit = 10): Collection;      public function findInvoicedToCompany(string $companyId): Collection; }  // --- 具體的類,實現了該接口  class InvoiceRepository implements InvoiceRepositoryInterface {      public function findAllOverdue(Carbon $since, int $limit = 10): Collection {         // 實現     }      public function findInvoicedToCompany(string $companyId): Collection {         // 實現     } }

添加接口是一種額外的間接方法,并不一定是好的。如果您的應用程序是 Repository 的唯一用戶,并且您不希望它有多個實現,那么我不認為引入接口有什么意義。對于測試,Repository 可以用 PHPUnit 模擬,只要它不被標記為 final。

如果你知道你將有多個實現,你應該使用一個接口。如果你正在編寫一個將在多個項目中使用的 包。或者你想要測試一個特殊的 Repository 實現,那么可能會發生不同的實現。

為了從 Laravel 的依賴注入中獲益,你必須將具體的實現綁定到接口上。這必須在服務提供者的注冊方法中完成。

use IlluminateSupportServiceProvider;  class RepositoryServiceProvider extends ServiceProvider {      public function register(): void {         $this->app->bind(InvoiceRepositoryInterface::class, InvoiceRepository::class);     } }

要點:

  • 一個接口可以進一步?解耦?從代碼的其余部分獲取代碼庫。
  • ?當您期望有多個具體類實現它時,使用 Repository 接口。
  • 在 Laravel 中,將具體類綁定到服務提供者中的接口。

原文地址:https://dev.to/davidrjenni/repository-pattern-in-laravel-1pph

譯文地址:https://learnku.com/laravel/t/62587

? 版權聲明
THE END
喜歡就支持一下吧
點贊15 分享