thinkphp6里duplicate函数的奇葩实现

456 阅读2分钟

工作中实现业务,大多会有这样的一段逻辑:

如果某条数据在数据库里存在,就更新,否则就插入。


用原生mysql语句实现就是:
`INSERT INTO ... ON DUPLICATE KEY UPDATE ...`

如果直接用if判断来写,也就是如下:

if(dataExist){
    //update data
}else{
    //insert data
}

为了方便快捷,就直接用thinkphp框架里的dupliate函数了。



第一次是我自己先入为主了,以为duplicate函数参数,就是传入唯一索引所涉及到的字段名: image.png

但其实正确的写法理应如下:

$resVal = $modelBlock->strict(false)->duplicate($insertData)->insert($insertData, true);

上面这段代码,一定要数据库表存在唯一索引,且$insertData包含索引的字段数据(equipment_id和field_name),功能才生效。

本以为坑是被我踩得明明白白的了,结果我今天遇到类似的业务,代码写成如下,数据库死活无法更新:

$condition = [
    'equipment_id' => $lemsData['id'],
    'data_date' => $dayDate,
];
$saveData = [
    'equipment_id' => $lemsData['id'],
    'data_date' => $dayDate,
    'device_id' => $lemsData['device_id'],
    'is_system' => 1,  //标志为系统修改
    'status' => 1,
    'value'  => $runTime,
    'remark'    => 'merge run time',
    'description' => json_encode($listRunRecord),
    'update_time' => DateUtil::getCurrentDatetime()
];
$res = $modelManual->strict(false)->duplicate($condition)->insert($saveData, true);

equipment_id和data_date就是数据库的唯一性组合索引unique key的相关字段。
最后检查上次的笔记,才发现上次前后都用的同一个数组,这个时候才感觉这方法设计得好冗余。

然后我突发奇想,想试试前后数组数据不一样会怎样?它到底是更新哪一个?
试完之后,感觉更奇葩了。测试代码大致如下:

        $saveData1 = [
            'equipment_id' => $lemsData['id'],
            'data_date' => $dayDate,
            'device_id' => $lemsData['device_id'],
            'is_system' => 1,  //标志为系统修改
            'status' => 1,
            'value'  => $runTime,
//            'remark'    => 'merge run time23',
            'description' => json_encode($listRunRecord),
            'update_time' => '2023-01-01 02:00:00'
        ];
        $saveData = [
            'equipment_id' => $lemsData['id'],
            'data_date' => $dayDate,
//            'device_id' => $lemsData['device_id'],
//            'is_system' => 1,  //标志为系统修改
//            'status' => 1,
//            'value'  => $runTime,
            'remark'    => 'merge run time11',
//            'description' => json_encode($listRunRecord),
//            'update_time' => DateUtil::getCurrentDatetime()
        ];
        $res = $modelManual->strict(false)->duplicate($saveData1)->insert($saveData, true);

经过详细测试,发现insert方法的参数,必须要有唯一索引性相关的字段数据,否则数据无法更新。
原本我直接以为,这个是作为duplicate函数的参数的,结果人家放在了后面。
更新的数据,放在insert参数里,全都“无效”,哪怕$saveData里有的数据在$saveData1里没有,也没法更新。
反而都是在duplicate函数参数里的数据,才可以被更新到,看下图:

image.png

insert/save方法是最直观意义上的插入和保存,居然参数的数据全是废物(除了索引字段),大家就说奇葩不奇葩。

本为了省事,想少写3行代码,一来简洁,二来节省时间,结果却折腾了一整个下午,真有点让人哭笑不得。