【问题标题】:Laravel querying many to many records with eager loading conditionsLaravel 查询具有急切加载条件的多对多记录
【发布时间】:2014-03-10 11:16:22
【问题描述】:

所以我的应用具有这样的结构。

  • payments hasAndBelongsToMany payers
  • payers属于usersusers有Many payers

我想查询某个用户 ID 的所有付款(和数据透视数据)。我一直在使用这个返回所有付款的查询,无论 user_id 是什么:

$payment_data = Payment::with('payers')->get();

现在,如果我只想获得付款人的 user_id 为 5 的付款,我尝试了以下操作:

$payment_data = Payment::with(array(
        'payers' => function($q){
           $q->where('user_id', '=', 5);
        }))->get();

但它返回几乎相同的结果,除了条件仅适用于付款人,而不是付款,即我仍然在付款表中获得所有付款,如果user_id 则不是数据透视数据条件不满足。从技术上讲,我可以只使用此结果并过滤掉 payers 子数组为空的结果,但如果有大量付款,这将变得非常低效。

形成这个查询的正确方法是什么?

编辑

这是我的数据库结构的基本表示:

简单的 SQL 查询将是这样的(RDMS 有所不同,但你明白了):

SELECT payments.*, payers_payments.* 
FROM   payments 
       RIGHT JOIN (payers 
                   LEFT JOIN payers_payments 
                          ON payers.id = payers_payments.payer_id) 
               ON payments.id = payers_payments.payment_id 
WHERE  payers.user_id = 5;

使用wherehas 会得到以下结果:

SELECT * 
FROM   "payments" 
WHERE  (SELECT Count(*) 
        FROM   "payers" 
               INNER JOIN "payer_payment" 
                       ON "payers"."id" = "payer_payment"."payer_id" 
        WHERE  "payer_payment"."payment_id" = "payments"."id" 
               AND "payers"."user_id" = '1') >= '1' 

这不是我想要的。

编辑 2

好的,经过一番折腾,我知道我需要使用即时加载,因为我需要数据透视字段以及支付模型本身的字段。这是一个显示 3 笔付款及其数据透视表的示例:

Array
(
    [0] => Array
        (
            [id] => 2
            [payment_date] => 2014-03-07
            [company] => Franco Manca
            [item] => Pizza
            [created_at] => 2014-03-10 10:16:08
            [updated_at] => 2014-03-10 10:16:08
            [payers] => Array
                (
                    [0] => Array
                        (
                            [id] => 1
                            [name] => tim
                            [email] => tim@tim.com
                            [user_id] => 1
                            [created_at] => 2014-03-10 10:07:23
                            [updated_at] => 2014-03-10 10:07:23
                            [pivot] => Array
                                (
                                    [payment_id] => 2
                                    [payer_id] => 1
                                    [amount] => 21.0
                                    [pays] => 0
                                    [created_at] => 2014-03-10 10:16:08
                                    [updated_at] => 2014-03-10 10:27:45
                                )

                        )

                    [1] => Array
                        (
                            [id] => 2
                            [name] => tom
                            [email] => tom@tom.com
                            [user_id] => 1
                            [created_at] => 2014-03-10 10:16:35
                            [updated_at] => 2014-03-10 10:16:35
                            [pivot] => Array
                                (
                                    [payment_id] => 2
                                    [payer_id] => 2
                                    [amount] => 0.0
                                    [pays] => 1
                                    [created_at] => 2014-03-10 10:27:45
                                    [updated_at] => 2014-03-10 10:27:45
                                )

                        )

                )

        )

    [1] => Array
        (
            [id] => 3
            [payment_date] => 2014-03-05
            [company] => Kaff
            [item] => Cocktail
            [created_at] => 2014-03-10 10:17:05
            [updated_at] => 2014-03-10 10:17:05
            [payers] => Array
                (
                    [0] => Array
                        (
                            [id] => 1
                            [name] => tim
                            [email] => tim@tim.com
                            [user_id] => 1
                            [created_at] => 2014-03-10 10:07:23
                            [updated_at] => 2014-03-10 10:07:23
                            [pivot] => Array
                                (
                                    [payment_id] => 3
                                    [payer_id] => 1
                                    [amount] => 12.0
                                    [pays] => 1
                                    [created_at] => 2014-03-10 10:17:05
                                    [updated_at] => 2014-03-10 10:17:05
                                )

                        )

                    [1] => Array
                        (
                            [id] => 2
                            [name] => tom
                            [email] => tom@tom.com
                            [user_id] => 1
                            [created_at] => 2014-03-10 10:16:35
                            [updated_at] => 2014-03-10 10:16:35
                            [pivot] => Array
                                (
                                    [payment_id] => 3
                                    [payer_id] => 2
                                    [amount] => 19.0
                                    [pays] => 1
                                    [created_at] => 2014-03-10 10:17:05
                                    [updated_at] => 2014-03-10 10:17:05
                                )

                        )

                )

        )

)

