浅析Laravel中存储库模式(Repository)的优点

为什么要在 laravel 中使用存储库模式(repository)?下面本篇文章给大家介绍一下使用存储库模式的优点,希望对大家有所帮助!

浅析Laravel中存储库模式(Repository)的优点

1. Laravel中的存储库模式2. 为什么要在 Laravel 中使用存储库模式(Repository)?

在之前的文章中,我解释了什么是存储库模式,它与Active Record模式有何不同,以及如何在Laravel中实现它。现在我想深入了解一下为什么应该使用存储库模式。

我在上一篇文章的评论中注意到,Repository模式在Laravel社区中是一个有争议的话题。有些人认为没有理由使用它,并坚持使用内置的Active Record模式。其他人则倾向于使用其他方法将数据访问从逻辑域中分离出来。请注意,我尊重这些意见,并将在接下来的博客文章中专门讨论此主题。

有了这个免责声明,让我们来了解一下使用存储库模式的优点。

单一责任原则

单一责任原则是主要鉴别器来区分Active Record模式和存储库模式。模型类已经保存数据并提供域对象的方法。当使用Active Record模式时,数据访问是额外引入的责任。这是我想在以下示例中说明的东西:

/** * @property string $first_name * @property int    $company_id */class Employee extends Model {}$jack = new Employee();$jack->first_name = 'Jack';$jack->company_id = $twitterId;$jack->save();

虽然域模型和数据访问技术的职责混合,但它直观上看还说得过去。在我们的应用程序中,员工必须以某种方式存储在数据库中,因此为什么不调用对象上的save()。单个对象被转化成单个数据行并存储。

但是,让我们更进一步,看看我们还能对员工做些什么:

$jack->where('first_name', 'John')->firstOrFail()->delete();$competition = $jack->where('company_id', $facebookId)->get();

现在,它变得不直观,甚至违背了我们的域模型。 为什么 Jack 会突然删除另一个甚至可能在不同公司工作的员工? 或者他为什么能把 Facebook 的员工拉过来?

当然,这个例子是人为设计的,但它仍然显示了 Active Record 模式如何不允许有意的域模型。 员工与所有员工列表之间的界限变得模糊。 您始终必须考虑该员工是被用作实际员工还是作为访问其他员工的机制。

仓库模式通过强制执行这个基本分区来解决这个问题。它的唯一用途是标识域对象的合集,而不是域对象的本身。

要点:

通过将所有域对象的集合与单个域对象分离, 仓库模式体现了单一责任原则

不要重复自己 (DRY)

一些项目将数据库查询洒遍了整个项目。下面是一个例子,我们从数据库中获取列表,并在 Blade 视图中显示他们。

class InvoiceController {    public function index(): View {        return view('invoices.index', [            'invoices' => Invoice::where('overdue_since', '>=', Carbon::now())                ->orderBy('overdue_since')                ->paginate()        ]);    }}

当这样的查询遍得更加复杂并且在多个地方使用时,考虑将其提取到 Repository 方法中。

存储库模式通过将重复查询打包到表达方法中来帮助减少重复查询。如果必须调整查询,只需更改一次即可。

class InvoiceController {    public __construct(private InvoiceRepository $repo) {}    public function index(): View {        return view('invoices.index', [            'invoices' => $repo->paginateOverdueInvoices()        ]);    }}

现在查询只实现一次,可以单独测试并在其他地方使用。此外,单一责任原则再次发挥作用,因为控制器不负责获取数据,而只负责处理HTTP请求和返回响应。

Takeaway:

? 存储库模式有助于减少重复查询

依赖反转

解释 Dependency Inversion Principle 值得发表自己的博客文章。我只是想说明存储库可以启用依赖项反转。

在对组件进行分层时,通常较高级别的组件依赖于较低级别的组件。 例如,控制器将依赖模型类从数据库中获取数据:

class InvoiceController {    public function index(int $companyId): View {        return view(            'invoices.index',            ['invoices' => Invoice::where('company_id', $companyId)->get()]        );    }}

依赖关系是自上而下的,紧密耦合的。 InvoiceController 取决于具体的 Invoice 类。 很难将这两个类解耦,例如单独测试它们或替换存储机制。 通过引入 Repository 接口,我们可以实现依赖倒置:

存了个图 存了个图

视频图片解析/字幕/剪辑,视频高清保存/图片源图提取

存了个图 17 查看详情 存了个图

