最近在做商品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='商品已選屬性表';
以上僅作記錄,如大神們有更好方案請不吝賜教。