使用Php和pdo创建灵活的更新查询 - bindparam的问题

时间:2013-03-27 08:58:49

标签: php pdo

我正在更新我的mysql函数以使用PDO。我已经掌握了大部分内容,但正在努力使用更新功能来更新记录中的多个字段。 该函数在一个类中,我试图保持其灵活性,以便与其他表等重用。

到目前为止,这是我的功能:

 public function dbUpdateRecord($table, $values, $where)
{


    $this->conn();
    $fieldNames = array_keys($values);
    var_dump($fieldNames);
    $set="";
    foreach ($fieldNames as $field) {
        $set .= " $field = :$field,";
    }

    //strip last comma
    $set = substr($set, 0, strlen($set) - 1);


    $wherefields = array_keys($where);
    $whereCondition="";
    foreach ($wherefields as $field) {
        $whereCondition .= " $field = :$field AND";
    }
    //strip last AND
    $whereCondition = substr($whereCondition, 0, strlen($whereCondition) - 3);

    $sql = "UPDATE $table SET $set WHERE $whereCondition";
    var_dump($sql);

    $stmt = $this->db->prepare($sql);

    foreach ($values as $field => $value) {

        $stmt->bindParam(':$field', $value);
    }
    foreach ($where as $field => $value) {
        $stmt->bindParam(':$field', $value);
    }
    return $stmt->execute();
}

问题是记录中的所有字段都是由$ where变量中包含的记录的id更新的。

$ values包含一个(fieldname => value)数组。

我认为问题在于bindparam并试图使字段名/占位符动态

我认为我需要使用bindparam作为最佳实践 - 这是正确的还是我可以去执行()?

非常感谢

3 个答案:

答案 0 :(得分:2)

您正在从错误的一端解除此日志 您的方法可能不安全但同时又不灵活。 如果您需要基于JOIN的更新怎么办?如果在WHERE(或IN)中需要OR怎么办?

您真正需要的是传统查询,其中只需要生成SET语句值。 因此,您需要一个helper function to produce such a statement数据数组,返回正确格式化的SET语句和带有要绑定的变量的数组:

$fields = array("name","email");
$sql = "UPDATE users SET ".pdoSet($fields,$values,$data)." WHERE id = :id"
// now we have $values array to be passed into query
$stmt = $dbh->prepare();
$values["id"] = $_POST['id'];
$stmt->execute($values);

使用此代码,您将能够为任意查询进行更新。并使其安全

作为进一步的步骤,您需要开始使用类型提示的占位符,以便像这样生成整个代码:

$db->query("UPDATE ?n SET ?u WHERE id IN(?a)",$table,$data,$ids);

回到你的问题,ONe是对的 - 你需要使用bindValue而不是bindParam(正如它在标签wiki中提到的那样)

答案 1 :(得分:1)

我认为问题在于你使用foreach将params绑定到查询。为什么这是个问题?因为绑定变量时绑定对该变量的引用,所以如果该变量发生更改,查询中的值也会更改。由于您使用的是foreach循环,因此所有参数的值将是变量$ value引用的最新值。

您可以详细了解此foreach行为herehere。所以基本上,你有两个选择:

  • 使用对实际值的引用,而不是使用对$ value的引用(可以在下一次迭代中更改其值)
  • 使用辅助变量引用在循环期间不会更改的另一个内存位置

答案 2 :(得分:0)

我来到这里是因为我遇到了同样的问题,YCS的解决方案就是我所需要的。对于这种情况下的其他任何人,这里是我最终使用的辅助函数:

function commit_table($ record_id,$ changed_values) {     $ db = open_database();

$query = 'UPDATE table SET ';
$query_arguments = array();

$is_first = TRUE;

foreach(array_keys($changed_values) as $key)
{
    if($is_first)
    {
        $is_first = FALSE;
    }
    else
    {
        $query .= ', ';
    }
    $value_var = ':' . $key;
    $query .= $key;
    $query .= ' = ';
    $query .= $value_var;
    $query_arguments[$value_var] = $changed_values[$key];
}

$query .= ' WHERE record_id = :record_id';
$query_arguments[':record_id'] = $record_id;

$stmt = $db->prepare($query);
$stmt->execute($query_arguments);

close_database($db);

}