mysqli (multi) _query - как это сделать и выполнить в цикле (ах)? - PullRequest
0 голосов
/ 15 марта 2012

После создания нескольких функций для моего нового веб-сайта я быстро понял, что со всеми включаемыми файлами с помощью вечного программирования все выходит из-под контроля, поэтому я решил изучить и преобразовать свои в настоящее время написанные функции в классы ООП и конвертировать из mysql.к MySQLДо сих пор не было ничего плохого в переходе, но я достиг функции, которая имеет / нуждается в нескольких запросах, один из которых - SELECT, чтобы проверить данные перед тем, как Я ОБНОВЛЮ их.То, что я написал до сих пор, используя подготовленное утверждение, работает как шарм, но это только SELECT.Я застрял там, где приходит время обновить БД.Вот что у меня есть:

public function ban() {
  $connection  = Database::getConnection();
  $user_id = $_POST['user'];
  $feedback = '';
  $query = "SELECT banned, user_name
            FROM users
            WHERE user_id = ?";
  $stmt = $connection -> prepare($query);

  // Check for a multi-user selection on POST
  if (count($user_id) > 1) {
     foreach ($user_id as $value) {
        $stmt -> bind_param('i', $value);
        if (!$result = $stmt -> execute()) {
           $feedback .= "Did not execute.";
        } else {
           $stmt -> bind_result($banned, $user);
           $stmt -> fetch();
           if ($banned == 1) {
              $feedback .= $user . " is already banned.<br />";
           } else {
              // This is where I need the code to update the database
              // with the users who aren't already banned.
           }
        }
     }
     $stmt -> close();
     return $feedback;
  } else {
     // This is where the UPDATE will be for a single person ban
     // if only one was selected on POST
  } 
}

Могу ли я создать / выполнить второй подготовленный оператор для инъекции UPDATE, запустив его в другом цикле в разделе, где мне нужен код, или лучше избежать этогоили это вообще сработает?Я уверен, что mysqli_multi_query - это, вероятно, лучший способ, когда нужно снова переписать функцию, так как я обнаружил (после написания большей части этой функции), что нельзя использовать подготовленный оператор с внедрением multi_query.Переписывание не имеет большого значения, но справка по использованию multi_query далека и немногочисленна.На веб-сайте PHP много документации, но, честно говоря, это чертовски запутанно.

Запрос UPDATE будет выглядеть примерно так:

$explain = mysql_real_escape_string($_POST['banExplain']);
UPDATE users
SET banned = '1', ban_reason = '$explain'
WHERE user_id = '$value'"

ЛЮБАЯ помощь по этому вопросу будет принята с благодарностью.Надеюсь, я достаточно хорошо объяснил, что мне нужно сделать.Если нет, дайте мне знать.СПАСИБО!

Ответы [ 2 ]

1 голос
/ 15 марта 2012

Пример кода все еще очень процедурный.

У вас должен быть метод для проверки, является ли пользователь user_banned, и метод для запрета пользователя.

Метод user_banned должен принимать user_id и возвращать логическое значение.Метод ban_user должен иметь причину и user_id.

Должна быть другая функция или метод, который выполняет цикл.

Преобразуйте user_id в массив, и вы можете сделать один цикл.

Использовать исключения для обработки ошибок.