问题是,如果我随后将 user_id 条件更改为 99999(它不存在),它仍然返回所有付款,其中 payers 数组为每个为空,而实际上它应该返回一个完全空的结果。

为预先加载生成的 2 个查询是:

select * from "payments" --I want to add my user_id condition to this

SELECT "payers".*, 
       "payer_payment"."payment_id" AS "pivot_payment_id", 
       "payer_payment"."payer_id"   AS "pivot_payer_id", 
       "payer_payment"."amount"     AS "pivot_amount", 
       "payer_payment"."pays"       AS "pivot_pays", 
       "payer_payment"."created_at" AS "pivot_created_at", 
       "payer_payment"."updated_at" AS "pivot_updated_at" 
FROM   "payers" 
       INNER JOIN "payer_payment" 
               ON "payers"."id" = "payer_payment"."payer_id" 
WHERE  "payer_payment"."payment_id" IN ( '2', '3' ) 
       AND "user_id" = '1'  --This satisfies the condition

【问题讨论】:

    标签: php sql laravel eloquent


    【解决方案1】:

    with 方法仅用于预先加载关系,对该关系的约束应该这样设置:

    $payment_data = Payment::whereHas('payers', function($q){
               $q->where('payers.user_id', '=', 5);
            })->get();
    );
    

    您还可以在 where 子句中指定要使用的变量:

    $payment_data = Payment::whereHas('payers', function($q) use ($foo){
               $q->where('payers.user_id', '=', $foo);
            })->get();
    );
    

    【讨论】:

    • 我不认为这是正确的,因为它只是添加了一个用于确定布尔结果的子查询(如果我们可以找到一些用户 ID 为 5 的付款人,则返回所有付款)。查询应连接表并添加条件。在上面添加更多细节。
    • 我在发布之前用类似的模型测试了查询,它只返回具有该付款人条件的付款。
    • 我在我的模型上进行了测试,它返回一个空结果。见上面的编辑
    • 这个怎么样:Payment::with('payers') ->join('payers', 'payment.id', '=', 'payers.id') ->where(' payers.user_id','=',5) ->get()
    • 好的,我检查了编辑问题,至少您会了解如何在相关模型中使用 join 子句将其调整为您的模型。
    【解决方案2】:

    换个方式试试

    User::find(5)->with('payers.payments')->get();
    

    您将与您的付款人和他们的付款一起上课,以便让他们获得循环所需的一切,我认为加入可以完美地完成这项工作,因为您不需要循环。

    【讨论】:

    • 这似乎做了某种无限循环
    • 哎呀忘了在最后添加 get() 方法。
    【解决方案3】:

    好的,经过大量搜索,problem with eloquent + sqlite 我有了答案:

    您需要首先使用whereHas 获得所需用户的付款:

    $payments = Payment::whereHas('payers', function($q){
        $q->where('user_id', '=', Auth::user()->id); 
    },'>=', DB::raw('1'))->get();
    

    然后,无需在整个 Payment 模型上调用预加载,您只需在您刚刚完成的付款收款中调用它(延迟预加载):

    $payment_data = $payments->load(array(
        'payers' => function($q){
           $q->where('user_id', '=', Auth::user()->id);
    }));
    

    这会将透视数据添加到您的 $payments 集合中,从而为您提供所需的内容,而无需查询数据库中的每笔付款,这是问题中的第一种方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-01
      • 1970-01-01
      • 2015-05-13
      • 1970-01-01
      • 2017-04-13
      相关资源
      最近更新 更多