浅析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:50:09
如何使用Composer解决Symfony项目中的密码强度验证问题
下一篇 2025年11月5日 17:50:21

相关推荐

  • composer require-dev和require有什么不同_Composer Require与Require-Dev区别解析

    require用于声明项目运行必需的依赖,如框架、数据库组件和第三方SDK,这些包会随项目部署到生产环境;2. require-dev用于声明仅在开发和测试阶段需要的工具,如PHPUnit、PHPStan、Faker等,不会默认部署到生产环境;3. 安装时composer install根据环境决定…

    2026年5月10日
    1000
  • 开源免费PHP工具 PHP开发效率提升利器

    推荐开源免费PHP开发工具以提升效率:VS Code、Sublime Text轻量高效,PhpStorm专业强大;调试用Xdebug、Kint、Ray;依赖管理选Composer;代码质量工具包括PHPStan、Psalm、PHP_CodeSniffer;数据库管理可用%ignore_a_1%MyA…

    2026年5月10日
    000
  • 怎么在PHP代码中实现图片上传功能_PHP图片上传功能实现与安全处理教程

    首先创建含enctype的HTML表单,再用PHP接收文件,检查目录、移动临时文件,验证类型与大小,生成唯一文件名,并调整php.ini限制以确保上传成功。 如果您尝试在PHP项目中添加图片上传功能,但服务器无法正确接收或保存文件,则可能是由于表单配置、文件处理逻辑或安全限制的问题。以下是实现该功能…

    2026年5月10日
    100
  • 获取日期中的周数:CodeIgniter 教程

    本教程旨在帮助开发者在 CodeIgniter 框架中,从日期字符串中准确提取周数。我们将使用 PHP 内置的 DateTime 类,并提供详细的代码示例和注意事项,确保您能够轻松地在项目中实现此功能。 使用 DateTime 类获取周数 PHP 的 DateTime 类提供了一种便捷的方式来处理日…

    2026年5月10日
    100
  • php常量怎么用_PHP常量(define/const)定义与使用方法

    PHP中可通过define函数和const关键字定义常量,用于存储不可变值。define适用于全局作用域,支持动态名称和条件定义,如define(‘SITE_NAME’, ‘MyWebsite’);const在编译时生效,语法简洁但限制多,只能在类或全…

    2026年5月10日
    000
  • PHP动态生成表单输入与POST数据获取实践指南

    本教程详细阐述了如何在php中根据动态数据源(如数据库值)生成多个表单输入框,并演示了如何通过post方法准确无误地获取这些动态生成的输入值。文章强调了正确的输入框命名策略,避免了常见的命名误区,并提供了完整的代码示例,确保开发者能够高效处理动态表单数据。 动态生成表单输入 在Web开发中,我们经常…

    2026年5月10日
    000
  • JavaScript函数中插入加载动画(Spinner)的正确方法

    本文旨在解决在JavaScript函数中插入加载动画(Spinner)时遇到的异步问题。通过引入async/await和Promise.all,确保在数据处理完成前后正确显示和隐藏加载动画,提升用户体验。我们将提供两种实现方案,并详细解释其原理和优势。 在Web开发中,当执行耗时操作时,显示加载动画…

    2026年5月10日
    100
  • PHP多维数组到复杂XML结构的SOAP序列化实践

    本文旨在解决php多维数组向复杂soap xml结构序列化时遇到的“无法序列化结果”问题。通过深入理解soap xml的结构要求,包括命名空间和类型属性,文章将指导您如何构建符合特定xml schema的php关联数组。我们将利用`spatie/array-to-xml`库,详细演示其安装与使用方法…

    2026年5月10日
    000
  • 使用 Ajax 和 FormData 实现文件上传及文本数据提交的完整教程

    本文旨在解决在使用 Ajax 和 FormData 进行文件上传时,遇到的 $_POST 和 $_FILES 为空的问题。通过详细的代码示例和解释,我们将展示如何正确地构建 FormData 对象,并通过 Ajax 将文件和文本数据发送到服务器端,同时避免常见的错误配置,确保数据能够成功地被 PHP…

    2026年5月10日
    000
  • 虫虫漫画直接进入官网入口_虫虫漫画网页版清爽版

    虫虫漫画直接进入官网入口_虫虫漫画网页版清爽版虫虫漫画直接进入官网入口_虫虫漫画网页版清爽版虫虫漫画直接进入官网入口_虫虫漫画网页版清爽版虫虫漫画直接进入官网入口_虫虫漫画网页版清爽版

    虫虫漫画官网入口为www.ccmh.com,用户可直接通过浏览器访问,支持多端适配与账号同步功能,界面简洁无广告,提供海量国漫、日漫、韩漫资源,涵盖恋爱、玄幻等热门题材,更新及时,支持多种阅读模式及离线缓存,阅读体验流畅。 虫虫漫画直接进入官网入口在哪里?这是不少网友都关注的,接下来由PHP小编为大…

    2026年5月10日 用户投稿
    100
  • 从 JavaScript 获取 URL 并在 PHP DataGrid 中使用

    本文档旨在指导开发者如何从 JavaScript 函数中获取 URL,并将其动态应用于 PHP DataGrid。通过前端 JavaScript 动态生成 API 地址,并将其传递给后端的 PHP DataGrid,实现数据根据用户会话动态加载。 动态配置 DataGrid 的 URL 在构建动态 …

    2026年5月10日
    000
  • CodeIgniter在IIS环境下实现URL重写与index.php移除指南

    本教程详细指导如何在IIS服务器上部署的CodeIgniter应用中,移除URL中不必要的index.php。核心解决方案涉及修改CodeIgniter的config.php文件,将$config[‘index_page’]设置为空,并辅以正确的IIS web.config重…

    2026年5月10日
    100
  • PHP安全文件下载:防止直链与保护资源

    本文旨在解决通过检查元素获取直链下载文件的问题,并提供一种安全的PHP服务器端文件交付方案。核心思想是利用PHP作为文件代理,通过设置HTTP响应头直接将文件发送给用户,从而隐藏文件的实际存储路径,有效防止未经授权的直接链接访问。 客户端下载链接的风险与局限性 在构建下载页面时,开发者常常面临一个挑…

    2026年5月10日
    100
  • php超过字数怎么解密_用PHP分段处理超字数加密数据并解密教程【技巧】

    分段解密超长加密数据需先确定算法限制,再通过OpenSSL扩展支持,编写函数逐段解密并拼接结果。1、明确加密算法与密钥对应的分段大小;2、启用php.ini中openssl扩展并重启服务;3、自定义函数读取私钥、base64解码密文、循环截取块解密;4、确保去除密文换行符并按原加密块大小切分;5、解…

    2026年5月10日
    000
  • Python中如何实现过滤器模式?

    在Python中实现过滤器模式的过程中,我们可以利用Python的灵活性来创建一个既简单又强大的过滤系统。让我们从回答这个问题开始:Python中如何实现过滤器模式? 在Python中,过滤器模式可以通过定义一系列的过滤器类来实现,这些类能够根据特定条件对对象进行过滤。Python的函数式编程特性,…

    2026年5月10日
    100
  • php代码如何操作JSON数据_php代码解析和生成JSON的方法

    答案:PHP中处理JSON需使用json_encode()和json_decode()函数。1、将数组转为JSON字符串时,用json_encode()并检查返回值是否为false;2、解析JSON字符串时,调用json_decode()并设第二参数为true返回数组,false则返回对象;3、处理…

    2026年5月10日
    000
  • 深入理解 Laravel Session::put:避免常见陷阱与实现表单限流

    本文旨在深入探讨 laravel 框架中 `session::put` 方法的正确用法及其常见误区。针对用户在实现表单提交限流时遇到的问题,详细阐述了 `session::put` 必须提供键值对的原理,并提供了如何在控制器中利用会话机制有效防止重复提交的实战代码示例。通过本文,读者将掌握 lara…

    2026年5月10日
    000
  • PHP代码注入检测日志分析_PHP代码注入日志检测方法详解

    答案:日志分析是发现PHP代码注入的关键手段,主要通过Web服务器访问日志、PHP错误日志、PHP-FPM日志及应用自定义日志等多源数据,结合grep、ELK、WAF等工具识别含eval()、system()、Base64编码、目录遍历等特征的异常请求,并建立基线、设置检测规则与自动化告警,配合事件…

    2026年5月10日
    000
  • Voyager 中关联关系的翻译问题解决方案

    本文档旨在解决在使用 TCGVoyager 管理后台时,关联模型无法正确翻译的问题。主要针对 Laravel 项目中,使用 Voyager 1.4 版本以及 Laravel 8.0 版本,并且已经配置多语言支持的情况下,如何确保关联关系中的可翻译字段能够根据当前应用语言环境进行正确翻译。通过修改 B…

    2026年5月10日
    000
  • PHP中通过键名高效关联与输出多维数组数据

    本教程旨在解决php开发中常见的数据关联与输出问题,特别是当需要将不同数组中通过共同键名关联的数据进行整合展示时。文章将详细阐述如何利用foreach循环的键值对特性,结合array_key_exists函数,实现从多个数组中提取并组合相关信息,从而避免不必要的嵌套循环,提升代码的清晰度和执行效率。…

    2026年5月10日
    000

发表回复

登录后才能评论
关注微信