<?php
    //the model
    ...  

    public function update_users (array $user_ids)
    {
       $result = array();         

       foreach ($user_ids as $user_id) {
           if (!$this->user_banned($user_id)) {
               $this->ban_user($user_id, $reason);

           } else {
               $result[$user_id] = "already banned";
           }

        return $result;
    }
    ...

    //the controller

    //prevent XSS, cast as integers or use filter_var or something
    $user_ids = sanitize((array) $_POST['user']);
    try {
        $result = $obj->update_users($user_ids);
    } catch (Exception $e) {
    ...
0 голосов
/ 16 марта 2012

Когда я перечитал свой вопрос, у меня возникла мысль об использовании второго подготовленного утверждения для обновления. Во-первых, я попытался включить второй оператор в начальный цикл foreach, но он допустил ошибку, возможно потому, что вы не можете открыть более одного оператора за раз. Затем я решил сохранить значение (проверяемый user_id) на каждой итерации в отдельном массиве, а затем, закрыв начальный цикл foreach и подготовленный оператор, я запустил второй цикл, используя новый подготовленный оператор для UPDATE и вуаля! Вот код, как он выглядит сейчас:

public function ban() {
  $connection  = Database::getConnection();
  $user_id = $_POST['user'];
  $reason = $_POST['banExplain'];
  $update = array();
  $feedback = '';
  $query = "SELECT banned, user_name
            FROM users
            WHERE user_id = ?";
  $query2 = "UPDATE users
             SET banned = '1', ban_reason = ?
             WHERE user_id = ?";
  $stmt = $connection -> prepare($query);
  $stmt2 = $connection -> prepare($query2);

  if (count($user_id) > 1) {
     foreach ($user_id as $value) {
        $stmt -> bind_param('i', $value);            
        if (!$result = $stmt -> execute()) {
           $feedback .= "Did not execute search.<br />";
        } else {
           $stmt -> bind_result($banned, $user);
           $stmt -> fetch();
           if ($banned == 1) {
              $feedback .= $user . " is already banned.<br />";
           } else {
              $update["$user"] = $value; // Populate an array to pass to update loop
           }
        }
     }
     $stmt -> close();

     // Run update query - $key from $update var is the user name
     foreach ($update as $key => $value) {
        $stmt2 -> bind_param('si', $reason, $value);
        if (!$result2 = $stmt2 -> execute()){
           $feedback .="Did not execute update.<br />";
        } else {
           $feedback .= $key . " is banned.<br />";
        }
     }
     $stmt2 -> close();
     return $feedback;
  } else {

     // Executes for single selection requests
     $stmt -> bind_param('i', $user_id);
     if (!$result = $stmt -> execute()) {
        $feedback .= "Did not execute search.<br />";
     } else {
        $stmt -> bind_result($banned, $user);
        $stmt -> fetch();
        if ($banned == 1) {
           $feedback .= $user . " is already banned.<br />";
        } else {
           $update["$user"] = $user_id;
        }
     }
     $stmt -> close();
     // Runs loop simply for the user name in the $key var
     foreach ($update as $key => $value) {
        $stmt2 -> bind_param('si', $reason, $value);
        if (!$result2 = $stmt2 -> execute()){
           $feedback .="Did not execute update.<br />";
        } else {
           $feedback .= $key . " is banned.<br />";
        }
     }
     $stmt2 -> close();
     return $feedback;
  }

}

Довольно долго для того, что кажется такой простой задачей, и я думаю, что mysqli::multi_query, вероятно, все еще лучший способ сделать это (после использования mysqli::real_escape для добавления необходимой защиты), но кроме громоздкость этой функции, она работает и безопасна (насколько я могу судить), сохраняя при этом минимальную стоимость запроса к серверу.

Я собираюсь сейчас воспользоваться советом user934258 и попытаться разбить его на более мелкие, более простые для усвоения и поддержки функций класса. Если / когда я завершу это, я продолжу здесь, чтобы показать результаты, которые, мы надеемся, помогут другим в том же затруднительном положении. Если у кого-то есть хорошие решения, пожалуйста, дайте мне знать. Я всегда пытаюсь улучшить свое кодирование

Спасибо всем, надеюсь, это хотя бы поможет кому-то еще в такой же ситуации.


Хорошо, хорошо, благодаря user934258 Я вернулся и повторно проверил кодирование этого / этих методов. Следуя его советам, я смог вырезать около 25 строк кода из методов запрета. Но, имея всего пару дополнительных строк и передавая / вставляя дополнительный аргумент, я смог сделать код многоцелевым, чтобы включить метод unban, который, в свою очередь, удаляет около 77 строк ненужного повторяющегося кода (unban был зеркальной копией запрет, за исключением того, что он проверял Ноль вместо Единицы в БД и отображал немного другой ответ.) Для тех из вас, кто в будущем может найти эту тему полезной, вот конечный продукт моего кода.

Метод, вызываемый из скрипта:

public function ban($arg) {              // 0 = Unban 1 = Ban
  $this -> user_id = $_POST['user'];     // initially set user id as global variable
  $user_id = $this -> user_id;
  $this -> banReason = mysqli_real_escape_string($_POST['banExplain']);

  if (!$this -> check_ban($user_id, $arg)) {
     $this -> feedback .= Admin::CONNFAIL;
     return $this -> feedback;           // Returned connection failure on select
  } elseif (!$this -> ban_user($arg)) {
     $this -> feedback .= Admin::UPFAIL;
     return $this -> feedback;           // Returned connection failure on update
  } else {
     return $this -> feedback;
  }
}

Метод проверки значений банов БД:

private function check_ban($user_id, $arg) {
  $connection = Database::getConnection();
  $query = "SELECT banned, user_name
            FROM users
            WHERE user_id = ?";
  $stmt = $connection -> prepare($query);

  foreach ($user_id as $value) {
     $stmt -> bind_param('i', $value);
     if (!$result = $stmt -> execute()) {
        return FALSE;
     } else {
        $stmt -> bind_result($banned, $user);
        $stmt -> fetch();
        if ($arg == 1 && $banned == 1) {
           $this -> feedback .= $user . " is already banned.<br />";
        } elseif ($arg == 0 && $banned == 0) {
           $this -> feedback .= $user . " is not currently banned.<br />";
        } else {
           $this -> update["$user"] = $value;  // Populate array to be un/banned
        }
     }
  }
  $stmt -> close();
  return TRUE;
}

И, наконец, способ обновить БД с помощью un / ban:

private function ban_user($arg) {
  $connection = Database::getConnection();
  $update = $this -> update;
  $reason = $this -> banReason;
  $query = "UPDATE users
            SET banned = ?, ban_reason = ?
            WHERE user_id = ?";
  $stmt = $connection -> prepare($query);

  foreach ($update as $key => $value) {
     $stmt -> bind_param('isi', $arg, $reason, $value);
     if (!$result = $stmt -> execute()) {
        return FALSE;
     } elseif ($arg == 0) {
        $this -> feedback .= $key . " is unbanned.<br />";
     } else {
        $this -> feedback .= $key . " is banned.<br />";
     }
  }
  $stmt -> close();
  return TRUE;
}

Обратите внимание, что в этих методах происходит очень мало проверки / фильтрации / очистки от ошибок. У меня есть несколько причин для этого: значения переменных проверяются в сценарии, я единственный, кому предоставлены права на это, а значения для переменных, таких как $user_id, предварительно заполнены в флажках и раскрывающихся списках с помощью точные значения из БД, так что вероятность ошибочного $user_id невелика.

Как я уже упоминал в других постах, если кто-то может предложить лучшее решение для того, что я сделал, пожалуйста, дайте мне знать.

Спасибо за такое замечательное сообщество!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...