【问题标题】:Laravel whereHas behaving unexpectedlyLaravel where 行为异常
【发布时间】:2018-12-13 11:01:24
【问题描述】:

我在我的 Laravel 5.6 应用程序中设置了以下关系。

Purchase belongs to many Invoice

Invoice belongs to many Purchase

Invoice belongs to many Payment

Payment belongs to many Invoice

这些关系是使用数据透视表建立的。

我想查找通过发票支付为 0 的购买。

在我的测试中,我有 一个 购买,我附上了 两张 发票到该购买,并且我附上了 一个 付款到每个那些发票。一笔付款金额为 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 的功能?

任何帮助将不胜感激,谢谢。

【问题讨论】:

  • >我只想找到通过发票支付为 0 的购买。>一笔付款的金额为 100,另一笔的金额为 0。>它始终退回我的购买创建。看起来不错?
  • 您是否要获取只有零金额付款而金额> 0的付款?
  • 我想要购买,通过发票,只有 0 的付款。如果该购买有任何与它相关的超过 0 的付款,我不想要购买。
  • 第二个查询看起来正确。可以分享一下dd(Purchase::whereDoesntHave('invoices.payments', function ($q) { return $q-&gt;where('amount', '&gt;', 0); })-&gt;toSql())
  • 好的,感谢您的帮助: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` &gt; ? and `payments`.`deleted_at` is null) and `invoices`.`deleted_at` is null) and `purchases`.`deleted_at` is null

标签: laravel eloquent query-builder


【解决方案1】:

您的第二种方法是正确的,但 whereDoesntHave() 不适用于 Laravel 5.6 中的嵌套关​​系。这个错误在 Laravel 5.7 中是 fixed

您可以使用此解决方法:

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

【讨论】:

    【解决方案2】:

    我想,你应该同时使用这两种方法。

    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 的购买。

    【讨论】:

    • 遗憾的是仍然没有运气。谢谢@IndianCoding。我开始怀疑我的测试设置,尽管我已经经历过无数次了。
    【解决方案3】:

    也许是我没有完全理解您的要求/这里的问题,但这就是我认为我理解的......

    您创建了 1 个Purchase。 它有 2 个Invoices。 每个Invoice 有1 个Payment(100 个,0 个1 个

    在你的问题中你说

    我想找到通过发票支付的付款 为 0。

    但是你抱怨你得到了结果......当你确实购买了零付款。

    为了让测试更“真实”,我会创建许多 Purchases 和许多 Invoices 和 Payments 等,以便真正了解正在发生的事情。

    更新

    您是否考虑过“购买”模型与“付款”的“hasManyThrough”关系?像这样

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

    那么也许你可以做这个查询。

    Purchase::whereDoesntHave('payment')->get();
    

    【讨论】:

    • 感谢您的回答约翰,您是对的,我只想要金额为 0 的相关付款的购买。但是,如果该付款也有高于 0 的相关付款,我不想要它。为了清楚起见,我提到我只使用一次购买,但实际上我的测试,就像你建议的那样,使用了许多购买。我对许多人的行为与对一个人的行为相同。
    • 我已经更新了我的答案。不确定这是否有帮助?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-09
    • 1970-01-01
    • 2017-11-19
    • 1970-01-01
    • 1970-01-01
    • 2014-05-29
    相关资源
    最近更新 更多