Получение 1 сводной строки для каждого отношения на основе значений сводных данных в отношении Laravel belongsToMany

Фон. Я создаю систему, в которой администраторы могут создавать произвольные поля, которые затем объединяются в форму. Затем пользователи заполняют эту форму, и значения, введенные для каждого поля, сохраняются в таблице. Однако вместо того, чтобы перезаписывать предыдущее значение, я планирую сохранить каждое прошлое значение в виде отдельных строк в таблице. Затем я хочу иметь возможность отображать содержимое, отправленное в каждой форме, но только последнее отправленное значение.

Проблема

У меня есть модель Service, которая имеет отношение «принадлежит многим» с другой моделью Field. Эти отношения определяются как:

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

В промежуточной таблице есть 3 значения, которые я хочу получить: id, value и date.

Service может иметь 1 или более Field, и для каждого поля он также может иметь более 1 сводной строки. То есть одна пара _9 _ / _ 10_ может иметь несколько записей в сводной таблице с разными значениями сводной таблицы. Например:

Таблица 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.

Например, в приведенной выше таблице я хотел бы, чтобы Service с идентификатором 1 имел 2 Field во взаимосвязи, причем каждый Field содержал атрибут для соответствующего значения поворота. Присоединенный Fields будет указан соответствующей записью сводной таблицы с самой последней датой. Что-то вроде:

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

Я чувствую, что существует очевидное решение Laravelly, но оно ускользает от меня ...

Обновить

Несколько невероятно, что это еще один случай, когда я не понимаю оконных функций. Я задал вопрос 7 лет назад, это в основном проблема, но с исходным 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

person codinghands    schedule 22.03.2018    source источник
comment
Вы имеете в виду 2 поля вместо 3?   -  person Jonas Staudenmeir    schedule 23.03.2018
comment
Посмотрите, что вы имеете в виду - это был всего лишь пример - здесь произвольное количество полей   -  person codinghands    schedule 23.03.2018


Ответы (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"
person Jonas Staudenmeir    schedule 23.03.2018
comment
Спасибо за ответ, Джонас. Это почти готово - однако внутренняя таблица не фильтруется по service_id, поэтому она возвращает последнее значение поля, введенное для любой службы (в отличие от конкретной вызываемой службы. Мне нужно добавить предложение where во внутреннюю таблицу field_service запрос, но я получаю параметр со знаком вопроса в конце запроса, когда добавляю где в запрос $ join - person codinghands; 25.03.2018
comment
Добавление: $join->where('field_service.service_id', '=', DB::raw($this->id)) и $join->where('field_service.status', '=', DB::raw("'published'")) сделали свое дело - просто нужно было найти метод DB :: raw! - person codinghands; 25.03.2018