Laravelで一括の値を更新したい時の処理を簡単にまとめてみました。
use Illuminate\Support\Facades\DB;
public function bulkUpdate(updateRecords)
{
$updates = [];
foreach ($updateRecords as $updateRecord) {
$updates[$updateRecord['id']] = $updateRecord['quantity'];
}
$ids = array_keys($updates);
$sql = "UPDATE update_table SET quantity = CASE id ";
foreach ($updates as $id => $quantity) {
$sql .= "WHEN $id THEN $quantity ";
}
$sql .= "END WHERE id IN (" . implode(',', $ids) . ")";
DB::statement($sql);
}
基本的な書き方は上記ですが、この実装にはSQLインジェクションの重大なリスクがあります。
問題点
上記のコードだと、パラメータのバインドを記述していないです。
$id
と$quantity
が直接文字列に挿入されているので、悪意のあるコードが含まれていた場合、SQLインジェクション攻撃が可能になります。
なので、以下のように修正する必要があります。
修正コード
use Illuminate\Support\Facades\DB;
public function bulkUpdate($updateRecords)
{
$updates = [];
foreach ($updateRecords as $updateRecord) {
$updates[$updateRecord['id']] = $updateRecord['quantity'];
}
$ids = array_keys($updates);
$sql = "UPDATE update_table SET quantity = CASE id ";
$bindings = [];
foreach ($updates as $id => $quantity) {
$sql .= "WHEN ? THEN ? ";
$bindings[] = $id;
$bindings[] = $quantity;
}
$sql .= "END WHERE id IN (" . implode(',', array_fill(0, count($ids), '?')) . ")";
foreach ($ids as $id) {
$bindings[] = $id;
}
DB::statement($sql, $bindings);
}
変更点
-
WHEN $id THEN $quantity → WHEN ? THEN ?
とし、$bindings
に$id
と$quantity
を順に追加 -
WHERE id IN (...)
もプレースホルダに置き換え、array_fill
で?
を並べて$bindings
に$ids
を追加
実行されるSQL
UPDATE update_table
SET quantity = CASE id
WHEN ? THEN ?
WHEN ? THEN ?
END
WHERE
id IN (?, ?)
;
// binding配列は以下の通り
[1, 10, 2, 20, 1, 2]
1つずつupdateのクエリを流す処理を書くのをゼロから調べてコードを書くのが面倒なので、メモとしていつでも引っ張ってこれるように、今回記事にしました。
少しでもお役に立つことができれば嬉しいです。