根据Laravel中的透视值检索每个关系的1个轴对行属于所有关系

时间:2018-03-22 22:57:05

标签: laravel laravel-5 laravel-5.6

背景 - 我正在创建一个系统,管理员可以在其中创建任意字段,然后将其合并到表单中。然后,用户填写此表单,并将针对每个字段输入的值存储在表中。但是,我不打算覆盖以前的值,而是计划将每个过去的值保存为表中的各个行。然后,我希望能够显示每个表单中提交的内容,但只显示最近提交的值。

问题

我有一个模型Service,它与另一个模型Field具有belongsToMany关系。这种关系定义为:

public function fields()
{
    return $this->belongsToMany('App\Field')->withPivot('id', 'value', 'date')->withTimestamps();
}

中间表有3个我想要检索的值,idvaluedate

Service可能有1个或多个Field个,对于每个字段,它也可能有超过1个数据透视表行。也就是说,单个Service / Field配对可能在数据透视表中具有多个具有不同数据透视值的条目。例如:

表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

我想要的是:

  • Field相关联的每个Service的数据透视表中的特定行,或
  • value相关联的每个Field的数据透视表中的特定Service

例如 - 在上表中,我希望ID为1的Service在关系中有2个Field s,每个Field包含相应数据透视的属性值。附加的Fields将由具有最新日期的相应数据透视表条目指定。类似于:

$service->fields()[0]->value = "lorem"
$service->fields()[1]->value = "est"

我觉得那里有一个明显的Laravel解决方案,但它让我无法实现......

更新

有些令人难以置信的是,这是我不理解窗口函数的另一个例子。我问question 7 years ago这基本上是这个问题,但是使用原始MySQL。以下原始的MySQL基本上给了我我想要的东西,我只是不知道如何Laravelise它:

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

1 个答案:

答案 0 :(得分:1)

试试这个:

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"