当前位置: 首页 > 知识库问答 >
问题:

在Laravel中管理关系,遵循存储库模式

奚飞星
2023-03-14

在阅读了T. Otwell关于Laravel中良好设计模式的书后,我在Laravel 4中创建了一个应用程序,我发现自己为应用程序上的每个表创建了存储库。

我最终得到了以下表格结构:

  • 学生:身份证,姓名

我有用于所有这些表的find、create、update和delete方法的存储库类。每个存储库都有一个与数据库交互的雄辩模型。根据Laravel的文档在模型中定义了关系:http://laravel.com/docs/eloquent#relationships.

当创建新课程时,我所做的就是调用课程存储库上的创建方法。这门课有作业,所以在创建作业时,我还想为这门课的每个学生在分数表中创建一个条目。我通过分配存储库做到这一点。这意味着作业存储库与两个雄辩模型通信,即作业和学生模型。

我的问题是:由于这个应用程序可能会越来越大,并且会引入更多的关系,在存储库中与不同的有说服力的模型进行通信是一种良好的做法,还是应该使用其他存储库来实现这一点(我指的是从分配存储库调用其他存储库)还是应该在雄辩的模型中一起实现?

此外,使用分数表作为作业和学生之间的枢纽是一种良好的做法,还是应该在其他地方进行?

共有3个答案

劳烨
2023-03-14

我喜欢从我的代码在做什么和它负责什么的角度来考虑它,而不是“对或错”。这就是我如何分解我的责任:

  • 控制器是HTTP层,将请求路由到底层API(也称为控制流)

考虑到这一点,每次使用存储库都是有意义的(无论您是否创建接口等都是另一个主题)。我喜欢这种方法,因为它意味着当我需要做某些工作时,我确切地知道该去哪里。

我还倾向于构建一个基本存储库,通常是一个定义主要默认值(基本上是CRUD操作)的抽象类,然后每个子级都可以根据需要扩展和添加方法,或者重载默认值。注入模型也有助于此模式变得非常健壮。

秦禄
2023-03-14

记住你在征求意见: D

这是我的:

我做你经常做的事情,发现它很有效。

然而,我经常围绕业务逻辑组织存储库,而不是按表回购。这很有用,因为它是一个围绕应用程序应该如何解决“业务问题”的观点。

课程是一个“实体”,有属性(标题、id等),甚至还有其他实体(作业,有自己的属性,可能还有实体)。

您的“课程”存储库应该能够返回课程和课程属性/作业(包括作业)。

幸运的是,你可以用雄辩的口才来完成。

(我通常会在每个表中创建一个存储库,但有些存储库的使用量比其他存储库大得多,因此有更多的方法。例如,如果你的应用程序更多地围绕课程而不是课程的作业集合,那么你的“课程”存储库可能比你的作业存储库功能更全面)。

我经常使用存储库中的存储库来执行一些数据库操作。

任何实现Eloquent以处理数据的存储库都可能返回Eloquent模型。有鉴于此,如果您的课程模型使用内置关系来检索或保存作业(或任何其他用例),这是可以的。我们的“实施”是围绕雄辩建立的。

从实践的角度来看,这是有道理的。我们不太可能将数据源更改为雄辩者无法处理的(非sql数据源)。

至少对我来说,这种设置最棘手的部分是确定雄辩实际上是在帮助还是伤害我们。ORM是一个棘手的问题,因为虽然从实用的角度来看,它们对我们帮助很大,但它们也将您的“业务逻辑实体”代码与进行数据检索的代码结合起来。

这就混淆了存储库的责任到底是处理数据还是处理实体(业务域实体)的检索/更新。

此外,它们充当传递给视图的对象。如果您以后不得不避免在存储库中使用雄辩的模型,那么您需要确保传递给视图的变量的行为方式相同,或者具有相同的可用方法,否则更改数据源将导致更改视图,并且您(部分)已经首先失去了将逻辑抽象到存储库的目的——项目的可维护性随着时间的推移而降低。

无论如何,这些想法有些不完整。如前所述,它们只是我的观点,这恰好是去年阅读领域驱动设计并观看Ruby Midwest的“bob叔叔”主题演讲等视频的结果。

钱欣悦
2023-03-14

我正在使用Laravel 4完成一个大型项目,必须回答您现在提出的所有问题。在阅读了Leanpub上所有可用的Laravel书籍和大量的Google搜索之后,我想出了以下结构。

  1. 每个数据表一个雄辩的模型类
  2. 每个雄辩模型一个存储库类
  3. 可以在多个存储库类之间通信的服务类

假设我正在建立一个电影数据库。我至少会有以下雄辩的模型课程:

  • 电影
  • 演播室
  • 导演
  • 演员
  • 复习

存储库类将封装每个雄辩模型类,并负责数据库上的CRUD操作。仓库类可能看起来像这样:

  • 电影故事

每个存储库类将扩展一个BaseRepository类,该类实现以下html" target="_blank">接口:

interface BaseRepositoryInterface
{
    public function errors();

    public function all(array $related = null);

    public function get($id, array $related = null);

    public function getWhere($column, $value, array $related = null);

    public function getRecent($limit, array $related = null);

    public function create(array $data);

    public function update(array $data);

    public function delete($id);

    public function deleteWhere($column, $value);
}

