【问题标题】:What is the usage of repositories and interfaces in Laravel?Laravel 中存储库和接口的用途是什么?
【发布时间】:2017-08-28 07:49:16
【问题描述】:

在使用 Codeigniter 开发了 2 年的几个项目之后,我开始学习 Laravel。

我下载了一些项目来了解它们是如何编码的。据我了解,他们中的许多人只使用与 Codeigniter 相同的模型、视图和控制器。

但有一个项目使用了存储库和接口。真的很难理解那个项目发生了什么。那么 Laravel 中的仓库和接口有什么用呢?我应该什么时候使用它们?

【问题讨论】:

  • 接口和/或存储库有不同的使用要求,它们并不特定于 Laravel。这是一个架构决策,你应该在你的团队中做出这样的决定,如果你有的话。除此之外,在 Laravel 中,如果您打算继续使用 Eloquent,即 Laravel ORM,则不应实施存储库。
  • 我个人不同意存储库的事情。 Repositories 甚至抽象了您使用 Eloquent 的方式,使控制器超轻量级、高度可维护且易于理解,如果您与中型团队一起工作,对于非后端开发人员来说也是如此。例如,存储库还可以促进多版本 API 的处理。

标签: php laravel laravel-5 interface repository


【解决方案1】:

我会尽量解释清楚这两个概念。

接口\合同

一般来说,OOP 接口用于描述实现该接口的类提供哪些方法/功能而不关心实际的实现

Laravel 使用Contracts 主要是为了将服务 与实际实现分开。为了更清楚,让我们举个例子

<?php

namespace App\Orders;

class OrdersCache
{

    protected $cache;

    public function __construct(\SomePackage\Cache\Memcached $cache)
    {
        $this->cache = $cache;
    }


    public function find($id)
    {
        if ($this->cache->has($id))    {
            //
        }
    }
}

正如您在此类中看到的,代码与缓存实现紧密耦合(即\SomePackage\Cache\Memcached),因此如果该缓存类的 API 发生更改,我们的代码也必须相应更改。如果我们想用另一个(例如redis)更改缓存实现,也会发生同样的事情。

我们的代码可能依赖于与实现无关的接口,而不是这样做:

<?php

namespace App\Orders;

use Illuminate\Contracts\Cache\Repository as Cache;

class OrdersCache
{

    public function __construct(Cache $cache)
    {
        $this->cache = $cache;
    }


    public function find($id)
    {
        if ($this->cache->has($id))    {
            //
        }
    }
}

现在我们的代码没有与任何特定的实现耦合,因为Cache 实际上是一个接口。所以基本上在我们的类中,我们需要一个类的实例表现 就像Cache 接口中描述的那样,但我们对它在内部的工作方式并不真正感兴趣。这样做,如果我们想更改缓存实现,我们可以编写一个实现接口Cache 的类,而无需更改OrdersCache 类中的任何代码行。这样做我们的代码更容易理解和维护,并且你的包更易于重用。有关更多示例,请参阅 Laravel 文档中的 Loose Coupling 部分。

接口和服务容器

Laravel 的主要功能之一是它的Service Container,它用于管理依赖和执行依赖注入。请查看 Laravel 文档中的 Service Container 定义。

依赖注入也被 Laravel 广泛用于将接口绑定到实现。举个例子吧:

$app->bind('App\Contracts\EventPusher', 'App\Services\RedisEventPusher');

让我们的班级成为

<?php

namespace App\Http\Controllers;

use App\Contracts\EventPusher;

class EventsController extends Controller 
{

    protected $pusher;

    public function __construct(EventPusher $pusher) 
    {

        $this->pusher = $pusher;        

    }

}

没有声明任何其他内容,我们基本上是在说每次有人需要 EventPusher 实例时,请 Laravel 提供 RedisEventPusher 类的实例。在这种情况下,每次实例化您的控制器时,Laravel 都会将 RedisEventPusher 的实例传递给您的控制器,而无需指定任何其他内容。

您可以通过查看 Laravel 文档中的 Binding Interfaces to Implementation 部分来深入了解。

存储库

存储库是适用于 MVC 模式的概念,独立于任何特定框架。通常,您的模型是 数据层(例如,直接与数据库交互),控制器处理 数据层 的访问逻辑,视图显示控制器提供的数据。

存储库可以定义如下:

简单来说,存储库模式是一种存储数据访问逻辑的容器。它从业务逻辑中隐藏了数据访问逻辑的细节。换句话说,我们允许业务逻辑在不了解底层数据访问架构的情况下访问数据对象。
Sorucehttps://bosnadev.com/2015/03/07/using-repository-pattern-in-laravel-5

要了解如何在 Laravel 中使用它们,请查看great article

就是这样,我希望它能帮助你理清思路。

