mysqli(multi)_query - 如何在循环中执行和执行?

时间:2012-03-15 06:05:20

标签: php mysql mysqli

对于我的新网站创建少数的函数后,我很快就意识到事情用永久编程失控与所有的头文件,所以我决定去学习和转换我目前正在写的函数到面向对象的类和从MySQL转换到mysqli。到目前为止,转换并没有太糟糕,但我已经达到了一个需要多个查询的函数,一个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 个答案:

答案 0 :(得分:1)

代码示例仍然非常程序化。

您应该有一个方法来检查用户是否是user_banned以及ban_user用户的方法。

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) {
    ...

答案 1 :(得分:0)

当我重新阅读我的问题时,它让我考虑使用第二个准备好的声明进行更新。首先,我尝试在初始foreach循环中包含第二个语句,但这是错误的,大概是因为你不能一次打开超过1个语句。我接下来决定将每次迭代的值(被检查的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的建议并尝试将其分解为更小,更容易消化和维护类功能。如果/当我完成时,我会在此处跟进以显示结果,这将有望帮助其他人摆脱同样的困境。如果其他人有一些好的解决方案,请告诉我。我总是试图改进我的编码

谢谢大家,希望这至少可以帮助处于相同情况的其他人。


好吧,thanks to 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;
  }
}

检查DB禁止值的方法:

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等变量的值被预先填充到复选框和下拉列表中列出了db中的确切值,因此错误$user_id的可能性很小甚至没有。

正如我在其他帖子中提到的,如果有人能为我所做的事情提供更好的解决方案,那么请告诉我。

感谢这样一个伟大的社区!