相信熟悉laravel的童鞋都知道,laravel有批量一次性插入多条记录,却没有一次性按条件更新多条记录。这篇文章就带你们了解Laravel防止SQL注入的方法,有兴趣的话可以前往laravel防止sql注入了解更过相关内容。

是否羡慕thinkphp的saveAll,是否羡慕ci的update_batch,但如此优雅的laravel怎么就没有类似的批量更新的方法呢?


以下实例结合laravel的Eloquent做了调整,可有效防止sql注入。




<?php namespace App\Models; use DB; use Illuminate\Database\Eloquent\Model; /**
 * 学生表模型
 */ class Students extends Model {
 protected $table = 'students';

 //批量更新  public function updateBatch($multipleData = [])  {
  try {
   if (empty($multipleData)) {
    throw new \Exception("数据不能为空");
   }
   $tableName = DB::getTablePrefix() . $this->getTable(); // 表名    $firstRow = current($multipleData);

   $updateColumn = array_keys($firstRow);
   // 默认以id为条件更新,如果没有ID则以第一个字段为条件    $referenceColumn = isset($firstRow['id']) ? 'id' : current($updateColumn);
   unset($updateColumn[0]);
   // 拼接sql语句    $updateSql = "UPDATE " . $tableName . " SET ";
   $sets  = [];
   $bindings = [];
   foreach ($updateColumn as $uColumn) {
    $setSql = "`" . $uColumn . "` = CASE ";
    foreach ($multipleData as $data) {
     $setSql .= "WHEN `" . $referenceColumn . "` = ? THEN ? ";
     $bindings[] = $data[$referenceColumn];
     $bindings[] = $data[$uColumn];
    }
    $setSql .= "ELSE `" . $uColumn . "` END ";
    $sets[] = $setSql;
   }
   $updateSql .= implode(', ', $sets);
   $whereIn = collect($multipleData)->pluck($referenceColumn)->values()->all();
   $bindings = array_merge($bindings, $whereIn);
   $whereIn = rtrim(str_repeat('?,', count($whereIn)), ',');
   $updateSql = rtrim($updateSql, ", ") . " WHERE `" . $referenceColumn . "` IN (" . $whereIn . ")";
   // 传入预处理sql语句和对应绑定数据    return DB::update($updateSql, $bindings);
  } catch (\Exception $e) {
   return false;
  }
 }
} 

可以根据自己的需求再做调整,下面是用法实例:




// 要批量更新的数组 $students = [
 ['id' => 1, 'name' => '张三', 'email' => [email protected]'],
 ['id' => 2, 'name' => '李四', 'email' => [email protected]'],
]; // 批量更新 app(Students::class)->updateBatch($students);

生成的SQL语句如下:




UPDATE pre_students 
SET NAME = CASE 
WHEN id = 1 THEN
  '张三' 
WHEN id = 2 THEN
  '李四' 
ELSE
  NAME 
END,
 email = CASE 
WHEN id = 1 THEN
  [email protected]' 
WHEN id = 2 THEN
  [email protected]' 
ELSE
  email 
END 
WHERE
  id IN (1, 2)

另外一种实现方法,使用MySQL的case…when实现数据库的批量更新

mysql case…when的用法

MySQL 的 case when 的语法有两种:

简单函数

CASE [col_name] WHEN [value1] THEN [result1]…ELSE [default] END

CASE [col_name] WHEN [value1] THEN [result1]…ELSE [default] END:枚举这个字段所有可能的值

示例:




UPDATE base_info SET 
city_id = CASE id 
WHEN 1 THEN 100010 
WHEN 2 THEN 100011 
WHEN 3 THEN 100012 
END 
WHERE id IN (1,2,3)

这句sql的意思是,更新city_id 字段:

如果id=1 则city_id 的值为100010,

如果id=2 则 city_id 的值为100011,

如果id=3 则 city_id 的值为100012。

即是将条件语句写在了一起。

这里的where部分不影响代码的执行,但是会提高sql执行的效率。

确保sql语句仅执行需要修改的行数,这里只有3条数据进行更新,而where子句确保只有3行数据执行。


laravel在model方法中封装该批量更新的方法




//批量更新  public function updateBatch($multipleData = [])  {
  try {
   if (empty($multipleData)) {
    Log::info("批量更新数据为空");
    return false;
   }
   $tableName = $this->table; // 表名    $firstRow = current($multipleData);

  $updateColumn = array_keys($firstRow);
  // 默认以id为条件更新,如果没有ID则以第一个字段为条件   $referenceColumn = isset($firstRow['id']) ? 'id' : current($updateColumn);
  unset($updateColumn[0]);
  // 拼接sql语句   $updateSql = "UPDATE " . $tableName . " SET ";
  $sets = [];
  $bindings = [];
  foreach ($updateColumn as $uColumn) {
   $setSql = "`" . $uColumn . "` = CASE ";
   foreach ($multipleData as $data) {
    $setSql .= "WHEN `" . $referenceColumn . "` = ? THEN ? ";
    $bindings[] = $data[$referenceColumn];
    $bindings[] = $data[$uColumn];
   }
   $setSql .= "ELSE `" . $uColumn . "` END ";
   $sets[] = $setSql;
  }
  $updateSql .= implode(', ', $sets);
  $whereIn = collect($multipleData)->pluck($referenceColumn)->values()->all();
  $bindings = array_merge($bindings, $whereIn);
  $whereIn = rtrim(str_repeat('?,', count($whereIn)), ',');
  $updateSql = rtrim($updateSql, ", ") . " WHERE `" . $referenceColumn . "` IN (" . $whereIn . ")";
  Log::info($updateSql);
  // 传入预处理sql语句和对应绑定数据   return DB::update($updateSql, $bindings);
 } catch (\Exception $e) {
  return false;
 }
}

在service层拼接需要更新的数据,并调用该函数




 foreach ($taskInfo as $info) {
   $cityId = $info['requirement']['city_ids'];
   //此处省略n行代码    $cityInfo = ['id' => $dataId[$info['id']]['id'], 'city_id' => $cityId];
   if ($cityInfo) {
    $cityInfos[] = $cityInfo;
   }
  }
  $res = $this->waybillDriverInfoModel->updateBatch($cityInfos);
 }

拼接的批量更新的数组格式为:

$students = [

[‘id’ => 1, ‘city_id’ => ‘100010′],

[‘id’ => 2, ‘city_id’ => ‘100011′],

];

生成的SQL语句如下:




UPDATE base_info  
SET `city_id` = CASE  
WHEN `id` = 1 THEN 100010  
WHEN `id` = 2 THEN 100011  
ELSE `city_id`  
END  
WHERE `id` IN (1,2)

因为每次只操作20条数据,所以这样拼接的字符串不会太长,符合mysql的字符串长度的要求,解决问题。更多相关技术咨询,欢迎前往并关注:编程学习网。