interface InvoiceRepository {    public function findByCompanyId($companyId): Collection;}class InvoiceController {    public function __construct(private InvoiceRepository $repo) {}    public function index(int $companyId): View {        return view(            'invoices.index',            ['invoices' => $this->repo->findByCompanyId($companyId)]        );    }}class EloquentInvoiceRepository implements InvoiceRepository {    public function findByCompanyId($companyId): Collection {        // 使用 Eloquent 查询构造器实现该方法    }}

Controller 现在只依赖于 Repository 接口, 和 Repository 实现一样. 这两个类现在只依赖于一个抽象, 从而减少耦合. 正如我将在下一节中解释的那样,这会带来更多优势.

Takeaway:

? 存储库模式作为一种抽象类,支持依赖反转.

抽象类

存储库 提高了可读性 因为复杂的操作被具有表达性名称的高级方法隐藏了.

访问存储库的代码与底层数据访问技术分离. 如有必要,您可以切换实现,甚至可以省略实现,仅提供 Repository 接口。 这对于旨在与框架无关的库来说非常方便。

OAuth2 服务包 ——  league/oauth2-server 也用到这个抽象类机制。 Laravel Passport 也通过 实现这个库的接口 集成 league/oauth2-server 包。

正如 @bdelespierre 在 评论 里回应我之前的一篇博客文章时向我指出的那样,你不仅可以切换存储库实现,还可以将它们组合在一起。大致以他的示例为基础,您可以看到一个存储库如何包装另一个存储库以提供附加功能:

interface InvoiceRepository {    public function findById(int $id): Invoice;}class InvoiceCacheRepository implements InvoiceRepository {    public function __construct(        private InvoiceRepository $repo,        private int $ttlSeconds    ) {}    public function findById(int $id): Invoice {        return Cache::remember(            "invoice.$id",            $this->ttlSeconds,            fn(): Invoice => $this->repo->findById($id)        );    }}class EloquentInvoiceRepository implements InvoiceRepository {    public function findById(int $id): Invoice { /* 从数据库中取出 $id */ }}// --- 用法:$repo = new InvoiceCacheRepository(    new EloquentInvoiceRepository(););

要点:

? 存储库模式抽象了有关数据访问的详细信息。? 存储库将客户端与数据访问技术分离。? 这允许切换实现,提高可读性并实现可组合性。

可测试性

存储库模式提供的抽象也有助于测试。

如果你有一个 Repository 接口,你可以提供一个替代的测试实现。 您可以使用数组支持存储库,而不是访问数据库,将所有对象保存在数组中:

class InMemoryInvoiceRepository implements InvoiceRepositoryInterface {    private array $invoices;    // implement the methods by accessing $this->invoices...}// --- Test Case:$repo = new InMemoryInvoiceRepository();$service = new InvoiceService($repo);

通过这种方法,您将获得一个现实的实现,它速度很快并且在内存中运行。 但是您必须为测试提供正确的 Repository 实现,这 ** 本身可能需要大量工作**。 在我看来,这在两种情况下是合理的:

您正在开发一个(与框架无关的)库,它本身不提供存储库实现。

测试用例复杂,Repository 的状态很重要。

另一种方法是“模仿”,要使用这种技术,你不需要适当的接口。你可以模仿任何 non-final 类。
使用 PHPUnit API ,您可以明确规定如何调用存储库以及应该返回什么。

$companyId = 42;/** @var InvoiceRepository&MockObject */$repo = $this->createMock(InvoiceRepository::class);$repo->expects($this->once())    ->method('findInvoicedToCompany')    ->with($companyId)    ->willReturn(collect([ /* invoices to return in the test case */ ]));$service = new InvoiceService($repo);$result = $service->calculateAvgInvoiceAmount($companyId);$this->assertEquals(1337.42, $result);

有了 mock,测试用例就是一个适当的单元测试。上面示例中测试的唯一代码是服务。没有数据库访问,这使得测试用例的设置和运行非常快速

另外:

? 仓库模式允许进行适当的单元测试,这些单元测试运行快并且是隔离的

原文地址:https://dev.to/davidrjenni/why-use-the-repository-pattern-in-laravel-2j1m

译文地址:https://learnku.com/laravel/t/62521

【相关推荐:laravel视频教程】

以上就是浅析Laravel中存储库模式(Repository)的优点的详细内容,更多请关注php中文网其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/339988.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月5日 17:47:38
下一篇 2025年11月5日 17:52:14

相关推荐

  • HTMLrev 上的免费 HTML 网站模板

