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

外卖应用数据库设计咨询(含特殊产品与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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 03:32:48