最近在做商品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='商品已選屬性表';



    以上僅作記錄,如大神們有更好方案請不吝賜教。