ollieread/laravel-repositories

此包已被弃用且不再维护。未建议替代包。

一个存在的包,用来展示仓库不必过于复杂

dev-master 2018-08-28 17:50 UTC

This package is auto-updated.

Last update: 2020-02-22 18:55:20 UTC


README

Latest Stable Version Total Downloads Latest Unstable Version License

仓库不需要复杂,这个包就是为了证明这一点。

常见问题解答

在具体介绍之前,这里有一个简单的常见问题解答。

拥有仓库的意义是什么?

仓库模式存在于在数据库/数据源交互和代码库逻辑之间提供一个抽象层。

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')