Laravel中如何从Invoice_Detail模型查询销量TOP5分类?
解决Laravel中查询销量前五分类的问题
看起来你的思路方向是对的,但用with()来做分组统计的方式不太对——with()是用来预加载关联模型的,它的执行逻辑是先查主表(invoice_details),再批量查关联的items和categories,这和你需要的按分类分组统计销量的逻辑不匹配,所以才会得到总销量但item为null的结果。
我给你两种可行的解决方案,根据你的习惯选就行:
方案一:使用Join直接关联表查询
这种方式更直接,通过关联三张表来统计分类的总销量:
use Illuminate\Support\Facades\DB; $topCategories = InvoiceDetail::join('items', 'invoice_details.item_id', '=', 'items.item_id') ->join('categories', 'items.category_id', '=', 'categories.id') // 选择分类的核心字段,以及统计的总销量 ->select('categories.id', 'categories.name', DB::raw('SUM(invoice_details.qty) as total_sales')) // 按分类分组,确保每个分类只统计一次 ->groupBy('categories.id', 'categories.name') // 按销量降序排序 ->orderBy('total_sales', 'DESC') // 取前五条 ->take(5) ->get();
注意:如果你的categories表还有其他需要展示的字段,要把它们也加到groupBy里(MySQL严格模式下要求分组字段必须包含所有非聚合字段)。
方案二:用Eloquent关联统计(更符合ORM风格)
如果想保持Laravel的ORM写法,可以从Category模型出发,利用hasManyThrough关联来统计:
首先在Category模型里定义关联关系:
class Category extends Model { public function items() { return $this->hasMany(Item::class, 'category_id'); } // 定义分类到发票详情的关联(分类→商品→发票详情) public function invoiceDetails() { return $this->hasManyThrough( InvoiceDetail::class, Item::class, 'category_id', // items表中关联category的外键 'item_id' // invoice_details表中关联item的外键 ); } }
然后就可以用withCount来做聚合统计:
use Illuminate\Support\Facades\DB; $topCategories = Category::withCount([ 'invoiceDetails as total_sales' => function ($query) { $query->select(DB::raw('SUM(qty)')); } ])->orderBy('total_sales', 'DESC') ->take(5) ->get();
这样得到的结果里,每个Category模型都会带有total_sales字段,就是该分类的总销量。
为什么你的原代码不行?
你原代码里的with(['item.category' => function($query){ $query->groupBy('id'); }])是在预加载时对分类分组,但主查询InvoiceDetail::selectRaw('SUM(qty) as qty')并没有关联到分类,所以它统计的是所有发票详情的总销量,而不是按分类分组的销量。同时预加载的分组逻辑错误,导致item无法正确关联,最终返回null。
内容的提问来源于stack exchange,提问作者CairoCoder
相关产品推荐
相关产品推荐

