Laravel Eloquent:如何在查询中添加属性而非模型中?
最优实现方案
方法一:数据库层面直接计算(推荐,支持排序)
直接通过查询构建器在数据库层面统计订单数量,所有操作一次完成,性能最优且天然支持排序:
// 直接调用查询构建器 $customers = Customer::selectRaw('customers.*, (SELECT COUNT(*) FROM orders WHERE orders.customer_id = customers.id) as orders_nb') ->orderBy('orders_nb') ->get();
如果需要复用该逻辑,可以封装成本地作用域,让调用更符合Eloquent链式风格:
class Customer extends Eloquent { // 定义本地作用域 public function scopeWithOrdersCount($query) { return $query->selectRaw('customers.*, (SELECT COUNT(*) FROM orders WHERE orders.customer_id = customers.id) as orders_nb'); } } // 调用方式 $customers = Customer::withOrdersCount()->orderBy('orders_nb')->get();
该方式优势:
- 仅执行1次数据库查询,避免N+1性能问题
- 原生支持
orderBy('orders_nb')进行数据库层面排序 - 仅在主动调用该作用域时才添加字段,不会全局生效
方法二:集合映射(适合无需数据库排序的场景)
如果不需要在数据库层面排序,可利用Eloquent集合的map方法简化代码,同时通过with预加载关联避免N+1查询:
$customers = Customer::with('orders')->get()->map(function ($customer) { $customer->orders_nb = $customer->orders->count(); // 可选:隐藏关联的orders字段,减少冗余返回数据 $customer->setHidden(array_merge($customer->getHidden(), ['orders'])); return $customer; });
注意:这种方式是在内存中计算数量,无法直接用orderBy('orders_nb')做数据库排序,若需排序需在集合层面操作:
$sortedCustomers = $customers->sortBy('orders_nb');
对比原方案的优化点
原方案未预加载orders关联,会触发N+1查询导致性能低下;上述两种方法:
- 方法一在数据库层完成统计,性能最优且支持排序
- 方法二用集合映射简化代码,同时通过预加载避免了N+1问题
内容的提问来源于stack exchange,提问作者Totuxx
相关产品推荐
相关产品推荐

