商品模型新增
接口文档
请求地址
POST /types
需进行token认证
示例: http://adminapi.tbyue.com/types
请求参数
名称 | 类型 | 必填 | 备注 |
---|---|---|---|
type_name | string | 是 | 模型名 |
spec | int | 是 | 规格数组 |
attr | int | 是 | 属性数组 |
参数示例:
type_name: 手机
spec[0][name]: 颜色
spec[0][sort]: 50
spec[0][value][]: 白色
spec[0][value][]: 黑色
spec[1][name]: 内存
spec[1][sort]: 50
spec[1][value][]: 64G
spec[1][value][]: 128G
attr[0][name]: 毛重
attr[0][sort]: 50
attr[0][value][]:
attr[1][name]: 产地
attr[1][sort]: 50
attr[1][value][]: 进口
attr[1][value][]: 国产
返回数据
名称 | 备注 |
---|---|
id | 模型id |
type_name | 模型名 |
返回示例
{
"id": 16,
"type_name": "手机"
}
实现思路
1.观察MVC项目中商品后台添加商品模型的表单,类推出了一个数组:
$data = [
'type_name' => '西裤',
'attr' => [
[
'name' => '毛重',
'sort' => 100,
'value' => ['500g', '', '2000g', '5000g', '']
],
[
'name' => '产地',
'sort' => 100,
'value' => ['国产', '港货', '美版']
]
],
'spec' => [
[
'name' => "颜色",
'sort' => 100,
'value' => ['黑色', '灰色', '蓝色', '红色', '白色', '', '']
], [
'name' => "尺码",
'sort' => 100,
'value' => ['S', 'L', 'M', 'XL', 'XXL', "XXXL"]
],
[
'name' => "面料",
'sort' => '100',
'value' => ['棉麻','涤纶','纯棉','羊毛']
]
],
];
2.针对上面的数组,可能会有空值现象,要进行适当过滤
3.首先要将商品类型信息入库,因为后续跟商品类型id有关联
4.将商品属性信息入库,此时涉及到数组的组装,以及属性值的转换
5.将商品类型对应的规格信息入库,此时也涉及到数组的组装,跟商品类型id有关
6.将商品类型对应的规格的规格值入库,此时还涉及到数组转换,关联的有:商品类型id和商品类型对应的规格id
7.整个逻辑要保证一致性和完整性,必须借助事务实现
8.由于逻辑较为复杂,特将控制器所应执行的逻辑分别拆分至Businesses和Common中做二次封装,实现代码的高度解耦
在商品的类型模型中定义添加方法
/**
* 商品类型入库
* @param $data
* @return Type|Model
*/
public static function addType($data){
return self::create($data,['type_name']);
}
在控制器层调用逻辑层进行添加
// 调用逻辑层将模型信息入库并得到模型的主键id
$type = TypesBusiness::addType($data);
逻辑层执行商品模型添加
/**
* 添加模型信息入库
* @param $data
*/
public static function addType($data)
{
try {
// 先将商品类型名称入库,此时能获得到主键id
$type = Type::addType($data);
} catch (DbException $exception) {
throw new Exception("商品类型入库失败");
}
self::$typeId = $type->id;
// 返回新增的商品模型
return ['type_id'=>$type->id,'type_name'=>$type->type_name];
}
思考:此处为何使用self将id保存起来呢?
在Common中封装数组处理的函数(针对商品属性和商品规格的非空处理)
// 四维数组空值处理的公共函数
if(!function_exists("arrayFilter")){
function arrayFilter($data,$filed){
// 过滤数组中的空元素
foreach ($data[$filed] as $key => $val) {
// 判断规格名称|属性名称是否填写
if (trim($val['name']) == "") {
// 直接将整个数组销毁
unset($data[$filed][$key]);
}
// 循环处理规格值
foreach ($val['value'] as $k => $v) {
// 判断规格值是否填写
if (trim($v) == "") {
unset($data[$filed][$key]['value'][$k]);
}
}
// 判断整个规格值是否一个都没有
if (count($val['value']) == 0) {
// 直接将整个数组销毁
unset($data[$filed][$key]);
}
}
return $data;
}
}
思考:为何将数组处理的方法放置在common中呢?
在控制器层调用公共函数处理将规格名称数组过滤空值
$data = arrayFilter($data, "spec");
调用逻辑层将规格信息入库保存
TypesBusiness::addSpec($data);
规格信息入库前处理逻辑
/**
* 规格信息入库保存的逻辑
* @param $data
* @return \think\Collection
* @throws \Exception\
*/
public static function addSpec($data)
{
// 组装spec表所需数据
$spec = [];
foreach ($data['spec'] as $val) {
$spec[] = [
'type_id' => self::$typeId,
'spec_name' => $val['name'],
'sort' => $val['sort']
];
}
try {
// 将商品类型对应的规格入库保存
self::$specData = (new Spec())->addSpec($spec);
} catch (DbException $exception) {
throw new Exception("商品规格入库失败");
}
}
规格模型层
/**
* 商品规格名称入库
* @param $data
* @return \think\Collection
* @throws \Exception
*/
public function addSpec($data){
return $this->saveAll($data);
}
调用逻辑层将规格值信息入库保存
TypesBusiness::addSpecValue($data);
规格值信息入库前处理逻辑
/**
* 规格值信息入库保存的逻辑
* @param $data
* @param $specData
* @throws \Exception
*/
public static function addSpecValue($data)
{
// 取出specValue的值
foreach ($data['spec'] as $key => $val) {
foreach ($val['value'] as $k => $v) {
$specValue[] = [
'spec_value' => $v,
'type_id' => self::$typeId,
'spec_id' => self::$specData[$key]['id']
];
}
}
try {
// 将规格值入库保存
(new SpecValue())->addSpecValue($specValue);
} catch (DbException $exception) {
throw new Exception("商品规格值入库失败");
}
}
规格值模型
/**
* 规格值批量入库
* @param $data
* @return \think\Collection
* @throws \Exception
*/
public function addSpecValue($data){
return $this->saveAll($data);
}
调用公共函数处理将属性名称数组过滤空值
$data = arrayFilter($data, "attr");
调用逻辑层将属性信息入库保存
TypesBusiness::addAttr($data);
属性信息入库前处理逻辑
/**
* 属性信息入库保存的逻辑
* @param $data
* @return \think\Collection
* @throws \Exception
*/
public static function addAttr($data)
{
// 组装商品属性的数组
$attr = [];
foreach ($data['attr'] as $key => $val) {
$attr[] = [
'attr_name' => $val['name'],
'type_id' => self::$typeId,
'attr_values' => implode(",", $val['value'])
];
}
try {
// 将商品属性入库保存
(new Attribute())->addAttr($attr);
} catch (DbException $exception) {
throw new Exception("商品属性入库失败");
}
}
属性模型
/**
* 商品属性批量添加
* @param $data
* @return \think\Collection
* @throws \Exception
*/
public function addAttr($data){
return $this->saveAll($data);
}
上面的操作都要基于:异常处理和事务处理,逻辑缜密
整体控制器层代码
/**
* 商品模型新增
* @param Request $request
* @return Json
*/
public function save(Request $request)
{
// 假设接收的表单数据
$data = [
'type_name' => '西裤',
'attr' => [
[
'name' => '毛重',
'sort' => 100,
'value' => ['500g', '', '2000g', '5000g', '']
],
[
'name' => '产地',
'sort' => 100,
'value' => ['国产', '港货', '美版']
]
],
'spec' => [
[
'name' => "颜色",
'sort' => 100,
'value' => ['黑色', '灰色', '蓝色', '红色', '白色', '', '']
], [
'name' => "尺码",
'sort' => 100,
'value' => ['S', 'L', 'M', 'XL', 'XXL', "XXXL"]
],
[
'name' => "面料",
'sort' => '100',
'value' => ['棉麻','涤纶','纯棉','羊毛']
]
],
];
// 校验数据
try {
$this->validate($data, [
'type_name|商品类型' => "require",
'attr|商品属性' => "array",
'spec|商品规格' => 'array'
]);
} catch (ValidateException $exception) {
return fail($exception->getMessage());
}
// 启动事务
Db::startTrans();
try {
// 调用逻辑层将模型信息入库并得到模型的主键id
$type = TypesBusiness::addType($data);
// 调用公共函数处理将规格名称数组过滤空值
$data = arrayFilter($data, "spec");
// 调用逻辑层将规格信息入库保存
TypesBusiness::addSpec($data);
// 调用逻辑层将规格值信息入库保存
TypesBusiness::addSpecValue($data);
// 调用公共函数处理将规格名称数组过滤空值
$data = arrayFilter($data, "attr");
// 调用逻辑层将属性信息入库保存
TypesBusiness::addAttr($data);
// 提交事务
Db::commit();
// 返回数据
return success($type);
} catch (\Exception $e) {
// 回滚事务
Db::rollback();
// 给出错误提示
return fail($e->getMessage());
}
}
postman测试时该功能表单参考:
<!--参考html表单-->
<form>
<label>模型名:</label>
<input type="text" name="type_name" value="">
<label>规格:</label>
<input type="text" name="spec[0][name]" value="">
<input type="text" name="spec[0][sort]" value="">
<input type="text" name="spec[0][vlaue][]" value="">
<input type="text" name="spec[0][value][]" value="">
<input type="text" name="spec[0][value][]" value="">
<input type="text" name="spec[1][name]" value="">
<input type="text" name="spec[1][sort]" value="">
<input type="text" name="spec[1][value][]" value="">
<input type="text" name="spec[1][value][]" value="">
<input type="text" name="spec[1][value][]" value="">
<label>属性:</label>
<input type="text" name="attr[0][name]" value="">
<input type="text" name="attr[0][sort]" value="">
<input type="text" name="attr[0][value][]" value="">
<input type="text" name="attr[1][name]" value="">
<input type="text" name="attr[1][sort]" value="">
<input type="text" name="attr[1][vlaue][]" value="">
<input type="text" name="attr[1][value][]" value="">
</form>
参考数组格式:
//参数数组参考:
$params = [
'type_name' => '手机',
'spec' => [
['name' => '颜色', 'sort' => 50, 'value'=>['黑色', '白色', '金色']],
['name' => '内存', 'sort' => 50, 'value'=>['64G', '128G', '256G']],
],
'attr' => [
['name' => '毛重', 'sort'=>50, 'value' => []],
['name' => '产地', 'sort'=>50, 'value' => ['进口', '国产']],
]
]