【讨论】:

    【解决方案2】:

    接口是任何实现类都应该调用的。

    interface CanFlyInterface
    {
        public function fly();
    }
    

    把它想象成编程,不用理会逻辑。

    if ($object instanceof CanFlyInterface) {
        $obj->fly();
    }
    

    现在我们可以传递一个 Bird 对象或 Airplane 对象! PHP DOESN'T CARE,只要它实现了接口!

    class Bird implements CanFlyInterface
    {
        public function fly()
        {
            return 'flap flap!';
        }
    }
    
    class Aeroplane implements CanFlyInterface
    {
        public function fly()
        {
            return 'roar! whoosh!';
        }
    }
    

    你的另一个问题,什么是 Repository 类。它只是一个将所有数据库查询保存在一个地方的类。以这个接口为例:

    interface RepositoryInterface
    {
        public function insert(array $data);
        public function update(array $data);
        public function findById($id);
        public function deleteById($id);
    }
    

    希望这可以为您解决问题!祝你所有的 PHP 编码好运:-D

    【讨论】:

    • 很遗憾,我仍然不明白接口背后的原因。为什么我们需要额外的文件,即接口,其方法与 Bird 和 Airplane 类具有相同的方法。我们不能只是没有接口,而且它少了一个文件。该方法在接口中基本上是空的,那么为什么没有没有接口的 Bird 和 Aeroplance 类呢?
    • 好吧,在上面的例子中,if ($object instanceof CanFlyInterface) { $object-&gt;fly() },我们不需要检查它是哪个类,我们知道它是一个有fly()方法的类,我们知道它会工作。想象一个保存任何数组的Saveable 接口。现在您可以保存到 db 或 json 文件,或者进行 api 调用,我们不在乎!只要该类实现了该接口,它将具有该方法:-)
    【解决方案3】:

    让我们从更简单的界面开始:

    您通常使用接口来实现具有所需方法的类: http://php.net/manual/en/language.oop5.interfaces.php

    Laravel 的 Contracts 是一组定义框架提供的核心服务的接口。例如,Illuminate\Contracts\Queue\Queue 契约定义了排队作业所需的方法,而 Illuminate\Contracts\Mail\Mailer 契约定义了发送电子邮件所需的方法。 https://laravel.com/docs/5.4/contracts#introduction

    当 Laravel 运行时,它可以检查一个类是否实现了一个特殊的接口:

    if ($cls instanceof IInterface) {
        $cls->interfaceFunction();
    }
    

    由于 Laravel 能够使用队列,它会通过检查退出接口来检查事件是否应该排队。

    要通知 Laravel 应该广播给定事件,请在事件类上实现 Illuminate\Contracts\Broadcasting\ShouldBroadcast 接口。 https://laravel.com/docs/5.4/broadcasting#defining-broadcast-events

    存储库:

    我没有找到太多关于这个的:

    我们的存储库不应该对谁向他们提供数据或他们如何提供数据有太多了解。 https://laravel.com/docs/5.4/contracts#loose-coupling

    但我在网页上找到了一些其他信息:

    存储库将工厂与网关连接起来 https://code.tutsplus.com/tutorials/the-repository-design-pattern--net-35804

    该链接将为您提供有关详细信息的更多信息。

    希望能帮到你:)

    【讨论】:

      【解决方案4】:

      首先,在大型应用程序中使用 Repository 和 Interface 不仅有利于 Laravel,而且有利于编码标准以及关注点分离的所有技术。

      根据微软 (我在这里找到了最好的解释)

      为什么要使用存储库:

      使用存储库来分离检索数据的逻辑和 将其从作用于 模型。业务逻辑应该与数据类型无关 包括数据源层。存储库在 数据源层和应用程序的业务层。它 查询数据的数据源,从数据中映射数据 源到业务实体,并在业务中持续更改 实体到数据源。
      存储库分离业务逻辑 来自与底层数据源或 Web 服务的交互。 数据层和业务层之间的分离具有三个好处: 它集中了数据逻辑或 Web 服务访问逻辑。它提供 单元测试的替换点。它提供了一个灵活的 架构,可以适应作为整体设计 应用程序发展。仓库可以通过两种方式查询 商业实体。它可以向客户端提交一个查询对象 业务逻辑,或者它可以使用指定业务的方法 标准。在后一种情况下,存储库在 代表客户。存储库返回一组匹配的实体 满足查询。

      对于Interface,上面你有很多答案,希望你能理解。

      【讨论】:

        【解决方案5】:

        首先,存储库和接口并非特定于 Laravel,而是大多数语言中的通用编码标准。

        如果您不介意花几美元,下面的 Laracasts 视频将有助于了解基础知识。

        https://laracasts.com/lessons/repositories-and-inheritance

        https://laracasts.com/series/object-oriented-bootcamp-in-php

        【讨论】:

          猜你喜欢
          • 2011-05-21
          • 1970-01-01
          • 2010-12-31
          • 2014-09-13
          • 1970-01-01
          • 1970-01-01
          • 2012-07-13
          • 2011-03-23
          相关资源
          最近更新 更多