    HTMLrev 是唯一的人工策划的库专门专注于免费 HTML 模板,适用于由来自世界各地慷慨的模板创建者制作的网站、登陆页面、投资组合、博客、电子商务和管理仪表板世界。 这个人就是我自己 Devluc,我已经工作了 1 年多来构建、改进和更新这个很棒的免费资源。我自己就是一名模板制作者,所以我知道如…

    2025年12月24日
    300
  • 如何使用 Laravel 框架轻松整合微信支付与支付宝支付?

    如何通过 laravel 框架整合微信支付与支付宝支付 在 laravel 开发中,为电商网站或应用程序整合支付网关至关重要。其中,微信支付和支付宝是中国最流行的支付平台。本文将介绍如何使用 laravel 框架封装这两大支付平台。 一个简单有效的方法是使用业内认可的 easywechat lara…

    2025年12月24日
    000
  • Laravel 框架中如何无缝集成微信支付和支付宝支付?

    laravel 框架中微信支付和支付宝支付的封装 如何将微信支付和支付宝支付无缝集成到 laravel 框架中? 建议解决方案 考虑使用 easywechat 的 laravel 版本。easywechat 是一个成熟、维护良好的库,由腾讯官方人员开发,专为处理微信相关功能而设计。其 laravel…

    2025年12月24日
    300
  • 如何在 Laravel 框架中轻松集成微信支付和支付宝支付?

    如何用 laravel 框架集成微信支付和支付宝支付 问题:如何在 laravel 框架中集成微信支付和支付宝支付? 回答: 建议使用 easywechat 的 laravel 版,easywechat 是一个由腾讯工程师开发的高质量微信开放平台 sdk,已被广泛地应用于许多 laravel 项目中…

    2025年12月24日
    000
  • 使用Laravel框架如何整合微信支付和支付宝支付?

    使用 Laravel 框架整合微信支付和支付宝支付 在使用 Laravel 框架开发项目时,整合支付网关是常见的需求。对于微信支付和支付宝支付,推荐采用以下方法: 使用第三方库:EasyWeChat 的 Laravel 版本 建议直接使用现有的 EasyWeChat 的 Laravel 版本。该库由…

    2025年12月24日
    000
  • 如何将微信支付和支付宝支付无缝集成到 Laravel 框架中?

    如何简洁集成微信和支付宝支付到 Laravel 问题: 如何将微信支付和支付宝支付无缝集成到 Laravel 框架中? 答案: 强烈推荐使用流行的 Laravel 包 EasyWeChat,它由腾讯开发者维护。多年来,它一直保持更新,提供了一个稳定可靠的解决方案。 集成步骤: 安装 Laravel …

    2025年12月24日
    100
  • 网页设计css样式代码大全,快来收藏吧!

    减少很多不必要的代码,html+css可以很方便的进行网页的排版布局。小伙伴们收藏好哦~ 一.文本设置    1、font-size: 字号参数  2、font-style: 字体格式 3、font-weight: 字体粗细 4、颜色属性 立即学习“前端免费学习笔记(深入)”; color: 参数 …

    2025年12月24日
    000
  • css中id选择器和class选择器有何不同

    之前的文章《什么是CSS语法?详细介绍使用方法及规则》中带了解CSS语法使用方法及规则。下面本篇文章来带大家了解一下CSS中的id选择器与class选择器,介绍一下它们的区别,快来一起学习吧!! id选择器和class选择器介绍 CSS中对html元素的样式进行控制是通过CSS选择器来完成的,最常用…

    2025年12月24日
    000
  • php约瑟夫问题如何解决

    “约瑟夫环”是一个数学的应用问题:一群猴子排成一圈,按1,2,…,n依次编号。然后从第1只开始数,数到第m只,把它踢出圈,从它后面再开始数, 再数到第m只,在把它踢出去…,如此不停的进行下去, 直到最后只剩下一只猴子为止,那只猴子就叫做大王。要求编程模拟此过程,输入m、n, 输出最后那个大王的编号。…

    好文分享 2025年12月24日
    000
  • CSS新手整理的有关CSS使用技巧

    [导读]  1、不要使用过小的图片做背景平铺。这就是为何很多人都不用 1px 的原因,这才知晓。宽高 1px 的图片平铺出一个宽高 200px 的区域,需要 200*200=40, 000 次,占用资源。  2、无边框。推荐的写法是     1、不要使用过小的图片做背景平铺。这就是为何很多人都不用 …

