绑定大插入或更新的更有效方法?

时间:2013-08-20 04:03:47

标签: php data-binding pdo foreach

好的,所以我对绑定很新,这里有一些有效的代码。我从教程中学到了这种格式,但我想有更多有效的方法可以做到。在我的例子中有4个名字,但实际上我将在一个项目中进行大量的插入和更新,即将有20个左右的字段。我喜欢这种方法的清晰度,但显然当你谈论20场或更多场时,它确实占用了大量的空间。让我们先看看我的代码。

以下是它使用的功能:

// prepare the statement
public function query($query){
    $this->stmt = $this->dbh->prepare($query);
}

public function bind($param, $value, $type = null){
    if (is_null($type)) {
        switch (true) {
            case is_int($value):
                $type = PDO::PARAM_INT;
                break;
            case is_bool($value):
                $type = PDO::PARAM_BOOL;
                break;
            case is_null($value):
                $type = PDO::PARAM_NULL;
                break;
            default:
                $type = PDO::PARAM_STR;
        }
    }
// run the binding process
$this->stmt->bindValue($param, $value, $type);
}

// execute the prepared statement
public function execute(){
    return $this->stmt->execute();
}

现在是实际的代码

$database->query("
    INSERT INTO users(
        user_name,
        user_password_hash,
        user_email,
        user_activation_hash
)

    VALUES(
        :user_name,
        :user_password_hash,
        :user_email,
        :user_activation_hash
    )
");

// bind the values
$database->bind(":user_name", "$this->user_name");
$database->bind(":user_password_hash", "$this->user_password_hash");
$database->bind(":user_email", "$this->user_email");
$database->bind(":user_activation_hash", "$this->user_activation_hash");

// execute the statement and insert the values into the database
$database->execute();

它只是呼喊一个循环,特别是因为我习惯调用post字段,输入字段,变量和占位符相同的名称,不确定这是好事还是坏事但我发现它对我很有帮助在处理我将成为的大型表格时。

无论如何我可以这样做:

$placeholder_array = array(
    "user_name"               => "\$this->user_name",
    "user_password_hash"      => "\$this->user_password_hash",
    "user_email"              => "\$this->user_email",
    "user_activation_hash"    => "\$this->user_activation_hash"
);

// well use this copy to edit the array keys and keep original for the binding
$placeholder_copy = $placeholder_array;


// turn the array into a string i.e user_name, user_password_hash....
$fields = implode (", ", array_keys($placeholder_array));

// foreach to add : placeholder prefix for binding
foreach ($placeholder_copy as $key => $value){
$placeholder_copy [':'.$key] = $value;
unset($placeholder_copy[$key]);
}

// turn the copy array which has prefix :user_name into a string 
$placeholders = implode (", ", array_keys($placeholder_copy));

$database->query("
    INSERT INTO users($fields)
    VALUES($placeholders) 
");

// bind the values
foreach ($placeholder_copy as $bind_values => $value){
    echo '$database->bind("'.$bind_values.'", "'.$value.'");' . "<br />";
}

// execute the statement and insert the values into the database
$database->execute();

然后我可以把它变成一个带参数的函数,用于传入关联数组和表名,以保持我的主代码更清晰。

现在想象一下,随着我正在进行的项目涉及向用户提交数据的大量表单,我将会做任何数量的这些。我是PDO的新手,并试图掌握它,所以可能有一种更简单的方式来构建这些类型的查询,我看看谷歌和stackflow但我没有真正得到他们正在做的所以我认为做我自己的一个将允许人们更好地向我解释发生了什么,我宁愿在开始我的项目时做到这一点,而不是回去后再改变一切。那么有更好的方法还是这个呢?

非常感谢任何反馈,我很高兴现在我在这里接受了人们的建议并转向了PDO。

2 个答案:

答案 0 :(得分:2)

不,遗憾的是,PDO对此事没有任何帮助 然而,你自己的方法在我看来并不是一个有效的方法。

首先,让我指出你的一组功能是没用的。我理解没有直接重写API函数。 PDO已经做了所有这些事情。

使用原始PDO,您可以获得更简洁的代码:

$stm  = $pdo->prepare("INSERT INTO users VALUES(NULL,?,?,?,?)");
$data = array($this->user_name, 
              $this->user_password_hash, 
              $this->user_email, 
              $this->user_activation_hash
);
$stm->execute($data);

关于您的动态查询构建,我发现它太过臃肿但仍然不安全。一些缺陷是

  • 没有必要打扰“:named”占位符,因为它们应该是人类可读的,但没有人应该在这里阅读它们。更不用说PDO会在发送到mysql之前将它们转换回?
  • 针对命名占位符的另一个异议 - 虽然Mysql(以及HTTP)可以让你在字段名称中有空格,带有空格的占位符只会让你的查询崩溃。
  • 此代码中的没有真正的好处来自您的同名方法 - 您必须手动编写十几次,请注意。< / LI>
  • 如果可以使用,则您没有白名单来检查您的字段列表,这可能是严重的安全漏洞
  • 代码太多
  • 不支持UPDATE查询,也可以非常方便(如果您使用的是mysql)
  • 当你把这个代码付诸实践时,你会陷入与其他人相同的不良理由,因为有可能拯救你整个两个词!

Here is my earlier approach for the matter

$allowed = array(
    "user_name", "user_password_hash", "user_email", "user_activation_hash"
);
$sql = "INSERT INTO users SET ".pdoSet($allowed, $values);
$stmt = $dbh->prepare($sql);
$stmt->execute($values);

这里有my current approach,最好的(尽管使用mysqli而不是PDO实现),使用自定义占位符获取数组数据:

$placeholder_array = array(
    "user_name"               => $this->user_name,
    "user_password_hash"      => $this->user_password_hash,
    "user_email"              => $this->user_email,
    "user_activation_hash"    => $this->user_activation_hash
);
$db->query("INSERT INTO users SET ?u", $placeholder_array);

或者,如果表单字段和SQL列之间直接连接

$allowed = array(
    "user_name", "user_password_hash", "user_email", "user_activation_hash"
);
$insert = $db->filterArray($_POST,$allowed);
$db->query("INSERT INTO users SET ?u", $insert);

通过这种方式,我可以使用INSERT IGNOREINSERT DELAYEDINSERT.. ON DUPLICATE UPDATEUPDATEUPDATE加入,以及无数其他选项,支持完整 SQL语言。

答案 1 :(得分:1)

我最近所做的并且不断改进,正在构建一个辅助类来简化我的应用程序中基于PDO的SQL语句。

让我们来看看你的例子吧。您想要将数据插入用户表:

INSERT INTO users(
    user_name,
    user_password_hash,
    user_email,
    user_activation_hash
) VALUES(
    :user_name,
    :user_password_hash,
    :user_email,
    :user_activation_hash
)

为了插入单个记录,我的SQL构造如下:

function myInsertSingle($PDO, $table, $ins_array) {
    $SQL = "INSERT INTO `".$table."` (".dbFieldList($ins_array)
            .") VALUES (".dbValuePList($ins_array).")";

......继续准备。

  • $PDO:我的PDO连接。
  • $table:我要插入的表格。
  • $ins_array:用于插入数据的结构化数组。

对于您的示例,数组$ins_array将如下所示:

$ins_array = array(
    "user_name"               => "your_user",
    "user_password_hash"      => "your_pw_hash",
    "user_email"              => "your@mail.xyz",
    "user_activation_hash"    => "your_other_Hash"
);

注意与阵列的相似性!

涉及两个功能。第一个给我一个(转义的)字段名列表。

function dbFieldList($fields) {
    $set = '';
    foreach ($fields as $field => $item) {
        $set .= "`".$field."`,";
    }   
    return rtrim($set, ',');
}

基本上从上面的数组示例中,函数返回

`user_name`, `user_password_hash`, `user_email`, `user_activation_hash`

...这是INSERT - 语句中所需字段的列表。

另一个对值进行类似的操作。

function dbValuePList($fields) {
    $set = '';
    foreach ($fields as $field => $item) {
        $set .= ":".$field.",";
    }   
    return rtrim($set, ',');
}

你猜对了,结果如下:

:user_name, :user_password_hash, :user_email, :user_activation_hash

现在您有了可以准备的SQL语句。要绑定值,如果我正确理解你的类,只需使用一个如下所示的循环:

foreach ($ins_array as $field => $item) {
    $PDO->bind(':'.$field,$item);
}

现在您可以执行该语句。

概要。基本上我的方法是将单个INSERT语句简化为:

$ins_array = array(
    "user_name"               => "your_user",
    "user_password_hash"      => "your_pw_hash",
    "user_email"              => "your@mail.xyz",
    "user_activation_hash"    => "your_other_Hash"
);

myInsertSingle($PDO, 'your_table', $ins_array);

如果这看起来像另一个开销,请记住,我在其他SQL语句中使用这些函数,如SELECT,UPDATE,DELETE等。