質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
GET

GETとはHTTPが対応するリクエストメソッドの一つです。クライアントからサーバーへ送られたURLパラメータのデータを取得する時必要がある時に使われます。

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

mysqli

MySQLiはPHP5より導入されているデータベース用のドライバです。MySQL 4.1.3以降の新しい機能の利点をまとめています。

POST

POSTはHTTPプロトコルのリクエストメソッドです。ファイルをアップロードしたときや入力フォームが送信されたときなど、クライアントがデータをサーバに送る際に利用されます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

2回答

729閲覧

MySQLで指定(isset)されたカラムのみUPDATEしたい

iinuma-y

総合スコア2

GET

GETとはHTTPが対応するリクエストメソッドの一つです。クライアントからサーバーへ送られたURLパラメータのデータを取得する時必要がある時に使われます。

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

mysqli

MySQLiはPHP5より導入されているデータベース用のドライバです。MySQL 4.1.3以降の新しい機能の利点をまとめています。

POST

POSTはHTTPプロトコルのリクエストメソッドです。ファイルをアップロードしたときや入力フォームが送信されたときなど、クライアントがデータをサーバに送る際に利用されます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

1クリップ

投稿2022/09/08 11:20

前提・実現したいこと

クエリパラメータに渡されたカラムのみ値を更新する update.php を作っています。
DBへはmysqliを使用して接続しています。
カラムは二十数個ありますが、すべてを指定する必要はなく、指定されなかったカラムは更新しないようにしたいです。
パラメータ名とカラム名は対応しており、例えば「id=taro123&age=20&pref=Tokyo」とパラメータを渡した場合、
「UPDATE table SET age = 20, pref = 'Tokyo' WHERE id = 'taro123'」のようなSQLを発行したいです。

SQLインジェクション対策が必要で、できればSQL一文で実現したいと思っています。
※WHERE句のidをユーザーが指定できてしまう脆弱性は考えなくて構いません。

発生している問題・エラーメッセージ

SQLインジェクション対策のため、普段はprepare()を使用していますが、update.phpに指定できるパラメータの数も可変であるため、この場合にどのようにbind_param()に引数を与えればよいかわからず、止まってしまっています。

該当のソースコード

現状、以下のように一つずつissetでチェックして実装していますが、非常に冗長なため、もっと効率の良い方法はないでしょうか?

PHP

1$db = new mysqli($dbHost, $dbUser, $dbPasswd, $dbName); 2if ( isset($_POST['familyName']) ) { 3 $stmt = $db->prepare('UPDATE table SET familyName = ? WHERE id = ?'); 4 $stmt->bind_param('ss', $_POST['familyName'], $_POST['id']); 5 $stmt->execute(); 6 $stmt->close(); 7} 8if ( isset($_POST['firstName']) ) { 9 $stmt = $db->prepare('UPDATE table SET firstName = ? WHERE id = ?'); 10 $stmt->bind_param('ss', $_POST['firstName'], $_POST['id']); 11 $stmt->execute(); 12 $stmt->close(); 13} 14if ( isset($_POST['age']) ) { 15 $stmt = $db->prepare('UPDATE table SET age = ? WHERE id = ?'); 16 $stmt->bind_param('is', $_POST['age'], $_POST['id']); 17 $stmt->execute(); 18 $stmt->close(); 19} 20~~~以下、二十数カラム分続く~~~ 21$db->close();

考えた別解

  • prepare()を使用することを諦めて、事前にパラメータが不正でないか全てチェックし、安全なSQL文を作ってquery()で実行

PHP

1$sql = 'UPDATE table SET '; 2if ( isset($_POST['familyName']) ) 3 $sql .= "familyName = {$_POST['familyName']}, "; 4if ( isset($_POST['firstName']) ) 5 $sql .= "firstName = {$_POST['firstName']}, "; 6~~~以下続く~~~ 7$sql .= "WHERE id = {$_POST['id']}"; 8$db->query($sql);

これもif文が連続し、あまり変わらない気がしています。。。

  • 先にSELECTでデフォルト値を取得しておいて、全てのカラムを指定する

PHP

1$stmt = $db->prepare('SELECT * FROM table WHERE id = ?'); 2$stmt->bind_param('s', $_POST['id']); 3$stmt->execute(); 4$values = $stmt->get_result()->fetch_assoc(); 5$values['familyName'] == $_POST['familyName'] ?? $values['familyName']; 6$values['firstName'] == $_POST['firstName'] ?? $values['firstName']; 7~~~以下同~~~ 8$stmt = $db->prepare('UPDATE table SET familyName = ?, firstName = ?, ......... WHERE id = ?');

