没有主键但在多个重叠字段上加载模型一对多关系 eloquent 方式

Load model one to many relation eloquent way without primary key but on multiple overlapping fields

我正在处理一个较旧的项目,我的任务是在我们进行完全重写的同时加快某些部分的速度,因为代码维护不善,编写不当且已过时去做。

我无意中遇到了项目核心的问题,因此我无法在不破坏几乎所有其他内容的情况下更改它。所以我需要以 eloquent 方式加载“关系”(使用 Planning:with('availability') 但没有真正的外国 ID,它包含多个字段。

有没有一种方法可以在具有重叠字段的一个查询中加载所有内容,而不是单独加载,从而造成 n+1 问题?

+--------------+-----------------+
| Planning     | Availability    |
+--------------+-----------------+
| planning_id  | availability_id |
| date         | date            |
| startHour    | startHour       |
| stopHour     | stopHour        |
| candidate_id | candidate_id    |
| section_id   | section_id      |
+--------------+-----------------+

从上面的例子中你可以看到重叠的字段是 date, startHour, stopHour, candidate_id and section_id.

我尝试了 get...attribute 但它仍然加载了 n+1,我尝试将它包含在 ->with(['availabilities']) 中但是这不起作用,因为我要求 模型而不是关系:

为更清晰起见进行编辑:

规划模型:

public function availabilities()
{
    return Availability::where('section_id', $this->section_id)
        ->where('candidate_id', $this->candidate_id)
        ->where('planningDate', $this->planningDate)
        ->where('startHour', $this->startHour)
        ->where('stopHour', $this->stopHour)
        ->get();
}

public function availabilities2()
{
    return $this->hasMany('App\Models\Availability', 'candidate_id', 'candidate_id')
}

控制器:

$plannings = Planning::with(['availabilities'])->get();

$plannings = Planning::with(['availabilities2' => function ($query) {
    // $this is suppose to be Planning model but doesn't work
    $query->where('section_id', $this->section_id)
        ->where('planningDate', $this->planningDate)
        ->where('startHour', $this->startHour)
        ->where('stopHour', $this->stopHour);

    // ---- OR ---- //
    // Don't have access to planning table here 
    $query->where('section_id', 'planning.section_id')
        ->where('planningDate', 'planning.planningDate')
        ->where('startHour', 'planning.startHour')
        ->where('stopHour', 'planning.stopHour');
}])->get();

当你想在 where() 方法中使用多个字段时,你最常在 where() 方法中插入一个数组: This document can help you

  • 将您的代码更改为:
return Availability::where([
    ['section_id', $this->section_id],
    ['candidate_id', $this->candidate_id],
    ['planningDate', $this->planningDate],
    ['startHour', $this->startHour],
    ['stopHour', $this->stopHour]
])->firstOrFail();

首先,为了能够加载我的关系,我选择了匹配的键之一,并选择了匹配最少的键,在我的例子中是 section_id.

所以在我的 Planning 模型上我有一个函数:

public function availabilities()
{
    return $this->hasMany('App\Models\Availability', 'section_id', 'section_id');
}

这样我就可以在需要时加载数据:Planning:with('availability')

然而,由于我有一些其他键需要匹配,所以我找到了一种通过向其添加子查询来限制这种关系的方法:

$planning = Planning::with([
    'availabilities' => function ($query) {
        $query->where('candidate_id', $this->candidate_id)
            ->where('startHour', $this->startHour)
            ->where('stopHour', $this->stopHour);
    },
    // Any other relations could be added here
    ])
    ->get();

这不是最好的方法,但这是我发现它不会获得太多额外数据的唯一方法,同时也尊重我的关系