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 急切加载添加多个键约束?