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

Posted

技术标签:

【中文标题】在没有主键但在多个重叠字段上加载模型一对多关系的雄辩方式【英文标题】:Load model one to many relation eloquent way without primary key but on multiple overlapping fields 【发布时间】:2021-10-24 10:07:04 【问题描述】:

我正在处理一个较旧的项目,我的任务是在我们进行完全重写的同时加快某些部分的速度,因为代码维护得很差,写得不好,而且对于它应该做的事情来说已经过时了.

我偶然发现了一个项目核心问题,因此我无法在不破坏几乎所有其他内容的情况下更改它。所以我需要以雄辩的方式加载“关系”(使用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 和 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();

【问题讨论】:

【参考方案1】:

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

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

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();

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

【讨论】:

【参考方案2】:

当您想在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();

【讨论】:

这不是我的意思,它应该被称为我的计划模型中的一个关系,但为了更清楚,我会编辑我的问题

以上是关于在没有主键但在多个重叠字段上加载模型一对多关系的雄辩方式的主要内容,如果未能解决你的问题,请参考以下文章

mybatis一对一关联关系映射

实验课程之基础

与附加字段的一对多关系

Hibernate的表之间的关系

Hibernate映射

Laravel 混合获取 Eloquent 急切加载嵌套多个模型