Реализация параметра привязки MYSQLI с неограниченными возможностями

#php #mysql #mysqli

#php #mysql #mysqli

Вопрос:

Я знаком с основами работы mysqli_stmt_bind_param, но я все еще новичок в PHP и столкнулся с проблемой, которую я не уверен, как решить. Как мне реализовать метод mysqli_stmt_bind_param всякий раз, когда я не уверен, сколько? будет в SQL-запросе? Я прикрепил пример кода ниже, который иллюстрирует, о чем я говорю. Я также включил скриншот структуры базы данных, если это поможет.

В этом примере оператор SQL строится в зависимости от наличия аргументов в URL, других условий и т. Д. Потенциально может быть где угодно от 1? до 10 ?, с которыми я не уверен, как ориентироваться.

Я хочу добиться этого, потому что, насколько я понимаю, stmt_bind_param безопаснее, чем делать это так, как я сейчас это делаю .. верно?

   if (isset($_GET['type'])) {
    if ($_GET['type'] == "forgiven") {$supplemental = "discipline.forgiven = 1 AND ";}
    else if ($_GET['type'] == "active") {$supplemental = "discipline.forgiven = 0 AND ";}
    else if ($_GET['type'] == "all") {$supplemental = "discipline.forgiven IS NOT NULL AND ";}
    else {$supplemental = "discipline.type = '" .$_GET['type']. "' AND ";}
  }

  if (isset($_GET['uuid'])) {$SQL = "SELECT users.firstName, users.lastName, users.homeroom, discipline.* FROM `discipline` INNER JOIN users ON users.uuid = discipline.recipient WHERE " .$supplemental. "discipline.recipient LIKE '" .$_GET['uuid']. "' ORDER BY discipline.timestamp DESC";}
  else {$SQL = "SELECT users.firstName, users.lastName, users.homeroom, discipline.* FROM `discipline` INNER JOIN users ON users.uuid = discipline.recipient WHERE " .$supplemental. "users.homeroom LIKE '" .$_GET['search']. "' OR discipline.recipient LIKE '" .$_GET['search']. "' OR discipline.reason LIKE '%" .$_GET['search']. "%' OR discipline.action LIKE '%" .$_GET['search']. "%' ORDER BY discipline.timestamp DESC";}


  $stmt = mysqli_stmt_init($connection);
  if (!mysqli_stmt_prepare($stmt, $SQL)) {echo "SQL database connection error!";exit();}
  else {
    mysqli_stmt_execute($stmt);
    $results = mysqli_stmt_get_result($stmt);
 

Структура таблицы:

введите описание изображения здесь

Комментарии:

1. Я голосую за то, чтобы закрыть этот вопрос как дубликат, потому что ответ заключается в передаче массива в mysqli_stmt_bind_param() с использованием ... синтаксиса, чтобы массив стал списком отдельных аргументов для этой функции. Это объясняется более подробно в ответе, на который я ссылался. Вы должны добавлять ? заполнители в SQL одновременно с добавлением элементов в массив значений, которые вы будете использовать при вызове bind_param .

2. @BillKarwin это похоже на совершенно другой вопрос, я не понимаю, почему это дубликат..

3. Я написал ответ, чтобы проиллюстрировать, как это применимо к вашему случаю.

Ответ №1:

Вот пример того, как вы можете сделать связанные параметры «динамическими», или, другими словами, выполнить SQL-запрос с переменным количеством заполнителей параметров на основе условий. По мере написания условий добавляйте значения к массиву параметров.

 $andTerms = [];
$types = "";
$params = [];
if (isset($_GET['type'])) { 
  if ($_GET['type'] == "forgiven") {
    $andTerms[] = "discipline.forgiven = 1";
  } else if ($_GET['type'] == "active") {
    $andTerms[] = "discipline.forgiven = 0";
  } else if ($_GET['type'] == "all") {
    $andTerms[] = "discipline.forgiven IS NOT NULL";
  } else {
    $andTerms[] = "discipline.type = ?";
    $types .= "s";
    $params[] = $_GET['type'];
  }
} 

if (isset($_GET['uuid'])) { 
  $andTerms[] = "discipline.recipient LIKE ?"
  $types .= "s";
  $params[] = $_GET['uuid'];
} else {
  $andTerms[] = "users.homeroom LIKE ? OR discipline.recipient LIKE ? OR discipline.reason LIKE ? OR discipline.action LIKE ?";
  $types .= "ssss";
  $params[] = $_GET['search']; // homeroom
  $params[] = $_GET['search']; // recipient
  $params[] = "%{$_GET['search']}%"; // reason
  $params[] = "%{$_GET['search']}%"; // action
}   

$andTermsImploded = implode(" AND ", $andTerms);
$SQL = " 
  SELECT users.firstName, users.lastName, users.homeroom, discipline.*
  FROM `discipline` INNER JOIN users ON users.uuid = discipline.recipient
  WHERE {$andTermsImploded}
  ORDER BY discipline.timestamp DESC";
 

К тому времени, когда вы закончите это, вы добавили переменное количество терминов в предложение WHERE запроса. Между тем, значения для привязки $params также имеют переменную длину, но если вы правильно написали каждый условный блок кода, он имеет то же количество элементов, что и количество заполнителей в SQL-запросе. Также $types имеет правильное количество управляющих символов.

Затем пришло время выполнить вызов bind_param . Используйте ... синтаксис для передачи вашего массива значений, как если бы это был список аргументов этой функции. Смотрите Пример # 10 в https://www.php.net/manual/en/functions.arguments.php это описывает списки аргументов переменной длины. Эта функция была введена в PHP 5.6 в 2014 году, поэтому она должна быть доступна любому сайту, на котором запущена версия PHP, срок службы которой не истек.

 $stmt = mysqli_stmt_init($connection);
if (!mysqli_stmt_prepare($stmt, $SQL)) {
  echo "SQL error!";
  trigger_error($stmt->error);
  exit();
} 

mysqli_stmt_bind_param($stmt, $types, ...$params); // passing variable arguments

$ok = mysqli_stmt_execute($stmt);
if (!$ok) {
  echo "SQL error!";
  trigger_error($stmt->error);
  exit();    
}

$results = mysqli_stmt_get_result($stmt);
 

Не забывайте сообщать об ошибке в свой журнал ошибок каждый раз, когда вы вызываете prepare или execute .

PS: Я немного переформатировал код, чтобы соответствовать руководству по стилю PSR-2. Пожалуйста, следуйте этому руководству в будущем. Это облегчает другим разработчикам чтение вашего кода.