如何在Laravel/Eloquent中查询包含嵌套去重关联记录的商品数据
方案实现
你不需要让colors表持有stock_id字段,Laravel的hasManyThrough完全适配当前表结构,按如下步骤实现即可:
1. 定义各模型关联
首先在app/Models目录下的三个模型中补充关联定义:
Product 模型
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasManyThrough; class Product extends Model { // 关联库存表 public function stocks(): HasMany { return $this->hasMany(Stock::class); } // 通过库存表关联颜色表,加distinct去重 public function colors(): HasManyThrough { return $this->hasManyThrough( Color::class, Stock::class, 'product_id', // Stocks表中关联Products的外键 'id', // Colors表的主键 'id', // Products表的主键 'color_id' // Stocks表中关联Colors的外键 )->distinct(); } }
Stock 模型
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; class Stock extends Model { // 关联颜色表 public function color(): BelongsTo { return $this->belongsTo(Color::class); } }
Color 模型无需额外修改,保持默认即可。
2. 查询数据并格式化输出
直接用预加载查询避免N+1问题,用集合方法处理出你需要的结构:
$products = Product::with(['stocks', 'colors']) ->get() ->map(function (Product $product) { return [ 'id' => (string) $product->id, 'name' => $product->name, // 从库存中提取所有尺码,去重后转为数组 'available_sizes' => $product->stocks->pluck('size')->unique()->values()->toArray(), // 直接取预加载的颜色列表 'available_colors' => $product->colors->toArray() ]; });
执行后$products的结构完全匹配你给出的期望格式。
可选:用API Resource规范返回
如果是接口返回场景,推荐用Laravel自带的API Resource统一格式化,代码更整洁可复用:
首先创建颜色资源:
php artisan make:resource ColorResource
编辑app/Http/Resources/ColorResource.php:
public function toArray($request) { return [ 'id' => (string) $this->id, 'name' => $this->name, 'hex' => $this->hex, ]; }
再创建商品资源:
php artisan make:resource ProductResource
编辑app/Http/Resources/ProductResource.php:
use App\Http\Resources\ColorResource; public function toArray($request) { return [ 'id' => (string) $this->id, 'name' => $this->name, 'available_sizes' => $this->stocks->pluck('size')->unique()->values(), 'available_colors' => ColorResource::collection($this->colors), ]; }
接口中直接返回即可:
return ProductResource::collection(Product::with(['stocks', 'colors'])->get());
内容的提问来源于stack exchange,提问作者Touhid Rahman
相关产品推荐
相关产品推荐

