You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel多表关联查询:菜品销量排名报表实现问题

解决方案:Laravel模型关联与菜品销量统计

我来帮你搞定这个关联和统计的问题,核心是先理清三个表的层级关系,再用合适的Eloquent关联或者查询构建器实现需求,咱们一步步来:


一、先理清楚模型关联逻辑

你的表关系链条是:Record(销量记录)→ 属于 DayRecipeeShopWorkshift(日期-菜品-门店-班次关联)→ 属于 Recipee(菜品)。基于这个链条,我们给每个模型定义正确的关联:

1. Record 模型(app/Models/Record.php)

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasOneThrough;

class Record extends Model
{
    protected $table = 'records'; // 可选,表名符合Laravel复数规则的话可以省略

    // 关联到中间表
    public function dayRecipeeShopWorkshift(): BelongsTo
    {
        return $this->belongsTo(DayRecipeeShopWorkshift::class);
    }

    // 直接关联到菜品(通过中间表)
    public function recipee(): HasOneThrough
    {
        return $this->hasOneThrough(
            Recipee::class,
            DayRecipeeShopWorkshift::class,
            'id',               // 中间表的主键
            'id',               // 菜品表的主键
            'day_recipee_shop_workshift_id', // 当前表关联中间表的外键
            'recipee_id'        // 中间表关联菜品表的外键
        );
    }
}

2. DayRecipeeShopWorkshift 模型(app/Models/DayRecipeeShopWorkshift.php)

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

class DayRecipeeShopWorkshift extends Model
{
    protected $table = 'day_recipee_shop_workshifts';

    public function recipee(): BelongsTo
    {
        return $this->belongsTo(Recipee::class);
    }

    public function records(): HasMany
    {
        return $this->hasMany(Record::class);
    }
}

3. Recipee 模型(app/Models/Recipee.php)

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;

class Recipee extends Model
{
    protected $table = 'recipees';

    public function dayRecipeeShopWorkshifts(): HasMany
    {
        return $this->hasMany(DayRecipeeShopWorkshift::class);
    }

    // 关联所有对应的销量记录
    public function records(): HasManyThrough
    {
        return $this->hasManyThrough(Record::class, DayRecipeeShopWorkshift::class);
    }
}

二、获取单条销量记录并按销量排序

如果你需要取出所有records数据,同时带上对应的菜品名称,并按单条记录的count降序排列,可以这么写:

use App\Models\Record;

$recordsWithRecipee = Record::with('recipee')
    ->orderBy('count', 'desc')
    ->get();

// 遍历输出示例
foreach ($recordsWithRecipee as $record) {
    echo "菜品名称:{$record->recipee->name},本次售出:{$record->count}";
}

三、生成菜品销量排名报表(核心需求)

这应该是你最需要的:按菜品分组统计总销量,然后生成排名。这里有两种优雅的实现方式:

方式1:使用查询构建器(Join方式)

适合需要自定义SQL逻辑的场景:

use App\Models\Record;
use Illuminate\Support\Facades\DB;

$recipeeRanking = Record::select(
        'recipees.name',
        DB::raw('SUM(records.count) as total_sold')
    )
    ->join('day_recipee_shop_workshifts', 'records.day_recipee_shop_workshift_id', '=', 'day_recipee_shop_workshifts.id')
    ->join('recipees', 'day_recipee_shop_workshifts.recipee_id', '=', 'recipees.id')
    ->groupBy('recipees.id', 'recipees.name') // 按菜品ID+名称分组,确保唯一性
    ->orderBy('total_sold', 'desc')
    ->get();

// 输出排名示例
foreach ($recipeeRanking as $rank => $item) {
    echo "第".($rank+1)."名:{$item->name},总销量:{$item->total_sold}";
}

方式2:使用Eloquent关联统计(更符合Laravel风格)

利用菜品模型的关联直接统计总销量:

use App\Models\Recipee;

$recipeeRanking = Recipee::withCount([
        'records as total_sold' => function ($query) {
            $query->select(DB::raw('SUM(count)'));
        }
    ])
    ->orderBy('total_sold', 'desc')
    ->get();

// 输出排名示例
foreach ($recipeeRanking as $rank => $item) {
    echo "第".($rank+1)."名:{$item->name},总销量:{$item->total_sold}";
}

为什么之前的关联尝试失败?

你提到用hasOneThrough和belongsToMany没成功,大概率是这两个原因:

  • belongsToMany是用来处理多对多直接关联的,而你的场景是三层层级关联(Record→中间表→菜品),属于一对多+一对一的组合,完全不适用多对多。
  • 使用hasOneThrough时可能参数顺序搞错了,这个方法的参数顺序非常关键,必须严格对应你的表字段,我上面的代码已经帮你把参数对应正确了。

内容的提问来源于stack exchange,提问作者Hmorv

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 19:24:07