如何使用一个查询将 Laravel 模型与多个条件进行匹配

问题描述 投票:0回答:1

我正在构建一个 Laravel 应用程序,我可以在其中跟踪公司内销售代理赚取的佣金。每次进行销售时,都会在分数数据库表(分数模型)中创建一条记录。因为佣金的计算方式可能会不时发生变化,所以我还有一个数据库表,其中保存开始日期和结束日期以及相关的佣金。表格看起来像这样:

分数

id 得分
1 2024-01-01 00:00:00
2 2024-02-01 00:00:00
3 2024-02-15 00:00:00
4 2023-12-01 00:00:00

佣金规则

id 开始 结束
1 2024-01-01 00:00:00
2 2024-02-01 00:00:00 2024-02-14 00:00:00
3 2024-02-01 00:00:00

出于高效的原因,我想运行一个查询,该查询为我提供一个包含 Scores.id 和 Commission_rules.id 的数组。现在,我通过循环所有分数来创建数组,并对每个分数的commission_rules 表运行查询以查找关联的规则。但由于分数表有 10k+ 条目,因此需要相当长的时间。如果一个分数有多个匹配的佣金规则,我想返回具有最高 id 的规则,如果没有找到规则,我想返回 null。

所以数组应该看起来像这样:

[
  [ 'id' => 1, 'commission_rule_id' => 1],
  [ 'id' => 2, 'commission_rule_id' => 3],
  [ 'id' => 3, 'commission_rule_id' => 3],
  [ 'id' => 4, 'commission_rule_id' => null]
]

我根据分数运行的 Eloquent 查询如下所示:

ValueRule::query()
  ->where('start', '<=', $score->scored_at)
  ->where(function ($query) use ($score) {
    $query->where('end', '>=', $score->scored_at)
      ->orWhere('end', '=', null);
            })
  ->orderBy('id', 'DESC')
  ->first();

我已经尝试过使用左连接,但问题是这会为找到的每个匹配项提供一行。

mysql laravel eloquent
1个回答
0
投票

您可以通过子查询达到您想要的结果:

Score::query()
  ->select('id')
  ->addSelect([
    'commission_rule_id' => ValueRule::query()
       ->whereColumn('commission_rules.start', '<=', 'scores.scored_at')
       ->where(function ($query) {
         $query->where('commission_rules.end', '>=', 'scores.scored_at')
           ->orWhereNull('commission_rules.end');
       })
       ->latest()
       ->limit(1)
   ])
   ->get();

在此之上,您可以更进一步,根据计算出的“commission_rule_id”字段创建动态关系。

class Score extends Model
{
  public function currentCommisionRule()
  {
    return $this->belongsTo(ValueRule::class, 'commission_rule_id');
  }

  public function scopeAddCurrentCommisionRuleField(Builder $query, string $as = 'commission_rule_id')
  {
     if (empty($query->getQuery()->columns) {
       $query->select(); // default * selection
     }
     
     $query->addSelect([
       $as => ValueRule::query()
         ->whereColumn('commission_rules.start', '<=', 'scores.scored_at')
         ->where(function ($query) {
           $query->where('commission_rules.end', '>=', 'scores.scored_at')
             ->orWhereNull('commission_rules.end');
         })
         ->latest()
         ->limit(1)
     ]);
  }

现在您可以像任何其他关系一样立即加载此动态关系或查询,模型上不存在“commission_rule_id”的事实隐藏在我们的自定义范围后面。

Score::query()
  ->with(['currentCommisionRule'])
  ->addCurrentCommisionRuleField()
  ->get()
© www.soinside.com 2019 - 2024. All rights reserved.