学习 · 2021年4月12日 0

Laravel导入功能

复盘下上周:通过excel表格批量导入商品库存及成本。

通过表格中的商品货号找到该商品,再通过匹配表格中的规格名称导入对应数据。

正式导入前,会先将匹配不到的数据返回至前台进行展示。

问题在于,平台伙伴也许对规格名称有其他的叫法,所以还需要在导入的基础上增加一个信息配置——将伙伴的属性名称对应平台的系统属性名称上。(当然,平台的一个系统属性有可能对应多个伙伴属性名称)

表格导入写过不下5次了,这次却翻车翻得最惨——再次感慨自己的基础知识薄弱。

确定表头

最开始想好的表头顺序为:商品货号、规格1……规格n、库存、成本。(示例:maatwebsite)

出于用户需求,表头必须是中文。所以读取数据就只能通过下标数值进行读取。

\Excel::load($file_path, function ($reader) use ($data){
//通过表头判断表格是否合法
$temp = clone $reader;
$header_now = array_filter($temp->takeRows(3)->toArray()[0]??[]);
$header = ['商品货号','库存','成本'];
foreach($header as $k=>$v){
    if(!isset($header_now[$k])||!$header_now[$k]==$v){
        throw new GoodsException('表格不合法');
    }
}

$data = $reader->all()->toArray();
}

首先判断表头是否合法,咱不能啥表都往这儿送。之所以clone $reader是因为$reader只能读取一次,类似读一次指针就停在了最终位置,无法反复使用。判断了表头还得用它获取所有的数据。

接着,就要判断表格内容是否合法了。

$item_sheet = array_filter($item_sheet);
if(empty($item_sheet)){
    continue;
}
if(!isset($item_sheet[0])){
    continue;
}

如果出现某行无数据,就直接跳过。这也是上方第一行代码为什么要array_filter的意义。但问题就来了——如果某行表格数据也不合法,库存与成本都没有数据,在不确定规格属性到底有几个的情况下,无法通过数值下标去获取到。

所以这样的表格格式无法使用。

调整后的表头:商品货号、库存、成本、规格1……规格n

将无法确定的规格放在表头最后,这样能够通过下方对库存与成本进行获取。

isset($item_sheet[1]) ? $item_sheet[1] : 0
isset($item_sheet[2]) ? $item_sheet[2] : ''

通过规格名称组合成唯一的sku

通过获取货号,可以拿到对应商品下的所有规格。

判断表格里的名称是否存在,如果不存在,再去信息配置中寻找是否有伙伴配置的不一样的属性名称。最终拿到所有属性id。

还没完,拿到所有对应的属性后,还要通过算法找出不同顺序组合下的组合再进行最后的匹配。

祭出烧掉好几十万脑细胞的笛卡尔乘积算法:

protected function cartesian($array,$arr = array()){
        //去除第一个元素
        $first = array_shift($array);
        //判断是否是第一次进行拼接
        if(count($arr) > 0) {
            foreach ($arr as $k => $val) {
                foreach ($first as $key => $value) {
                    $arr2[] = $val.','.$value;
                }
            }
        }else{
            foreach ($first as $key => $value) {
                $arr2[] = $value;
            }
        }

        //递归进行拼接
        if(count($array) > 0){
            $arr2 = $this->cartesian($array,$arr2);
        }

        $tmp = [];

        foreach ($arr2 as $v){
            $tmp_arr = explode(',',$v);
            sort($tmp_arr);
            $tmp[] = implode('__',$tmp_arr);
        }
        //返回最终笛卡尔积
        return $tmp;
    }

最后,匹配商品对应的唯一的sku。

我的问题

1、最开始想的只用名称去找就好了,但是如果有信息配置里的名称与系统名称重合时,我的名称组合就无法找准是哪一个了。这个最开始就该想到。

2、表格式的问题也是如此。

3、数据结构不清晰。

综上,丧了我一上午,还是要谢谢我的锅巴。