Laravel中如何以数组为外键,构建订单与药品的hasMany关联
Laravel订单关联数组存储药品ID的关联查询解决方案
场景说明
现有数据库结构:
- 药品表(Medicines)
- id
- price
- description
- 其他字段...
- 电商订单表(Ecommerce Orders)
- id
- user_id
- products:存储药品ID及数量的数组(已在
EcommerceOrders模型中通过$casts转为数组)
当前尝试用hasMany关联药品模型,但由于外键是数组格式,无法正常工作,以下是可行的解决方法:
方法1:自定义关联查询(单订单场景)
直接在模型中编写方法,通过whereIn匹配数组中的药品ID,适合查询单个订单的药品信息:
protected $casts = [ 'products' => 'array', ]; public function items() { // 适配两种products结构: // 情况1:products是纯ID数组,如[1,2,3] $productIds = $this->products; // 情况2:products是包含id和数量的数组集合,如[{"id":1,"quantity":2},{"id":3,"quantity":1}] // $productIds = collect($this->products)->pluck('id')->toArray(); return Medicines::whereIn('id', $productIds)->withTrashed(); }
使用方式:
$order = EcommerceOrders::find(1); $items = $order->items->get();
方法2:支持预加载的批量查询
如果需要批量查询订单时预加载关联药品,可以结合查询作用域实现:
protected $casts = [ 'products' => 'array', ]; // 单个订单的药品查询 public function items() { $productIds = collect($this->products)->pluck('id')->toArray(); return Medicines::whereIn('id', $productIds)->withTrashed(); } // 批量预加载的作用域 public function scopeWithItems($query) { // 提取所有订单的药品ID并去重 $allProductIds = $query->get()->pluck('products') ->flatten(1) // 适配嵌套数组结构 ->pluck('id') ->unique() ->toArray(); // 预加载药品数据,后续手动匹配到对应订单 return $query->with(['items' => function($q) use ($allProductIds) { $q->whereIn('id', $allProductIds)->withTrashed(); }]); }
使用方式:
$orders = EcommerceOrders::withItems()->get();
方法3:数据库结构优化(最佳实践)
数组存储外键不符合关系型数据库设计规范,长期来看会带来查询效率低、维护困难等问题,推荐创建中间表order_products:
中间表结构(order_products)
- id
- order_id(关联订单表id)
- medicine_id(关联药品表id)
- quantity(购买数量)
- price_snapshot(下单时的药品单价快照,可选)
模型关联调整
EcommerceOrders模型:
public function items() { return $this->hasMany(OrderProduct::class); }
新建OrderProduct模型:
public function medicine() { return $this->belongsTo(Medicines::class)->withTrashed(); }
使用方式:
// 预加载订单及关联的药品信息 $orders = EcommerceOrders::with('items.medicine')->get();
这种方式完全适配Laravel的Eloquent关联特性,支持预加载、关联统计、事务操作等,是最规范的实现方案。
内容的提问来源于stack exchange,提问作者cdBreak
相关产品推荐
相关产品推荐

