Получить последний из отношений, где есть условие - PullRequest
0 голосов
/ 30 ноября 2018

У меня есть две таблицы с отношением один ко многим.Моя задача довольно проста, мне нужно для каждого родительского последнего элемента из таблицы отношений, где применяется условие.

Первая таблица - это статус:

| id | partner_id | status | created_at          |
|----|------------|--------|---------------------|
| 1  | 1          | 1      | 2018-01-01 00:00:00 |
| 2  | 1          | 5      | 2018-02-01 00:00:00 |
| 3  | 2          | 1      | 2018-01-01 00:00:00 |

Отношение

public function partner()
{
    return $this->belongsTo(Partner::class, 'partner_id');
}

Второе - партнеры:

| id | name      |
|----|-----------|
| 1  | partner_1 |
| 2  | partner_2 |

Отношение:

public function status()
{
    return $this->hasMany(Status::class, 'partner_id')->orderBy('id', 'desc');
}

Допустим, я хочу получить всех партнеров, где их последний статус равен 1. Я попробовал это:

Partner::with('status')
    ->whereHas('status', function ($query) use ($status) {
          $query->where('status', $status);
       })
    ->get();

Результат таков:

"select * from `partners` where exists (select * from `status` where `partners`.`id` = `status`.`partner_id` and `status` = 1)"

Но ся также получу partner_1, даже если его последний статус равен 5, но в истории он имел статус 1.

Я создал SQL-запрос, чтобы получить это, но я думаю, что сделать такую ​​простую задачу довольно сложно.И также я думаю, что должен быть какой-то простой подход Laravel:

select p.id, ps.status, ps.newest_status
from partners as p
INNER JOIN (SELECT a.created_at AS newest_status, a.partner_id, a.status
              FROM status as a
                INNER JOIN (
                  SELECT max(created_at) as newest_status, partner_id
                FROM status
                GROUP BY partner_id
              ) as b ON a.partner_id = b.partner_id AND a.created_at = b.newest_status
) ps ON p.id = ps.partner_id and ps.status = 1
GROUP BY p.id

Моя цель - получить только partner_2, потому что только он имеет последний статус 1.

Ответы [ 2 ]

0 голосов
/ 30 ноября 2018

Вы можете использовать это:

Partner::select('partners.*')
    ->join('status', 'partners.id', 'status.partner_id')
    ->where('status.status', $status)
    ->where('status.id', function($query) {
        $query->select('id')
            ->from('status')
            ->whereColumn('partner_id', 'partners.id')
            ->orderByDesc('id')
            ->limit(1);
    })->get();
0 голосов
/ 30 ноября 2018

Один из способов сделать это - настроить метод отношений так, чтобы он включал в себя некоторый порядок (как предложено @ yrv16)

Но для этого нужно использовать подзапросы, если вы хотите повысить эффективность.Очень хорошая статья Дж. Рейнинка объясняет это приятно.

Короче, что-то вроде этого:

class Partner extends Model {

    // ...

    public function scopeWithLastStatus($query)
    {
        $query->addSubSelect('last_status', Status::select('status')->latest());
    }

    // ...

Это добавит атрибут 'virtual' last_status к вашей модели.

Тогда вы можете сделать что-то вроде этого:

Partner::withLastStatus()->get()->where('last_status', 'idle');
...