本文目录导读:

在PHP项目中实现商品规格管理(SKU管理),核心在于多对多关系的数据结构设计,以及规格组合与库存的动态生成,下面是一个通用的、经过项目实践检验的实现方案。
核心概念
- 规格名:如“颜色”、“尺寸”、“版本”。
- 规格值:如“红色”、“XL”、“256G”。
- SKU(Stock Keeping Unit):库存量单位,即“颜色:红色,尺寸:XL,版本:256G”这一具体组合,拥有独立的库存、价格、图片等。
数据库表结构设计(推荐方案)
这是最常用、扩展性最好的方案,使用四张表。
商品表 (products)
CREATE TABLE `products` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL DEFAULT '' COMMENT '商品名称', `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态 1上架 0下架', `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
规格名表 (spec_names)
CREATE TABLE `spec_names` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `product_id` int(11) NOT NULL COMMENT '关联商品ID', `name` varchar(100) NOT NULL DEFAULT '' COMMENT '规格名,如:颜色,尺寸', `sort_order` int(11) NOT NULL DEFAULT '0' COMMENT '排序', PRIMARY KEY (`id`), KEY `product_id` (`product_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
规格值表 (spec_values)
CREATE TABLE `spec_values` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `spec_name_id` int(11) NOT NULL COMMENT '关联规格名ID', `value` varchar(100) NOT NULL DEFAULT '' COMMENT '规格值,如:红色,XL', `sort_order` int(11) NOT NULL DEFAULT '0' COMMENT '排序', PRIMARY KEY (`id`), KEY `spec_name_id` (`spec_name_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
SKU 表 (sku_stock)
CREATE TABLE `sku_stock` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `product_id` int(11) NOT NULL COMMENT '商品ID', `sku_code` varchar(100) NOT NULL DEFAULT '' COMMENT 'SKU编码(唯一)', `price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '售价', `stock` int(11) NOT NULL DEFAULT '0' COMMENT '库存', `image` varchar(255) NOT NULL DEFAULT '' COMMENT '该SKU专属图片', `spec_value_ids` varchar(255) NOT NULL DEFAULT '' COMMENT '规格值ID组合,如 1,4,7 方便查询', PRIMARY KEY (`id`), KEY `product_id` (`product_id`), KEY `sku_code` (`sku_code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
PHP 业务逻辑实现(以 Laravel 为例,其他框架类似)
模型关联(可选,但推荐)
// Product.php (商品模型)
public function specNames() {
return $this->hasMany(SpecName::class);
}
public function skus() {
return $this->hasMany(SkuStock::class);
}
// SpecName.php (规格名模型)
public function specValues() {
return $this->hasMany(SpecValue::class);
}
保存规格(后端接收数据)
前端提交的数据格式通常是:
{
"product_name": "T恤",
"specs": [
{ "name": "颜色", "values": ["红色", "蓝色"] },
{ "name": "尺寸", "values": ["M", "L", "XL"] }
],
"skus": [
{ "specs": {"颜色":"红色","尺寸":"M"}, "price":99, "stock":100, "image":"red_m.jpg" },
{ "specs": {"颜色":"红色","尺寸":"L"}, "price":109, "stock":50, "image":"red_l.jpg" }
]
}
控制器处理逻辑:
public function store(Request $request) {
// 1. 保存商品基本信息
$product = Product::create(['name' => $request->product_name]);
// 2. 保存规格名和规格值
$specValueIdMap = []; // 记录 "规格名_规格值" -> 数据库ID
foreach ($request->specs as $spec) {
$specName = SpecName::create([
'product_id' => $product->id,
'name' => $spec['name']
]);
foreach ($spec['values'] as $value) {
$specValue = SpecValue::create([
'spec_name_id' => $specName->id,
'value' => $value
]);
$specValueIdMap[$spec['name'] . '_' . $value] = $specValue->id;
}
}
// 3. 保存SKU
foreach ($request->skus as $skuData) {
$ids = [];
foreach ($skuData['specs'] as $specName => $specValue) {
$ids[] = $specValueIdMap[$specName . '_' . $specValue];
}
sort($ids); // 排序后组合,便于查询
SkuStock::create([
'product_id' => $product->id,
'sku_code' => implode('-', $ids) . '-' . $product->id,
'price' => $skuData['price'],
'stock' => $skuData['stock'],
'image' => $skuData['image'],
'spec_value_ids' => implode(',', $ids)
]);
}
return success();
}
前端展示与选择(关键)
当用户选择规格时,需要根据已选的规格值,动态过滤可选的SKU。
数据结构示例(传给前端):
{
"specs": [
{
"name": "颜色",
"values": [
{"id":1, "value":"红色", "disabled": false},
{"id":2, "value":"蓝色", "disabled": true} // 该规格组合缺货
]
},
{
"name": "尺寸",
"values": [
{"id":3, "value":"M", "disabled": false},
{"id":4, "value":"L", "disabled": false}
]
}
],
"skus": [
{"id":1, "spec_value_ids": "1,3", "price":99, "stock":100, "image":"red_m.jpg"},
{"id":2, "spec_value_ids": "1,4", "price":109, "stock":0, "image":"red_l.jpg"}
],
"default_sku": null
}
前端JavaScript核心逻辑(以Vue为例):
// 当用户点击某个规格值时
function selectSpec(specNameIndex, specValueIndex) {
// 1. 切换选中状态
// 2. 根据当前已选中的规格值ID组合,去sku列表中查找匹配的SKU
// 3. 根据匹配结果,高亮或禁用未选中的规格值
// 4. 更新展示的价格、库存、图片
// 简化示例:遍历所有规格值,检查是否存在包含该规格值的SKU
this.specs.forEach((spec, i) => {
spec.values.forEach(val => {
// 检查是否有SKU包含 val.id 且 所有已选规格值也包含
val.disabled = !this.skus.some(sku => {
const skuIds = sku.spec_value_ids.split(',').map(Number);
// sku必须包含当前val,且包含所有已选的其他规格值
return skuIds.includes(val.id) && this.selectedSpecIds.every(selectedId =>
selectedId === val.id || skuIds.includes(selectedId)
);
});
});
});
}
高级优化建议
- 规格组合图片:SKU表增加
image字段,可为不同规格组合设置不同主图,提升用户体验。 - 规格组合编码:
sku_code可用规格值ID拼接(如1-4-7),或使用商品ID+随机码,用于仓库管理。 - 批量编辑:后台提供按规格名批量更新价格/库存的功能,如“红色尺码统一加10元”。
- 规格值排序:前端拖动排序,后端用
sort_order字段控制展示顺序。 - 缓存优化:SKU量大的商品(如几十个规格组合),可将规格树和SKU列表缓存到Redis,减少数据库查询。
- 缺货规格隐藏:对于库存为0的SKU,其规格值可显示为灰色并不可点击。
常见问题与解决方案
- 不同规格组合价格差异大:不要让所有SKU共用主表价格,而是以SKU表的价格为准。
- 规格值数量过多:不建议超过3-4个规格维度(如颜色、尺寸),否则SKU组合数会呈指数级增长(如10色10尺寸5版本=500个SKU),管理成本极高。
- 无规格商品:特殊处理,直接使用商品的默认价格和库存,SKU表只存一条记录,
spec_value_ids为空。
- 设计核心:
spec_names+spec_values+sku_stock三表 +products主表。 - 核心逻辑:SKU表通过
spec_value_ids字段存储组合关系,前端通过该字段实现规格联动。 - 最佳实践:前后端分离,前端负责动态渲染与禁用逻辑,后端负责数据维护与存储。
这个方案适用于大多数电商系统(如商城、服装、电子产品),扩展性好,易于维护,如果SKU量特别大(如上万),可以考虑使用专门的多SKU插件或自建缓存层。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播