根据 Laravel belongsToMany 关系中的枢轴值检索每个关系的 1 个枢轴行
Retrieving 1 pivot row per relation based on pivot values in Laravel belongsToMany relation
背景 - 我正在创建一个系统,管理员可以在其中创建任意字段,然后将这些字段组合成一个表单。然后用户完成此表单,针对每个字段输入的值存储在 table 中。但是,我没有覆盖以前的值,而是计划将每个过去的值作为单独的行保存在 table 中。然后我希望能够显示每个表单提交的内容,但只显示最近提交的值。
问题
我有一个模型 Service
,它与另一个模型 Field
具有 belongsToMany 关系。这种关系定义为:
public function fields()
{
return $this->belongsToMany('App\Field')->withPivot('id', 'value', 'date')->withTimestamps();
}
中介 table 有 3 个值我想检索,id
、value
和 date
。
一个 Service
可能有 1 个或多个 Field
,并且对于每个字段,它也可能有超过 1 个数据透视行。也就是说,单个 Service
/Field
对可能在枢轴 table 中有多个具有不同枢轴值的条目。例如:
Table field_service
id | service_id | field_id | value | created_at
------------------------------------------------------
1 | 1 | 1 | lorem | 2018-02-01
2 | 1 | 1 | ipsum | 2018-01-01
3 | 1 | 1 | dolor | 2017-12-01
4 | 1 | 2 | est | 2018-03-10
5 | 1 | 2 | sicum | 2018-03-09
6 | 1 | 2 | hoci | 2018-03-08
我想要的是:
- 与
Service
或 关联的每个 Field
的枢轴 table 的特定行
- 与
Service
关联的每个 Field
来自枢轴 table 的特定 value
。
例如 - 在上面的 table 中,我希望 ID 为 1 的 Service
在关系中有 2 个 Field
,每个 Field
包含对应枢轴值的属性。附加的 Fields
将由具有最新日期的相应枢轴 table 条目指定。类似于:
$service->fields()[0]->value = "lorem"
$service->fields()[1]->value = "est"
我觉得那里有一个明显的“Laravel”解决方案,但它让我望而却步...
更新
有点难以置信,这是我不理解窗口函数的另一个例子。我问了一个 question 7 years ago 基本上就是这个问题,但是原始 MySQL。下面的原始 MySQL 基本上给了我想要的东西,我只是不知道如何 Laravel 使用它:
SELECT services.name, fields.name, field_service.value, field_service.created_at, field_service.field_id
FROM field_service
INNER JOIN
(SELECT field_id, max(created_at) as ts
FROM field_service
WHERE service_id = X
GROUP BY field_id) maxt
ON (field_service.field_id = maxt.field_id and field_service.created_at = maxt.ts)
JOIN fields ON fields.id = field_service.field_id
JOIN services ON services.id = field_service.service_id
试试这个:
public function fields()
{
$join = DB::table('field_service')
->select('field_id')->selectRaw('max(`created_at`) as `ts`')
->where('service_id', DB::raw($this->id))->groupBy('field_id');
$sql = '(' . $join->toSql() . ') `maxt`';
return $this->belongsToMany(Field::class)->withPivot('id', 'value', 'created_at')
->join(DB::raw($sql), function($join) {
$join->on('field_service.field_id', '=', 'maxt.field_id')
->on('field_service.created_at', '=', 'maxt.ts');
});
}
然后像这样使用它:
$service->fields[0]->pivot->value // "lorem"
$service->fields[1]->pivot->value // "est"
背景 - 我正在创建一个系统,管理员可以在其中创建任意字段,然后将这些字段组合成一个表单。然后用户完成此表单,针对每个字段输入的值存储在 table 中。但是,我没有覆盖以前的值,而是计划将每个过去的值作为单独的行保存在 table 中。然后我希望能够显示每个表单提交的内容,但只显示最近提交的值。
问题
我有一个模型 Service
,它与另一个模型 Field
具有 belongsToMany 关系。这种关系定义为:
public function fields()
{
return $this->belongsToMany('App\Field')->withPivot('id', 'value', 'date')->withTimestamps();
}
中介 table 有 3 个值我想检索,id
、value
和 date
。
一个 Service
可能有 1 个或多个 Field
,并且对于每个字段,它也可能有超过 1 个数据透视行。也就是说,单个 Service
/Field
对可能在枢轴 table 中有多个具有不同枢轴值的条目。例如:
Table field_service
id | service_id | field_id | value | created_at
------------------------------------------------------
1 | 1 | 1 | lorem | 2018-02-01
2 | 1 | 1 | ipsum | 2018-01-01
3 | 1 | 1 | dolor | 2017-12-01
4 | 1 | 2 | est | 2018-03-10
5 | 1 | 2 | sicum | 2018-03-09
6 | 1 | 2 | hoci | 2018-03-08
我想要的是:
- 与
Service
或 关联的每个 - 与
Service
关联的每个Field
来自枢轴 table 的特定value
。
Field
的枢轴 table 的特定行
例如 - 在上面的 table 中,我希望 ID 为 1 的 Service
在关系中有 2 个 Field
,每个 Field
包含对应枢轴值的属性。附加的 Fields
将由具有最新日期的相应枢轴 table 条目指定。类似于:
$service->fields()[0]->value = "lorem"
$service->fields()[1]->value = "est"
我觉得那里有一个明显的“Laravel”解决方案,但它让我望而却步...
更新
有点难以置信,这是我不理解窗口函数的另一个例子。我问了一个 question 7 years ago 基本上就是这个问题,但是原始 MySQL。下面的原始 MySQL 基本上给了我想要的东西,我只是不知道如何 Laravel 使用它:
SELECT services.name, fields.name, field_service.value, field_service.created_at, field_service.field_id
FROM field_service
INNER JOIN
(SELECT field_id, max(created_at) as ts
FROM field_service
WHERE service_id = X
GROUP BY field_id) maxt
ON (field_service.field_id = maxt.field_id and field_service.created_at = maxt.ts)
JOIN fields ON fields.id = field_service.field_id
JOIN services ON services.id = field_service.service_id
试试这个:
public function fields()
{
$join = DB::table('field_service')
->select('field_id')->selectRaw('max(`created_at`) as `ts`')
->where('service_id', DB::raw($this->id))->groupBy('field_id');
$sql = '(' . $join->toSql() . ') `maxt`';
return $this->belongsToMany(Field::class)->withPivot('id', 'value', 'created_at')
->join(DB::raw($sql), function($join) {
$join->on('field_service.field_id', '=', 'maxt.field_id')
->on('field_service.created_at', '=', 'maxt.ts');
});
}
然后像这样使用它:
$service->fields[0]->pivot->value // "lorem"
$service->fields[1]->pivot->value // "est"