最近在做商品sku的时候遇到了一些困惑,记录一下,以备日后会看。



    先说一说需求:

        1.根据选中的SKU,显示对应的SKU图片,没有选中的SKU时,默认展示SPU商品图,单击放大预览;

        2.每个属性下属性值有多个时,默认不选,只有一个时,默认选中;

        3.后台没有设置SKU时,则无属性值展示;

        4.选中交互:选中某一个属性下属性值时,检查其他属性的属性值,哪些不能组合为一个SKU,不能组合的属性值只读,原选中的则取消选中。


    已上就是给出的文字需求,在需求讨论的时候产品定了调调,通过既定的属性去确定一个sku,即所有未选择的属性假定一个初始值。一听心里乐了,所有选中的属性都已知,排序之后再匹配不就是轻松确定sku了嘛,美滋滋。

/**
 *  获取商品sku属性(初始默认选中,已经被否了呀)
 */
public function goods_sku_version_1 (){
    $goodsId = $this->post['goods_id'] or $this->response(201, 'goods_id不能为空');
    $catId = mall_database_execute('goods')
        ->where(['goods_id'  => $goodsId, 'is_on_sale'=> 1, 'is_delete' => 0])
        ->getField('goods_type')
        or $this->response(208, '商品不存在');
    //构建查询条件
    $where = ['gs.goods_id'=> $goodsId];
    $attrArr = [];
    if (!empty($this->post['attr_id'])){
        $attrArr = explode(',', $this->post['attr_id']);
        sort($attrArr); //升序
        $where['gs.attr_id_arr'] = implode(',', $attrArr);
    }
    //获取sku信息
    $this->rows['info'] = mall_database_execute('goods_sku')->alias('AS gs')
        ->where($where)
        ->find() or $this->response(208, 'sku不存在');
    if (empty($this->post['attr_id'])) $attrArr = explode(',', $this->rows['info']['attr_id_arr']);
    //获取顶级sku列表
    $this->rows['attribute'] = mall_database_execute('goods_sku_list')->alias('AS gsl')
        ->field('ga.attr_id, ga.attr_type, ga.attr_name, gsl.attr_values_arr')
        ->join(mall_database_table('goods_attribute').' AS ga ON ga.attr_id = gsl.attr_id', 'LEFT')
        ->where([
            'gsl.goods_id'  => $goodsId,
            'gsl.status'    => 1
        ])
        ->select() ? : '';
    if (!empty($this->rows['attribute'])){
        //通过二定一待,确定sku
        foreach ($this->rows['attribute'] as $k=>&$v){
            $newAttrArr = $attrArr;
            $newAttrArr[$k] = '%';
            $str = implode(',', $newAttrArr);
            $v['option'] = mall_database_execute('goods_sku')
                ->field('goods_sku_id,attr_id_arr,status'
                )
                ->where([
                    'goods_id'      => $goodsId,
                    'attr_id_arr'   => ['LIKE', $str]
                ])
                ->select();
            if (!empty($v['option'])){
                foreach ($v['option'] as &$vo){
                    $newAttrArr = explode(',', $vo['attr_id_arr']);
                    $attrInfo = mall_database_execute('goods_attribute')
                        ->field('attr_id, attr_type, attr_name')
                        ->where([
                            'attr_id'  => $newAttrArr[$k]
                        ])
                        ->find();
                    unset($vo['attr_id_arr']);
                    $vo = array_merge($attrInfo, $vo);
                }
            }
        }
    }
    $this->response();
}



    本以为这样就完事了,结果没过多久需求就改了,不假定每个属性的初始值,根据用户当前选择动态匹配sku,GG。这个方案一开始没想明白的点在于,基于用户当前选择,推算所有属性的节点是否还有可选的可能(可用sku),之前想到过这一层面,想着偷懒完成需求即可,便没有深究。


/**
 * 基于用户当前属性选择状态,获取sku,及推算每个属性的可选状态
 */