もっと美しい方法はありませんでしょうか?
またMySQL自体の扱いも不慣れで、直接の質問以外の箇所でも構文等に不自然、無駄な箇所があればご指摘頂けると幸いです。
どうか、皆様の知恵をお借りしたく、宜しくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

カラムは二十数個ありますが、すべてを指定する必要はなく、指定されなかったカラムは更新しないようにしたいです。

動的SQLという事になりますね。

SQLインジェクション対策のため、普段はprepare()を使用しています

prepareする事が、SQLインジェクション対策ではありません。
バインド変数を使用する事がSQLインジェクション対策です。

prepareしたいなら、SQLを固定にする必要がありますので、「指定されなかったカラムは更新しない」ではなく「指定されなかったカラムの値は変更しない」という事であれば、指定が無い場合のバインド変数にNullを設定する事で、以下の様なSQLで可能です。

SQL

1update XXX set yyy=coalesce(バインド変数, yyy)

投稿2022/09/08 15:10

sazi

総合スコア25173

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

iinuma-y

2022/09/09 02:04

>できればSQL一文で実現したいと思っています。 これに対する回答がまさにCOALESCE関数でした。非常にスマートな解決法、ありがとうございました。
guest

0

ベストアンサー

項目数がそれだけあるなら、その分だけSETを構成する必要があるので、それだけの分岐は必要です。

update.phpに指定できるパラメータの数も可変であるため、

配列に突っ込んでいってその分だけ回してbindすれば良し。

※例なので実装イメージ。未検証。

PHP

1$set = []; 2$params = []; 3$params_type = []; 4if ( isset($_POST['familyName']) ) 5 $set[] = " familyName = ? "; 6 $params[] = $_POST['familyName']; 7 $params_type[] = 's'; 8if ( isset($_POST['firstName']) ) 9 $set[] = " firstName = ? "; 10 $params[] = $_POST['firstName']; 11 $params_type[] = 's'; 12 13/* 中略 */ 14//$setが0件の場合ってある?あるならSQLそのもの実行しないように回避してください。 15 16$sql .= implode(',' , $set); 17$sql .= ' WHERE id = ?'; 18 19 $params[] = $_POST['id']; 20 $params_type[] = 's'; //idがsでいいの? 21 22 $stmt = $db->prepare($sql ); 23 $stmt->bind_param(implode('' , $params_type), $params); 24

PDOのほうが書きやすいかなぁ。

疑問:
蛇足レベルですけど、isset()なら空文字が来ても更新されるのですけど、

PHP

1<?php 2$a = [null,0,'0','1','a']; 3 4echo '$b is'; 5var_dump(isset($b)); 6 7foreach($a as $p){ 8 echo $p.' is '; 9 var_dump(isset($p)); 10}

それはバリデーションでチェックしてるということなんですかね。
それとも空文字更新ありという仕様なのか。

投稿2022/09/08 11:39

編集2022/09/08 12:03
m.ts10806

総合スコア80850

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

iinuma-y

2022/09/09 02:25 編集

コードを拝見し、bind_paramの可変長部分の引数って配列でいいの?と思い検索をしたところ、引数のアンパックができるのですね。恥ずかしながらそれを知りませんでした。 回答はおそらく...演算子の抜けかと思いますが、とても参考になりました。 検索語彙が増えたことで、次のような記事も発見し、様々参考にさせて頂きながら解決できました。 https://agohack.com/bind_param-dynamic-array/ 最終的には編集を許可するパラメータ名と型を配列で持ち、$_POSTをforeachで回してkeyがその配列に存在すれば$setに追記する、あとはm.ts10806さんの回答というコードにすることにしました。 $paramNames = [  'familyName' => 's',  'firstName' => 's',  'age' => 'i', ]; $paramsType = $params = $set = []; foreach ( $_POST as $key => $value ) {  if ( array_key_exists($key, $paramNames) ) {   $set[] = $key . '=?';   $params[] = $value;   $paramsType[] = $paramNames[$key];  } } ※補足頂いている内容ですが、今回はパラメータさえ渡っていれば空文字でも更新する仕様ですので問題ありません。お気遣いありがとうございます。 この度はありがとうございました。
m.ts10806

2022/09/09 02:28

SQLもPHPからすると文字列なので、 「共通部分以外を動的に生成する文字列」を想定して組むと良いと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問