Laravel 约束急切加载 - 如果关系列应用限制!= 1

Posted

技术标签:

【中文标题】Laravel 约束急切加载 - 如果关系列应用限制!= 1【英文标题】:Laravel constrain eager loading - apply limit if relationship column != 1 【发布时间】:2022-01-10 04:43:30 【问题描述】:

我正在构建一个与流行的名为“Anki”的抽认卡软件类似的系统,并且正在尝试加载一些属于用户研究的答案,但进行了一些修改。

模型关系

User 有很多 Study Study 有很多 Answer

这里是代码 sn-p。 (请看下面的第二部分)

$user_query = User::with([
    'studies.answers' => function ( $query_answer ) use ( $args ) 
        $query_answer
            ->where( 'grade', '=', 'hold' )
            ->groupBy( 'card_id' )
            ->orderByDesc( 'id' );

            if ( $args['no_date_limit'] ) 
                $query_answer = $query_answer->where( 'next_due_at', '>', $args['start_date'] );
             else 
                $query_answer = $query_answer->whereBetween( 'next_due_at', [ $args['start_date'], $args['end_date'] ] );
            

            if ( ! empty( $args['card_ids_in'] ) ) 
                $query_answer = $query_answer->whereIn( 'card_id', $args['card_ids_in'] );
            
            if ( ! empty( $args['card_ids_not_in'] ) ) 
                $query_answer = $query_answer->whereNotIn( 'card_id', $args['card_ids_not_in'] );
            

下面,如果study.answer_all_on_hold ($study_all_on_hold) != 1,我想应用限制 并从study.no_on_hold读取$no_on_hold

            // Here, I want to apply the limit ($study_all_on_hold ) if study.answer_all_on_hold != 1
            if ( ! $study_all_on_hold ) 
                // And also read the $no_on_hold value from study.no_on_hold
                $query_answer = $query_answer->limit( $no_on_hold );
            
        ,
    ])
    ->where( 'ID', '=', $user_id );

如您所见,我正在尝试应用研究列值的限制。 目前可以吗?

编辑

我已经通过阅读研究study.no_on_hold 中的列的值来了解如何添加限制。剩下的就是仅在 study.answer_all_on_hold 为 != 1 时应用该限制的方法

//if ( ! $study_all_on_hold ) 
            $query_answer = $query_answer->limit( 'study.no_on_hold' );
//  

【问题讨论】:

可能不在单个查询中。不过,您绝对可以映射生成的集合以获得您想要的格式。 感谢您的建议,但我真的需要在应用所有这些条件的情况下急切地加载答案。我试过单独加载它,同时循环研究,但运行了很多查询,这就是为什么我对预先加载进行排序。 没有办法做到这一点,即使在原始 SQL 中也是如此。此外,您应该使用when() 方法查看条件查询子句,这将使您的代码更加简洁。 【参考方案1】:

在您的示例中,Eloquent 正在执行 3 个查询:

    SELECT * FROM users WHERE id = $user_id SELECT * FROM studies WHERE user_id IN (all users.id from query 1) SELECT * FROM answers WHERE study_id IN (all studies.id FROM query 2)

然后,它将所有结果分组到生成的Collection 中。由于 Eloquent 的工作方式,您不能在查询 3 上添加 LIMIT

我认为你可以通过应用一些collection methods 来获得你想要的结果。

$user_query = User::...get();

$user_query = $user_query->each(function ($user) 
    $user->studies->transform(function ($study) 
        if ($study->answer_all_on_hold != 1) 
            $study->answers = $study->answers->take($study->no_on_hold)->values();
            return $study;
         else 
            return $study;
        
    );
);

这不会改变查询(因此您仍然会查询每个答案),甚至会导致一些额外的开销。

【讨论】:

感谢您的回答。我真的不想做任何循环,因为随着用户开始使用该系统,会有很多研究。同样调用 $user->studies 将执行另一个查询,$study->answers 也将执行(将为每个研究执行)。不是吗? 我已经通过 $query_answer = $query_answer->limit( 'study.no_on_hold' ); 弄清楚了如何应用限制.剩下的唯一事情是如何仅在 study.answer_all_on_hold 为 =1 时应用该限制(当它为真时) 执行$query_answer->limit('study.no_on_hold') 将对每个答案产生影响。不只是属于特定的Study 要明白我的意思,请尝试输入limit(1),您会发现您将在 3 项研究中得到一个答案,即使所有研究都至少有一个答案。 $query_answer->limit('study.no_on_hold') 将我将为每个Study 阅读的答案数量限制为每个study.no_on_hold 的值,这就是我需要的。这就是为什么我不能使用硬值作为限制,比如limit(1)。它目前对我很有用,只是只有当每个研究的 study.answer_all_on_hold 的列值为 != 1 时,我才需要应用该限制

以上是关于Laravel 约束急切加载 - 如果关系列应用限制!= 1的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Laravel 中为 ORM 急切加载添加多个键约束?

Laravel 急切加载特定列错误

使用 laravel 急切加载选择特定列不起作用

Laravel 5.4急切加载belongsToMany关系null绑定

Laravel 动态关系 - 在急切加载时访问模型属性

Laravel 在急切加载时使用 Eloquent 选择特定列