服务类用于将多个存储库粘合在一起,并包含应用程序的真正“业务逻辑”。控制器仅与服务类通信以执行创建、更新和删除操作。

因此,当我想在数据库中创建新的电影记录时,我的MovieController类可能有以下方法:

public function __construct(MovieRepositoryInterface $movieRepository, MovieServiceInterface $movieService)
{
    $this->movieRepository = $movieRepository;
    $this->movieService = $movieService;
}

public function postCreate()
{
    if( ! $this->movieService->create(Input::all()))
    {
        return Redirect::back()->withErrors($this->movieService->errors())->withInput();
    }

    // New movie was saved successfully. Do whatever you need to do here.
}

由您决定如何将数据发布到控制器,但假设postCreate()方法中的Input::all()返回的数据如下所示:

$data = array(
    'movie' => array(
        'title'    => 'Iron Eagle',
        'year'     => '1986',
        'synopsis' => 'When Doug\'s father, an Air Force Pilot, is shot down by MiGs belonging to a radical Middle Eastern state, no one seems able to get him out. Doug finds Chappy, an Air Force Colonel who is intrigued by the idea of sending in two fighters piloted by himself and Doug to rescue Doug\'s father after bombing the MiG base.'
    ),
    'actors' => array(
        0 => 'Louis Gossett Jr.',
        1 => 'Jason Gedrick',
        2 => 'Larry B. Scott'
    ),
    'director' => 'Sidney J. Furie',
    'studio' => 'TriStar Pictures'
)

由于MoviRepository不应该知道如何在数据库中创建Actor、导演或Studio记录,我们将使用我们的MoviService类,它可能看起来像这样:

public function __construct(MovieRepositoryInterface $movieRepository, ActorRepositoryInterface $actorRepository, DirectorRepositoryInterface $directorRepository, StudioRepositoryInterface $studioRepository)
{
    $this->movieRepository = $movieRepository;
    $this->actorRepository = $actorRepository;
    $this->directorRepository = $directorRepository;
    $this->studioRepository = $studioRepository;
}

public function create(array $input)
{
    $movieData    = $input['movie'];
    $actorsData   = $input['actors'];
    $directorData = $input['director'];
    $studioData   = $input['studio'];

    // In a more complete example you would probably want to implement database transactions and perform input validation using the Laravel Validator class here.

    // Create the new movie record
    $movie = $this->movieRepository->create($movieData);

    // Create the new actor records and associate them with the movie record
    foreach($actors as $actor)
    {
        $actorModel = $this->actorRepository->create($actor);
        $movie->actors()->save($actorModel);
    }

    // Create the director record and associate it with the movie record
    $director = $this->directorRepository->create($directorData);
    $director->movies()->associate($movie);

    // Create the studio record and associate it with the movie record
    $studio = $this->studioRepository->create($studioData);
    $studio->movies()->associate($movie);

    // Assume everything worked. In the real world you'll need to implement checks.
    return true;
}

所以我们剩下的是一个很好的、明智的关注点分离。存储库只知道它们从数据库中插入和检索的雄辩模型。控制器不关心存储库,他们只是传递他们从用户那里收集的数据,并将其传递给适当的服务。服务不关心它接收的数据是如何保存到数据库中的,它只是将控制器提供的相关数据传递给适当的存储库。

 类似资料:
  • 第二个存储区是。它的责任是管理随机选择的TODO项目。在我看来,应该使用通过访问TODO项。 问题在于,如前所述,在启动时不会加载TODO项。 问题是:“如何保证已经检索了TODO项?”

  • 我正在尝试学习存储库模式,似乎有点困惑,当我急于加载关系并将db逻辑排除在控制器之外时,如何使用此存储库模式。 快速概述我的存储库/应用程序结构。 示例ProductInterface。php 示例类别接口。php 好的,最简单的部分是使用DI向控制器注入模型依赖关系。 列出与相关产品的所有类别更加困难,因为我不再使用雄辩的模型。我正在使用一个界面,它没有暴露所有雄辩的方法。 如果我没有在我的El

  • 将现有存储库数据库迁移到其他数据库可能包括迁移到同一服务器中的其他数据库,或迁移到不同操作系统上的数据库(例如,从 Windows 到 macOS)。 如果你想迁移你的存储库数据库,请按照下列步骤操作: 通过运行命令,或使用通知区域或菜单栏中的图标来停止 Navicat Monitor 服务。 在你的数据库管理工具中,将当前的存储库数据库(所有表的结构和数据)复制到新的存储库数据库。 启动 Nav

  • 问题内容: 我想将分层的二维科学数据集存储在关系数据库(MySQL或SQLite)中。每个数据集都包含一个数值数据表,其中包含任意数量的列。另外,每个数据集可以具有一个或多个与其表的给定行关联的相同类型的子级。每个数据集通常具有1至100列和1至1.000.000行。数据库应该能够处理许多数据集(> 1000),并且数据的读/写应该相当快。 存储此类数据的最佳数据库模式将是什么?是否有一个“主”表

  • 问题内容: 我已经在django 1.4中从主干尝试过prefetch_related(),但无法使其预取反向查找。 我的简化模型(每本书有很多价格): 我视图的查询: 然后,我得到了AttributeError消息: 如何使其运作?谢谢。 问题答案: 定义一个相关名称: 然后使用它: