在哪里放置与 Eloquent 无关的 SQL 查询

Posted

技术标签:

【中文标题】在哪里放置与 Eloquent 无关的 SQL 查询【英文标题】:Where to put not Eloquent related SQL queries 【发布时间】:2014-06-30 19:09:05 【问题描述】:

我使用 Eloquent 来实现我的模型。它有很多方法可以更轻松地处理模型。但是,在某些情况下,我必须实现访问数据库但不返回 Eloquent 模型的功能。

例如,想象一个常见的用户模型。 Eloquent 在 User 模型的 CRUD 操作中帮助了我很多,但是如果我必须实现一个方法来返回一些关于用户的统计信息(例如,有多少总用户、多少活跃用户等)。

我应该在哪里实现这个功能?在 Eloquent 模型中,作为public static 函数,(例如User::getStats()),或者应该为此设置不同的类。

在 Eloquent 模型上使用这些方法似乎有点不自然,因为它们与 Eloquent 无关。

【问题讨论】:

我在这种情况下使用存储库。存储库在构造函数中采用 User 模型的实例。然后我只使用 UserRepository,而不是直接使用 User。 您的存储库中有 sql 查询,对吧? 【参考方案1】:

这完全取决于您,但最好决定约定并坚持下去。

我的经验是这样的:

    如果它与通常由 Eloquent 模型表示的单个项目相关,则将其作为该模型上的公共静态方法执行。例如,在我当前的项目中,我有一个 Contract 模型。我需要一种可以为新合同生成合同 ID 字符串(为了与遗留系统兼容)的方法。这与单个项目(合同)有关,但出于技术原因需要与 Eloquent 模型分开生成(即它基于具有不同连接的单独数据库)。所以我创建了一个公共静态方法:public static function generateContractIdentifier($id, $salesRep)。如果您使用存储库来访问您的数据库,则可以将其放在那里。

    如果它更通用(即不绑定到 Eloquent 模型的实例),我会将其放入单独的库中。例如,在同一个应用程序中,我有一个处理打印项目的队列(工业过程)。该应用程序有一个仪表板,我需要显示当前队列状态以进行管理。为此,我创建了一个ProcessStatus 库并实现了一个方法:public function getStatus(),它运行一个查询并将结果作为数组提供以在视图中显示。这与队列中的任何特定项目无关,因此我将其放在单独的库中。

【讨论】:

【参考方案2】:

与往常一样,这取决于您的项目以及用户在其中扮演的角色。

但基本上,不,我认为构建报告的逻辑不属于用户模型。虽然它可能与用户有关,但关于 SOLID 原则,User 类应该只有一个职责,在这种情况下是处理 User 实体。

这包含获取和设置实例的属性,在一个简单的项目中,在模型上定义一些范围也可能很好,例如只选择活跃用户,例如User::getActive();

但是随着项目的增长,您应该考虑使用更具体的类。

例如,您可以将 Eloquent 功能抽象到用户存储库中。所以现在你有一个处理实体本身的操作的处理程序,比如

$userRepo->getAll();
$userRepo->getActive();
$userRepo->getInactive();

和一个用户实例的处理程序:

$user->getName();
$user->setStatus();

创建报告和统计数据是一个完全不同的主题。所以你可以有类似 UserReportBuilderUserStatisticsService 的东西:

$userStats->getMostActive();
$userStats->getRegistrationsPerDay();

一个简单的例子:

// 用户存储库:

class UserRepository


  protected $model = $model;

  public function __construct($model)
  
     // you pass in an instance of the actual Eloquent model
     // so you have the whole power of eloquent in here
     $this->model = $model;
  

  public function getActive()
  
     // this returns a collection of models
     return $this->model->where('status', 'active')->get();
  



$userRepo = new UserRepo(new User);

差不多就是这样。你仍然可以使用 Eloquent,但是你已经将功能分离为具有明确责任的部分。因此,您的 UserStats 类只能用于构建用户统计信息:

class UserStats

    // You could pass in the Repository through the constructor
    // or just use the Eloquent model directly

    public function getRegistrationsPerDay()
    
        return User::groupBy('day')->get(
            [
                 DB::raw('DATE(created_at) as day'),
                 DB::raw('count(*) as registration_count')
            ]
        );
    

User 实例或 UserStats-builder 不需要知道如何获取所有用户,并且 User 实例或 UserRepository 不需要知道如何计算每天的注册量,因此将该功能拆分为单独的、独立的部分只做一件事。

我想你明白了,我希望它是有道理的。也许您应该让自己更加熟悉SOLID-principles,并在遇到此类问题时尽量记住它们。

【讨论】:

很棒的答案。实际上为我回答了更多问题。

以上是关于在哪里放置与 Eloquent 无关的 SQL 查询的主要内容,如果未能解决你的问题,请参考以下文章

SQL JOIN 在哪里放置 WHERE 条件?

将 Eloquent 模型与业务逻辑分离

我可以在哪里放置 SQL 代码以在 AWS Data Pipeline 中删除表?

我应该在哪里放置包含我在laravel项目层级文件夹中的数据库播种器中使用的记录的.sql文件

如何在laravel eloquent中为左连接表起别名

PHP使用组件构建自己的PHP框架使用 Eloquent 模型进行数据库操作