The following tutorial column will introduce you to the The recently popular Laravel repository pattern (Repository) repository mode (Repository) by The recently popular Laravel repository pattern (Repository). I hope it will be helpful to everyone!
- 1. Repository Pattern in The recently popular Laravel repository pattern (Repository)
- 2. Why use the repository pattern in The recently popular Laravel repository pattern (Repository) ( Repository)?
In most web applications, accessing the database accounts for a large portion of the code base. To avoid cluttering our application logic with SQL queries, we rely on abstractions, which hide the data access mechanisms behind PHP methods.
There are several modes for structured data access, "Active Record" and "Repository" are the two most famous ones. In this blog post, I will explain them specifically in the context of the The recently popular Laravel repository pattern (Repository) framework. Discussion of the advantages and disadvantages of using the Repository pattern will be discussed in a separate blog post.
By default, The recently popular Laravel repository pattern (Repository) uses Active Record mode. Every The recently popular Laravel repository pattern (Repository) programmer uses it intuitively because it is implemented in the abstract Model base class, from which models usually inherit. Let's look at an example:
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();
Of course, you can read and write properties you create on Person
. But to save the model, you can also call the method directly on the model. There is no need for another object - the model already provides all the methods to access the corresponding database table.
This means that the domain model combines your custom properties and methods with all data access methods in the same class. The second part is achieved by inheriting Model
.
Key points:
Model
class. The Repository pattern is an alternative to the Active Record pattern. It also provides abstractions for handling data access. But more broadly, it can be viewed as a conceptual repository or collection of domain objects.
Contrary to active record mode, storage mode separates database access from the domain model. It provides a high-level interface where you can create, read, update, and delete domain models without having to worry about the actual underlying data store.
The underlying repository can access the database by building and executing SQL queries, access remote systems via the REST API
, or simply manage the in-memory data structures that contain all domain models. This is useful for testing. The key part of the repository pattern is the high-level interface it provides to the rest of the code.
Key points:
When it comes to implementing the Repository pattern in The recently popular Laravel repository pattern (Repository), I see mainly two variations.
In the first variation, the repository method is focused and specific. The name explains what the caller gets, and options for parameterizing the underlying query are limited.
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(); } }
The advantage of this method lies in the expressiveness of the method. When reading the code, it's clear what to expect from the methods and how to call them. This results in fewer errors. Repository methods are easy to test because they have limited parameters.
One disadvantage of this approach is that you may end up using a large number of methods in the repository. Since methods cannot be easily reused, additional methods must be added for new use cases.
Key points:
The method on the other hand provides a general method. This resulted in a reduction in methods. But these methods have a large API surface, since each method can be called with different combinations of parameters.
The key issue is parameter representation. This representation should guide callers in understanding method signatures and avoid invalid input. To do this, you can introduce a special class, for example using the Query Object pattern.
但是我在实践中经常看到的是标量参数和 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 ,你可能会考虑在模型中不继承它。但是这个方法违反了很多内置的 The recently popular Laravel repository pattern (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 实现,那么可能会发生不同的实现。
为了从 The recently popular Laravel repository pattern (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
The above is the detailed content of The recently popular Laravel repository pattern (Repository). For more information, please follow other related articles on the PHP Chinese website!