Laravel, где ведет себя неожиданно

В моем приложении Laravel 5.6 установлены следующие отношения.

Purchase belongs to many Invoice

Invoice belongs to many Purchase

Invoice belongs to many Payment

Payment belongs to many Invoice

Эти отношения устанавливаются с помощью сводных таблиц.

Я хочу найти только покупки с нулевыми платежами по счетам.

В моем тесте у меня есть одна покупка, я приложил два счета к этой покупке, и я приложил один платеж к каждому из этих счетов. Один платеж имеет сумму 100, другой - 0.

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

Это вопрос, который я написал:

Purchase::whereHas('invoices.payments', function ($q) {
   return $q->where('amount', '<=', 0);
})->get();

Я также пробовал:

Purchase::whereDoesntHave('invoices.payments', function ($q) {
   return $q->where('amount', '>', 0);
})->get();

Я здесь что-то не так делаю? Я неправильно понимаю возможности WhereHas?

Любая помощь будет принята с благодарностью, спасибо.


person Lawrence Dawson    schedule 13.12.2018    source источник
comment
›Я хочу найти только покупки, для которых в счетах-фактурах платежи равны 0.› Один платеж имеет сумму 100, а другой - 0. ›он постоянно возвращает созданную мной покупку. Выглядит правильно?   -  person IndianCoding    schedule 13.12.2018
comment
Вы хотите получить результат ТОЛЬКО с нулевым платежом без платежей с суммой ›0?   -  person IndianCoding    schedule 13.12.2018
comment
Я хочу Покупки, которые по счетам имеют ТОЛЬКО платежи в размере 0. Если с этой покупкой связаны ЛЮБЫЕ платежи на сумму более 0, я не хочу покупать.   -  person Lawrence Dawson    schedule 13.12.2018
comment
Второй запрос выглядит правильным. Не могли бы вы поделиться dd(Purchase::whereDoesntHave('invoices.payments', function ($q) { return $q->where('amount', '>', 0); })->toSql())   -  person IndianCoding    schedule 13.12.2018
comment
Конечно, спасибо за помощь: select * from `purchases` where exists (select * from `invoices` inner join `invoice_purchase` on `invoices`.`id` = `invoice_purchase`.`invoice_id` where `purchases`.`id` = `invoice_purchase`.`purchase_id` and not exists (select * from `payments` inner join `invoice_payment` on `payments`.`id` = `invoice_payment`.`payment_id` where `invoices`.`id` = `invoice_payment`.`invoice_id` and `amount` > ? and `payments`.`deleted_at` is null) and `invoices`.`deleted_at` is null) and `purchases`.`deleted_at` is null   -  person Lawrence Dawson    schedule 13.12.2018
comment
Я добавил ответ. Пожалуйста, проверьте и оставьте отзыв.   -  person IndianCoding    schedule 13.12.2018


Ответы (3)


Ваш второй подход правильный, но whereDoesntHave() не работает должным образом с вложенными отношениями в Laravel 5.6. Эта ошибка была исправлена ​​ в Laravel 5.7.

Вы можете использовать этот обходной путь:

Purchase::whereDoesntHave('invoices', function ($q) {
    $q->whereHas('payments', function ($q) {
        $q->where('amount', '>', 0);
    });
})->get();
person Jonas Staudenmeir    schedule 13.12.2018

Я полагаю, вам следует использовать оба метода.

Purchase::whereHas('invoices.payments', function ($q) {
        return $q->where('amount', 0);
    })
    ->whereDoesntHave('invoices.payments', function ($q) {
        return $q->where('amount', '>', 0);
    })
    ->get();

whereHas() принять все покупки с суммой платежа = 0, whereDoesntHave() выбросить покупки с платежами> 0.

person IndianCoding    schedule 13.12.2018
comment
К сожалению, с этим все еще не повезло. Спасибо @IndianCoding. Я начинаю сомневаться в своей тестовой установке, хотя я перебирал ее бесчисленное количество раз. - person Lawrence Dawson; 13.12.2018

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

Вы создали 1 Purchase. Имеет 2 Invoices. Каждому Invoice соответствует 1 Payment (1 из 100, 1 из 0 ‹- технически не платеж).

В своем вопросе вы говорите

Я хочу найти только покупки с нулевыми платежами по счетам.

Но потом вы жалуетесь, что получили результат ... Когда вы ДЕЙСТВИТЕЛЬНО совершаете покупку с нулевым платежом.

Чтобы сделать тест немного более «реальным», я бы создал много Purchase с множеством Invoice и Payment и т.д., чтобы действительно почувствовать, что происходит.

ОБНОВЛЕНИЕ

Рассматривали ли вы отношение hasManyThrough в вашей модели покупки к платежу? нравится

public function payment()
{
    return $this->hasManyThrough(Payment::class, Invoice::class);
}

Тогда, возможно, вы могли бы сделать этот запрос.

Purchase::whereDoesntHave('payment')->get();
person John Halsey    schedule 13.12.2018
comment
Спасибо за ваш ответ, Джон, вы правы, мне нужны только те покупки, которые связаны с платежами на сумму 0. Но если у этого платежа также есть связанный платеж выше 0, я не хочу этого. Для ясности в описании я упомянул, что я использую только одну покупку, но на самом деле в моем тесте, как вы предположили, используется много покупок. У меня такое же поведение со многими, как и с одним. - person Lawrence Dawson; 13.12.2018
comment
Я обновил свой ответ. Не уверен, что это поможет? - person John Halsey; 14.12.2018