模态框:Subscription.php
class Subscription extends Model {
use HasFactory;
public function payments(): HasMany {
return $this->hasMany(SubscriptionPayment::class, 'sub_id');
}
public function totalDurationDays(): Attribute {
return Attribute::make(
get: fn() => $this->payments()->sum('duration_days')
)->shouldCache();
}
}
控制器:SubscriptionController.php
$subs = SharedServer::from('subscriptions AS ss')
->select(
'ss.*',
DB::raw("(SELECT SUM(duration_days) FROM subscription_payments AS ssp WHERE ssp.sub_id = ss.id) AS total_duration_days"),
)
->paginate();
如果total_duration_days
从自定义属性调用,与直接从控制器调用相比,Laravel 调试栏会显示太多查询。
有没有办法指示查询生成器total_duration_days
直接在一个查询中急切加载自定义属性?
重要的是,哪个部分导致查询过多。如果我们只讨论自定义属性,那么
->payments()
问题就出现了。为什么?因为你每次都试图通过查询来计算天数的总和。有什么解决方案?
预先加载:
首先,您需要使用 with 或 load 进行预加载,然后需要使用不带括号的关系。最终代码:
正如@Tarashdeep-Singh提到的:
但要记住两点:1- 删除括号并使用
payment->
for 关系 2- 不要忘记使用急切加载方法,如 with 或 load。如果您忘记了其中任何一项,您将遇到 n+1 问题。加入:
这是查询此类数据最有效的方式:
这可能不会给你想要的结果,因为这取决于你想如何过滤结果,而内连接或左连接可能是可能的答案。但是有了这个,你只有一个查询,所以它的性能最高。
子查询:
子查询是一种很好且非常干净的查询方式,说实话它也很容易。但代价是,如果子查询是相关的,那么它的性能可能不如连接。我不会包含该示例,因为您已经在描述中包含了子查询。如您所见,您的子查询依赖于
ss
(订阅)表作为相关子查询。如果您想使用 totalDurationDays 方法作为自定义属性并避免多次查询,则付款关系会立即加载。这样,可以从加载的付款集合中计算总和,而不必对每个订阅模型进行额外的查询。
在您的 subscriptioncontroller.php 中