public function goods_sku(){
    $goodsId = $this->post['goods_id'];
    //检测商品是否存在
    mall_database_execute('goods')
        ->where(['goods_id'  => $goodsId, 'is_on_sale'=> 1, 'is_delete' => 0])
        ->getField('goods_id')
        or $this->response(208, '商品不存在');
    $where = [
        'goods_id'  => $goodsId,
        'status'    => 1
    ];
    //获取所有可选sku的属性组合
    $allSkuLists = array_column(mall_database_execute('goods_sku')
        ->field('attr_id_arr')
        ->where($where)
        ->select(), 'attr_id_arr');
    $this->rows['sku_num'] = count($allSkuLists);
    if (!empty($this->post['attr_id'])){
        $attrArr = explode(',', $this->post['attr_id']);
        foreach ($attrArr as &$vv){
            if ($vv == '') $vv = '%';
        }
        $where['old_id_arr'] = ['LIKE', implode(',', $attrArr)];
    }
    //查询一个当前用户选择状态下满足条件的组合(先这样,等产品出了可能组合排序规则再完善)
    $this->rows['info'] = mall_database_execute('goods_sku')
        ->field('goods_sku_id,price,goods_number,attr_value,goods_thumb')
        ->where($where)
        ->find() ? : '';
    if ($this->rows['info']['goods_thumb']) $this->rows['info']['goods_thumb'] = change_file_path($this->rows['info']['goods_thumb']);
    //获取规格
    $attribute = mall_database_execute('goods_sku_list')->alias('AS gsl')
        ->field('ga.attr_id, ga.attr_type, ga.attr_name, gsl.attr_values_arr')
        ->join(mall_database_table('goods_attribute').' AS ga ON ga.attr_id = gsl.attr_id', 'LEFT')
        ->where([
            'gsl.goods_id'  => $goodsId,
            'gsl.status'    => 1
        ])
        ->order('attr_type asc, attr_id asc')
        ->select();
    if (!empty($attribute)){
        //遍历获取所有规格的属性,并假定每种属性都不可选
        foreach ($attribute as $k=>&$v){
            $v['option'] = mall_database_execute('goods_attribute')
                ->field('attr_id,attr_type,attr_name,2 as status')
                ->where([
                    'attr_id'   => ['IN', $v['attr_values_arr']],
                    'status'    => 1
                ])
                ->order('attr_id asc')
                ->select();
            unset($v['attr_values_arr']);
            if (!empty($allSkuLists)){
                //遍历所有属性判断基于用户选择状态下属性是否有可选方案
                foreach ($v['option'] as &$vo){
                    //获取用户当前选择
                    $checkSku = explode(',', $this->post['attr_id']);
                    //把当前属性与用户选择进行组合
                    $checkSku[$k] = $vo['attr_id'];
                    //遍历所有可能的组合列表,查找是否有匹配当前的组合
                    foreach ($allSkuLists as $vov){
                        //判断条件:如果我有的你都有那就对了
                        $intersect = array_intersect($checkSku, explode(',', $vov));
                        asort($intersect);
                        asort($checkSku);
                        //序列化后做比较
                        if (implode(',', $intersect) == implode(',', array_filter($checkSku))) $vo['status'] = '1';
                    }
                }
            }
        }
    }
    $this->rows['attribute'] = $attribute ? : '';
    $this->response();
}


附上表结构:

CREATE TABLE `fd_goods_sku` (
`goods_sku_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',
  `goods_id` int(11) NOT NULL,
  `old_id_arr` varchar(255) NOT NULL DEFAULT '' COMMENT '没排序之前的id组合',
  `attr_id_arr` varchar(255) NOT NULL DEFAULT '0' COMMENT '属性id',
  `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1正常,2失效',
  `price` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '售价,单位分',
  `goods_number` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '库存',
  `attr_value` varchar(255) NOT NULL DEFAULT '' COMMENT '商品自定义属性',
  `goods_thumb` varchar(255) NOT NULL DEFAULT '',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`goods_sku_id`),
  KEY `goods_id` (`goods_id`,`goods_sku_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2208 DEFAULT CHARSET=utf8mb4 COMMENT='商品sku表';


CREATE TABLE `fd_goods_sku_list` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `goods_id` int(10) unsigned NOT NULL,
  `attr_id` varchar(255) NOT NULL DEFAULT '' COMMENT '属性id',
  `attr_values_arr` varchar(255) NOT NULL DEFAULT '' COMMENT '属性值id',
  `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1正常,2失效',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=599 DEFAULT CHARSET=utf8mb4 COMMENT='商品已选属性表';



    以上仅作记录,如大神们有更好方案请不吝赐教。