[PHP] mysqliのbind paramに可変する引数を渡す

PHPSQL

プリペアドステートメントの「?」の数を動的に変化させる方法

例えば、画面で検索条件が複数指定できて、「検索」的なボタンを押すたびに、SQLのWHERE句に指定する条件を変えたい場合。または、テーブルの各行にチェックボックスあって、チェックされた数だけ、WHERE句の条件が増える時に使う。

スポンサーリンク

WHERE orderid IN (?,?) を可変にする

SELECT * FROM table WHERE id IN (?,?,?,....)
こんなSQLがあって、INのあとの’?’を可変にしたい場合

「… による引数のアンパック」を使う

$stmt->bind_param($paramType, ...$stmtArray);
…つければ、配列をbind_paramの引数に指定できる

PHP 5.6以上から使える
PHP マニュアル 「… による引数のアンパック」

// チェックボックスの値が配列で送られてくるとか
  $ids = filter_input(INPUT_POST, 'id', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);

  $sql = 'SELECT * FROM table WHERE id IN ('.implode(',', array_fill(0, count($ids), '?')).')';
  $stmt = $mysqli->prepare($sql);
  // idが文字列だったら's'、数値だったら'i'
  $stmt->bind_param(str_repeat('s', count($ids)), ...$ids);
  $stmt->execute();
  $res = $stmt->get_result();

SQLにはバインドさせたいパラメータの数だけ’?’を出力しなければならないので、array_fill(初めの要素のインデックス番号, 要素の数, 値)で’?’の値をもつ配列を作り、implode(連結文字 , 連結したい配列)で、カンマ区切りの’?’連結文字列を出力する


bind_paramの第一引数は、バインド変数の数だけ型 (s = string, i = integer, d = double, b = blob) を指定するので、str_repeat(出力する文字列, 反復回数)で必要な個数分出力する

call_user_func_arrayを使う

PHP 5.6以下使ってる場合は、上記のアンパックは使えないのでこっち


call_user_func_arrayとは
(引数を持つ)関数の引数に配列を渡し、その関数を実行させることができる
PHP マニュアル call_user_func_array

// チェックボックスの値が配列で送られてくるとか
$ids = filter_input(INPUT_POST, 'id', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);

$sql = 'SELECT * FROM table WHERE id IN ('.implode(',', array_fill(0, count($ids), '?')).')';

//IN句の組み立て
$sqlParams = [];
$sqlParams[0] = "";
foreach($ids as $key => $val) {
  $sqlParams[0] .= "s";
  $sqlParams[] = $val;
}
// bind_paramに渡す引数を参照渡しにする
$params = [];
foreach ($sqlParams as $key => $value) {
    $params[$key] = &$sqlParams[$key];
}

// プリペアドステートメント
if ($stmt = $mysqli->prepare($sql)) {
// 変数のバインド
call_user_func_array(array($stmt, 'bind_param'), $params);
$stmt->execute();

bind_paramに渡す引数は参照渡しでなければならない
&をつけると、参照渡しになる

WHERE句の検索条件が可変する

WHERE name=? and id=?とか WHERE id=?とか、WHERE句の条件が可変で組み立てが必要な時

「… による引数のアンパック」版

// POSTで送られてきた
$chk = filter_input(INPUT_POST, 'chk', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
$name = filter_input(INPUT_POST, 'name');

$sql='select * from tableA';

$sqlWhere = [];
// WHERE句の組み立て
if (!empty($chk) ) {
  $sqlWhere[] = 'id IN ('.implode(',', array_fill(0, count($chk), '?')).')';
}
if (!empty($name)) {
  $sqlWhere[] = "name = ?";
  $chk[] = $name;
}

if (count($sqlWhere) > 0) {
  $sql .= ' WHERE ' . implode(" and ", $sqlWhere);
}
$stmt = $mysqli->prepare($sql);
$stmt->bind_param(str_repeat( 's', count($chk)), ...$chk);
$stmt->execute();
$res = $stmt->get_result();

count($chk)するため、$nameを$chk[]に追加している。




以上

Posted by Agopeanuts