ollieread / laravel-repositories
一个存在的包,用来展示仓库不必过于复杂
Requires
- php: ^7.1
- laravel/framework: ^5.6
This package is auto-updated.
Last update: 2020-02-22 18:55:20 UTC
README
- Laravel: 5.6
- PHP: 7.1+
- 作者: Ollie Read
- 作者主页: https://ollieread.com
仓库不需要复杂,这个包就是为了证明这一点。
常见问题解答
在具体介绍之前,这里有一个简单的常见问题解答。
拥有仓库的意义是什么?
仓库模式存在于在数据库/数据源交互和代码库逻辑之间提供一个抽象层。
Eloquent 是否已经涵盖了这一点?
是的,也不是。Eloquent 使用了 Active Record 模式,因此存在一些单一关注点危机。Eloquent 可以做你在仓库中通常会做的事情,但这也意味着查询没有存储在一个地方。它们无处不在。
到处都是查询有什么问题?
你有一个模型,你在 15 个地方查询这个模型。其中 13 个地方需要额外的条件。你现在需要挖掘出每个查询并更新它们。你不能添加全局范围,因为并不是所有东西都需要这个。我保证,大多数情况下你会错过一个。
但是我为什么要使用仓库呢?
仓库会将所有数据库/数据源交互集中到一个中央位置。这意味着随着你的代码库增长,你知道确切的去处,并且可以轻松地了解模型与数据库/数据源的整个交互。
我不想定义大量的接口
很好,你不需要这么做。遵循 YAGNI。如果你真正需要更换数据库/数据源,有一堆接口根本帮不上忙。当然,你可能认为它会,但相信我,它不会。此外,你根本不可能这么做。
但是我的仓库会很大
它不应该很大。仓库的一个重要作用是帮助遵循 DRY 原则。如果你有 15 个几乎相同的查询,你可能只需要一个方法,一个或多个参数,排序。
如果我公开了所有 Eloquent 功能,比如关系,仓库不就变成了 Eloquent 吗?
是的,会发生这种情况。这就是为什么你不应该这么做。在仓库设计模式的原始定义中,使用了一个叫做 Specification
的东西来帮助添加上下文和构建查询。在这个包中,我使用了 Criteria
并提供了一些基本的用于使用。
例如,你可以有一个 PostRepository::getPosts()
方法。如果你想获取特定用户的帖子,你可以添加一个条件对象 $repository->with(new ForUser($user))->getPosts()
,也许你只想获取特定类别的帖子? $repository->with(new ForCategory($category))->getPosts()
。这样一来,你编写的代码更少,过程更简单,易于理解。这些条件也可以由任何与用户或类别有关系的其他东西使用。
我仍然不认同
这并不适用于每个人。最终,是否使用这个包或这个设计模式的选择在于你。我只要求你客观地看待它,并尝试一下。如果你认为这只会让事情更复杂,你总会找到不喜欢它的理由。
安装
非常简单,非常有趣。在你的终端中运行以下命令。
composer require ollieread/laravel-repositories
配置
目前还没有配置,但将来可能会有。
使用
要使用这个包,只需创建自己的仓库,比如 App\Repositories\PostRepository
,并扩展基本仓库;
Ollieread\Repositories\Repository
然后你定义 model()
方法以返回该仓库表示的模型的完全限定名。
protected function model(): string { return Post::class; }
这就完成了,这是一个完全功能的仓库。我喜欢添加文档块来提示父返回类型。
<?php namespace App\Repositories; use Ollieread\Repositories\Repository; /* * @method Post make(array $arguments = []) * @method null|Post first(array $arguments = []) */ class PostRepository extends Repository { protected function model(): string { return Post::class; } }
现在你可以访问基本仓库提供的一切。
通常,你会有一个针对每个模型的仓库。当然,你可以只使用其中几个,但通常你应该保持你的代码库一致性。
获取所有模型
Repository::get(array $arguments = []): Collection
这和 Model::get()
或 Model::all()
一样,但你可以提供一个包含 column => value
条目的数组,用于简单的 =
条件。
获取第一个模型
Repository::first(array $arguments = []): ?Model
这和 Model::first()
一样,但就像上面一样,你可以传递一个数组作为 where 条件。
获取分页结果
Repository::paginate(array $arguments = [], int $count = 20, string $pageName = 'page', int $page = 1, array $columns = ['*']): LengthAwarePaginator
上面的代码和 Model::paginate()
一样,但带有 where 条件数组。
Repository::simplePaginate(array $arguments = [], int $count = 20, string $pageName = 'page', int $page = 1, array $columns = ['*']): Paginator
上面的代码和 Model::simplePaginate()
一样,但带有 where 条件数组。
查询
如果你想创建自己的查询,有几个选择。
只是一个查询对象
Repository::query()
这将返回一个新的 Eloquent 查询构建器给你。注意,这不会运行任何当前排队中的条件。
包含一些简单 where 条件的查询对象
Repository::builderQuery(array $arguments = [])
传递一个包含 column => value
对的数组以添加简单的 where 条件。如果值是一个闭包,它将被传递查询对象;如果值是一个数组,它将运行一个 whereIn()
。所有默认辅助方法都使用此,所以这种结构也适用于那些。
这将也会运行任何排队中的条件。
获取我自己的查询实例
如果您想使用自己的查询实例,可以直接使用以下方法;
Repository::make(array $attributes = [])
这等同于 $model = new Model($attributes)
。从这里您可以通过调用许多方法之一来获取一个新的查询;
$model->newQuery()
$model->newModelQuery()
$model->newQueryWithoutRelationships()
甚至可以直接从 Model
对象开始构建查询,但这会显得比较杂乱!
条件
条件是向查询添加额外...嗯...条件的一种方式,而无需在您的存储库内部定义大量方法。
所有条件都应该扩展基本类;
Ollieread\Repositories\Criteria
并且它们都应该定义一个 perform($query)
方法。
/** * @param \Illuminate\Database\Eloquent\Builder $query */ public function perform($query) { // db stuff here }
要向查询添加条件,您可以在存储库上使用 with()
方法;
$repository->with(new WithTrashed)->paginate([], 20);
添加条件将将其添加到当前存储库实例,因此如果您需要调用不包含此条件的方法,您可以刷新它;
$repository->flushCriteria()
如果您想保留条件但只是禁用它,可以这样做;
$repository->noCriteria()->paginate([], 20);
如果您想再次启用条件,可以这样做;
$repository->useCriteria()->paginate([], 20);
通常,您会在想要添加一个子句(例如,可能只是一个 where,但不是 =
子句)的情况下使用条件;
条件可以是通用的,也可以是具体的,没有规则。如果您真的希望,可以使用上面 FAQ 中的示例。
请记住,尽管条件了解数据库,但您使用条件时不需要了解数据库。一个例子是 new For('user', $user)
。这里您必须了解用户关系及其名称。更好的方法是 new ForUser($user)
。
提供了一些默认条件以供参考。
预加载关系
new Ollieread\Repositories\Criteria\WithRelations('relation1', 'relation2')
这等同于使用 Model::with()
。
包含软删除模型
new Ollieread\Repositories\Criteria\WithTrashed
这仅适用于具有软删除特质的模型,等同于 Model::withTrashed()
。
按创建时间列排序
new Ollieread\Repositories\Criteria\OrderedByCreation(bool $descending = true)
这等同于 Model::orderBy('created_at')
。
按更新时间列排序
new Ollieread\Repositories\Criteria\OrderedByModification(bool $descending = true)
这等同于 Model::orderBy('updated_at')
。