以下由最近很火的Laravel存儲庫模式(Repository)教學專欄帶大家推薦介紹關於最近很火的Laravel存儲庫模式(Repository)儲存庫模式(Repository),希望對大家有幫助!
- 1. 最近很火的Laravel存儲庫模式(Repository) 中的儲存庫模式(Repository)
- 2. 為什麼要在最近很火的Laravel存儲庫模式(Repository) 中使用儲存庫模式( Repository)?
在大多數 web 應用程式中,存取資料庫佔了程式碼庫的很大一部分。為了避免在我們應用程式邏輯上摻雜 SQL 查詢,我們依賴抽象,它隱藏了 PHP 方法背後的資料存取機制。
有幾種模式可以結構化資料訪問,“Active Record” 和 “Repository” 是最著名的兩種。在這篇文章中,我將在 最近很火的Laravel存儲庫模式(Repository) 框架 的背景下具體解釋它們。關於使用 Repository 模式的優點和缺點的討論將在單獨的部落格文章中進行。
預設情況下,最近很火的Laravel存儲庫模式(Repository) 使用 Active Record 模式。每個 最近很火的Laravel存儲庫模式(Repository) 程式設計師都直觀地使用它,因為它是在抽象的 Model 基底類別中實現的,而模型通常從它繼承而來。讓我們來看一個範例:
use Illuminate\Database\Eloquent\Model; /** * @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
來實現的。
重點:
Model
類別實作它。 Repository 模式是 Active Record 模式的替代方案。它還提供了處理資料存取的抽象。 但更廣泛地說,它可以被視為域物件的概念性儲存庫或集合。
與活動記錄模式相反,儲存模式將資料庫存取與域模型分開。它提供了一個高級接口,你可以在其中建立、讀取、更新和刪除域模型,而不必考慮實際的底層資料儲存。
底層的儲存庫可以透過建構和執行 SQL 查詢存取資料庫,透過 REST API
存取遠端系統,或僅管理包含所有領域模型的記憶體資料結構。這對測試很有用。存儲庫模式關鍵部分是它為其餘程式碼提供的高級介面。
要點:
在 最近很火的Laravel存儲庫模式(Repository) 中實作 Repository 模式時,我主要看到兩種變體。
在第一個變體中,儲存庫方法是重點和特定的。名稱解釋了呼叫者獲得的內容,用於參數化底層查詢的選項是有限的。
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 方法很容易測試,因為參數有限。
這種方法的一個缺點是,最終可能會在儲存庫中使用大量的方法。由於方法無法輕鬆重複使用,因此必須為新用例新增其他方法。
要點:
另一方面的方法是提供一般的方法。這導致了方法的減少。 但是這些方法有一個很大的 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存儲庫模式(Repository) 魔术方法,在我看来并不是一个好的方法。
要点:
你的另一个选择是,是否要引入一个接口。上面的例子可以用一个接口和一个或多个实现来分隔:
// --- 接口: 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存儲庫模式(Repository) 的依赖注入中获益,你必须将具体的实现绑定到接口上。这必须在服务提供者的注册方法中完成。
use Illuminate\Support\ServiceProvider; class RepositoryServiceProvider extends ServiceProvider { public function register(): void { $this->app->bind(InvoiceRepositoryInterface::class, InvoiceRepository::class); } }
要点:
原文地址:https://dev.to/davidrjenni/repository-pattern-in-laravel-1pph
译文地址:https://learnku.com/laravel/t/62587
以上是最近很火的Laravel存儲庫模式(Repository)的詳細內容。更多資訊請關注PHP中文網其他相關文章!