Вложенные отношения фильтра Laravel

Итак, у меня есть три модели: Волонтер, Задача и Оплата. У волонтера может быть много задач (у него много отношений), а у задачи может быть много (у другого много взаимосвязей) выплат.

class Volunteer

public function tasks()
{
    return $this->hasMany(Task::class);
}

class Task

  public function volunteer()
{
    return $this->belongsTo(Volunteer::class);
}

 public function payments()
{
    return $this->hasMany(Payment::class);
}

class Payment 

public function task() {
    return $this->belongsTo(Task::class);
}

Теперь я хочу опросить всех добровольцев с неоплачиваемыми / частично оплачиваемыми задачами. Итак, в основном я хочу отфильтровать задачи волонтера, где сумма каждой задачи должна равняться сумме всех платежей, связанных с этой конкретной задачей.

Я пробовал использовать whereHas и with, но, похоже, не могу правильно фильтровать задачи.

Мне удалось сделать это с объединениями, но мне было интересно, возможно ли это с помощью whereHas или с. Ниже приведен код:

Volunteer::select('volunteers.id', 'volunteers.name', 'tasks.amount', DB::raw('SUM(payments.amount) as amount_paid'))
        ->join('tasks', 'tasks.volunteer_id', '=', 'volunteers.id')
        ->leftJoin('payments', 'payments.task_id', '=', 'tasks.id')
        ->groupBy('volunteers.id', 'volunteers.name', 'tasks.amount')
        ->havingRaw('amount_paid >= tasks.amount')
        ->get();

Любая помощь будет оценена по достоинству!


person Muhammad Kazim Sadiq    schedule 02.08.2020    source источник


Ответы (2)


Я хотел бы предложить что-то еще, что добавляет столбец в tasks таблицу, который указывает, является ли задача [оплаченной, неоплаченной или частично оплаченной] в вашей tasks миграции, например

$table->unsignedTinyInteger('paid_status')->default(0); // 0 -> unpaid, 1 -> partially paid, 2 -> paid

затем каждый раз, когда волонтер производит платеж, вы будете выполнять простую проверку tasks.paid_status, чтобы обновить что-то вроде проверки общего paid_amount и задания amount

затем используя Laravel hasManyThrough в модели Volunteer, вот так

public function payments()
{
   return $this->hasManyThrough(
       'App\Payment',
       'App\Task'
  );
}

теперь, чтобы получить свои данные, вы сделаете это

// unpaid tasks
Volunteer::query()->first()->payments()->where('tasks.paid_status', '0')->get();
// partially paid tasks
Volunteer::query()->first()->payments()->where('tasks.paid_status', '1')->get();
// paid tasks
Volunteer::query()->first()->payments()->where('tasks.paid_status', '2')->get();

вы можете узнать больше о HasManyThrough здесь

person mmabdelgawad    schedule 02.08.2020
comment
Хорошая идея. Просто интересно, не сломает ли это динамическую базу данных? Как и в случае, я бы жестко закодировал статус платежа, а не вычислял его на лету. Некоторые советы были бы полезны. - person Muhammad Kazim Sadiq; 02.08.2020
comment
Нет, я не думаю, что это помешает вашей базе данных быть динамической, на самом деле это хорошая практика, говоря о статусе платежа, вы можете делать PaymentObserver каждый раз, когда совершается новый платеж, вы будете проверять tasks.amount на $task->payments()->sum('amount'), а затем обновлять tasks.paid_status, который полностью динамичным способом. - person mmabdelgawad; 02.08.2020
comment
Потрясающий. Большое тебе спасибо! - person Muhammad Kazim Sadiq; 02.08.2020

Вы также можете справиться с этим, используя способности красноречия. Расширяет модель задачи с помощью метода локальной области

class Task extends Model
{
    public function volunteer()
    {
        return $this->belongsTo(Volunteer::class);
    }

    public function payments()
    {
        return $this->hasMany(Payment::class);
    }

    public function scopeIncompletePayments($query)
    {
        return $query->select('tasks.*')->leftJoin('payments', 'tasks.id', '=', 'payments.task_id')
            ->having('tasks.amount', '>', DB::raw('SUM(payments.amount)'))
            ->groupBy('tasks.id')
            ->with(['volunteer', 'payments']);
    }
}

Это позволяет запустить следующий код, чтобы получить задачи, в которых сумма связанных платежей меньше суммы задачи. С платежами и волонтером, нетерпеливо загруженным

Task::incompletePayments()->get()
person Danny Van Der Sluijs    schedule 05.08.2020