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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 06:05:20