    好文分享 2025年12月23日
    000
  • CSS中实现图片垂直居中方法详解

    [导读] 在曾经的 淘宝ued 招聘 中有这样一道题目:“使用纯css实现未知尺寸的图片(但高宽都小于200px)在200px的正方形容器中水平和垂直居中。”当然出题并不是随意,而是有其现实的原因,垂直居中是 淘宝 工作中最 在曾经的 淘宝UED 招聘 中有这样一道题目: “使用纯CSS实现未知尺寸…

    好文分享 2025年12月23日
    000
  • CSS派生选择器

    [导读] 派生选择器通过依据元素在其位置的上下文关系来定义样式,你可以使标记更加简洁。在 css1 中,通过这种方式来应用规则的选择器被称为上下文选择器 (contextual selectors),这是由于它们依赖于上下文关系来应 派生选择器 通过依据元素在其位置的上下文关系来定义样式,你可以使标…

    好文分享 2025年12月23日
    000
  • CSS 基础语法

    [导读] css 语法 css 规则由两个主要的部分构成:选择器,以及一条或多条声明。selector {declaration1; declaration2;     declarationn }选择器通常是您需要改变样式的 html 元素。每条声明由一个属性和一个 CSS 语法 CSS 规则由两…

    2025年12月23日
    300
  • CSS 高级语法

    [导读] 选择器的分组你可以对选择器进行分组,这样,被分组的选择器就可以分享相同的声明。用逗号将需要分组的选择器分开。在下面的例子中,我们对所有的标题元素进行了分组。所有的标题元素都是绿色的。h1,h2,h3,h4,h5 选择器的分组 你可以对选择器进行分组,这样,被分组的选择器就可以分享相同的声明…

    好文分享 2025年12月23日
    000
  • CSS id 选择器

    [导读] id 选择器id 选择器可以为标有特定 id 的 html 元素指定特定的样式。id 选择器以 ” ” 来定义。下面的两个 id 选择器,第一个可以定义元素的颜色为红色,第二个定义元素的颜色为绿色: red {color:re id 选择器 id 选择器可以为标有特…

    好文分享 2025年12月23日
    000
  • 有关css的绝对定位

    [导读] 定位(左边和顶部) css定位属性将是网虫们打开幸福之门的钥匙: h4 { position: absolute; left: 100px; top: 43px }这项css规则让浏览器将 的起始位置精 确地定在距离浏览器左边100象素,距离其 定位(左边和顶部) css定位属性将是网虫们…

    好文分享 2025年12月23日
    000
  • html5怎么加php_html5用Ajax与PHP后端交互实现数据传递【交互】

    HTML5不能直接运行PHP,需通过Ajax与PHP通信:前端用fetch发送请求,PHP接收处理并返回JSON,前端解析响应更新DOM;注意跨域、编码、CSRF防护和输入过滤。 HTML5 本身是前端标记语言,不能直接运行 PHP 代码,但可以通过 Ajax(异步 JavaScript)与 PHP…

    2025年12月23日
    300
  • 手机端怎么运行html文件_手机端运行html文件方法【教程】

    可通过手机浏览器、代码编辑器、本地服务器或在线工具四种方式预览HTML文件:一、用文件管理器打开HTML并选择浏览器即可渲染页面;二、使用Acode等编辑器导入文件后点击预览功能实时查看;三、对复杂项目可用KSWEB搭建本地服务器,将文件放入指定目录后通过http://127.0.0.1:8080访…

    2025年12月23日
    000
  • html5如何插入txt纯文本_html5txt文本嵌入与编码设置【实操】

    可通过iframe、fetch+pre、object标签或服务端预处理四种方式在HTML5中显示外部TXT文件,需重点处理字符编码(如UTF-8声明、BOM、响应头)并防范XSS风险。 如果您希望在HTML5页面中显示外部TXT纯文本文件的内容,浏览器默认不支持直接嵌入TXT文件为可渲染内容,必须通…

    2025年12月23日
    000
  • php如何html_在PHP代码中输出HTML内容【输出】

    必须确保PHP正确解析并输出原始HTML字符串而非转义文本;可通过echo/print直接输出、heredoc语法处理多行含变量HTML,或用PHP结束标签切换至纯HTML模式。 如果您在PHP脚本中需要将HTML代码作为响应内容发送给浏览器,则必须确保PHP正确解析并输出原始HTML字符串,而非将…

    2025年12月23日
    000

发表回复

登录后才能评论
关注微信