外卖应用数据库设计咨询(含特殊产品与Laravel适配需求)
外卖应用产品数据库设计与Laravel实现方案
数据库设计思路
针对你提到的三种产品类型和配置需求,采用结构化表设计避免EAV模式的查询复杂度问题:
1. 产品主表 (products)
存储所有产品基础信息,通过product_type区分类型:
- 字段:
id,name,description,base_price(固定价产品直接使用,动态价/组合价设为0),product_type(枚举值:fixed,dynamic,combo),status,created_at,updated_at - 用途:统一管理产品基础属性,快速区分产品类型
2. 产品变体表 (product_variants)
处理尺寸这类影响价格和配置限制的变体,仅关联dynamic类型产品:
- 字段:
id,product_id,variant_type(如size),value(如small/medium),price_adjustment(相对于base_price的调整额,也可直接存variant_price),max_sauce_count(该尺寸允许的免费酱料数量),created_at,updated_at - 用途:绑定尺寸与价格、酱料限制的关系,解决动态定价和配置依赖问题
3. 可选配置项表 (product_options)
存储酱料这类可选附加项,支持单独定价:
- 字段:
id,product_id,option_type(如sauce),name,additional_price(额外选择的费用,免费则设为0),is_active,created_at,updated_at - 用途:管理产品的所有可选配置,支持不同产品有不同配置选项
4. 组合产品关联表 (combo_product_items)
处理双拼披萨这类组合产品,关联子产品及其变体:
- 字段:
id,combo_product_id,child_product_id,variant_id,quantity,created_at,updated_at - 用途:记录组合产品的组成子产品(含具体变体),方便计算组合价和生成订单
Laravel风格实现
模型定义
Product 模型
namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; class Product extends Model { protected $fillable = ['name', 'description', 'base_price', 'product_type', 'status']; protected $casts = [ 'product_type' => 'string', 'base_price' => 'decimal:2', 'status' => 'boolean', ]; public function variants(): HasMany { return $this->hasMany(ProductVariant::class); } public function options(): HasMany { return $this->hasMany(ProductOption::class); } public function comboItems(): HasMany { return $this->hasMany(ComboProductItem::class, 'combo_product_id'); } }
ProductVariant 模型
namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; class ProductVariant extends Model { protected $fillable = ['product_id', 'variant_type', 'value', 'price_adjustment', 'max_sauce_count']; protected $casts = [ 'price_adjustment' => 'decimal:2', 'max_sauce_count' => 'integer', ]; public function product(): BelongsTo { return $this->belongsTo(Product::class); } }
ProductOption 模型
namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; class ProductOption extends Model { protected $fillable = ['product_id', 'option_type', 'name', 'additional_price', 'is_active']; protected $casts = [ 'additional_price' => 'decimal:2', 'is_active' => 'boolean', ]; public function product(): BelongsTo { return $this->belongsTo(Product::class); } }
ComboProductItem 模型
namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; class ComboProductItem extends Model { protected $fillable = ['combo_product_id', 'child_product_id', 'variant_id', 'quantity']; protected $casts = [ 'quantity' => 'integer', ]; public function comboProduct(): BelongsTo { return $this->belongsTo(Product::class, 'combo_product_id'); } public function childProduct(): BelongsTo { return $this->belongsTo(Product::class, 'child_product_id'); } public function variant(): BelongsTo { return $this->belongsTo(ProductVariant::class, 'variant_id'); } }
验证规则
产品创建/更新验证(FormRequest)
namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class StoreProductRequest extends FormRequest { public function authorize() { return true; } public function rules() { return [ 'name' => 'required|string|max:255', 'description' => 'nullable|string', 'base_price' => 'required_if:product_type,fixed|numeric|min:0', 'product_type' => 'required|in:fixed,dynamic,combo', 'status' => 'boolean', 'combo_items' => 'required_if:product_type,combo|array|min:1', 'combo_items.*.child_product_id' => 'exists:products,id', 'combo_items.*.variant_id' => 'nullable|exists:product_variants,id', 'combo_items.*.quantity' => 'required|integer|min:1', ]; } }
酱料选择自定义验证
订单提交时,验证用户选择的酱料数量是否符合尺寸限制:
// 示例代码,可放在控制器或自定义Request中 $variant = ProductVariant::findOrFail($request->variant_id); $selectedSauces = $request->input('selected_sauces', []); if (count($selectedSauces) > $variant->max_sauce_count) { $extraCount = count($selectedSauces) - $variant->max_sauce_count; return redirect()->back()->withErrors([ 'selected_sauces' => "该尺寸仅允许选择{$variant->max_sauce_count}种免费酱料,额外{$extraCount}种需支付额外费用" ]); }
开源方案参考
可以借鉴Laravel生态内的成熟电商项目优化设计:
- Bagisto:基于Laravel的开源电商系统,有完善的产品变体、属性组合管理模块,组合产品和动态定价的实现逻辑可直接参考
- Aimeos Laravel:专注电商的Laravel扩展包,提供标准化的产品、变体、配置项数据库结构,适合复杂电商场景
- Laravel Nova:后台管理工具,可快速搭建产品配置可视化界面,支持关联模型快速管理,减少重复开发
内容的提问来源于stack exchange,提问作者hijack
相关产品推荐
相关产品推荐

