【问题标题】:"Build" a prepared SQL statement based on user-inputs with PHP使用 PHP 根据用户输入“构建”准备好的 SQL 语句
【发布时间】:2020-01-21 16:59:21
【问题描述】:

这个问题有点难以表达,但基本上我有一个表格。这是一种帐户信息表格,人们可以在其中更改与其帐户关联的信息。问题是整个表单都被提交,每个输入都是可选的 (Leave input empty for no-change) 。因此,例如使用此表格,您可以更改您的城市、地址和电话。但是,一个人可以选择只填写城市,按提交,然后更新该信息,所有其他信息保持不变。

手头的代码可能更有意义,这是我用来完成这项任务的 PHP;包含代码的 cmets 有 4 行,因为它们试图回答我自己的问题,所以这些被留下了;他们可能有助于解释我想要实现的目标:

PHP

// SQL HELPER
function prepared_Query($con, $sql, $params, $types = ""){
    $types = $types ?: str_repeat("s", count($params));
    $stmt   = $con -> prepare($sql);
    $stmt -> bind_param($types, ...$params);
    $stmt -> execute();
    return $stmt;
}

// TRIM ALL POST VARS
function trim_Val(&$val){
    $val = trim($val);
}
array_filter($_POST, 'trim_Val');

// SANITIZE ALL INPUTS 
$filter = ['filter' => FILTER_SANITIZE_STRING, 'flags' => FILTER_FLAG_ENCODE_HIGH | FILTER_FLAG_ENCODE_LOW];
$inputs = ['prov', 'city', 'addr', 'code', 'phone', 'phone_alt'];
$keys       = array_fill_keys($inputs, $filter);
$values    = filter_input_array(INPUT_POST, $keys);
unset($filter, $inputs, $keys); // cleanup

$sqlA   = [];
foreach($values as $key=>$val){
    if($val){
        $$key   = $val;
        //$sqlA[]   = "meta_".$key." = ?";
    }
}

//$sqlA = implode(", ", $sqlA);
//$sql  = "UPDATE _MAIN_meta SET ".$sqlA." WHERE meta_user = ?;";
//$stmt = prepared_Query($con, $sql, [$$key, $uid]);

if(!empty($prov)){
    $sql    = "UPDATE _MAIN_meta SET meta_prov = ? WHERE meta_user = ?;";
    $stmt   = prepared_Query($con, $sql, [$prov, $uid]);
    $stmt   -> close();
    echo "Updated Province";
}
if(!empty($city)){
    $sql    = "UPDATE _MAIN_meta SET meta_city = ? WHERE meta_user = ?;";
    $stmt   = prepared_Query($con, $sql, [$city, $uid]);
    $stmt   -> close();
    echo "Updated City";
}
if(!empty($addr)){
    $sql    = "UPDATE _MAIN_meta SET meta_addr = ? WHERE meta_user = ?;";
    $stmt   = prepared_Query($con, $sql, [$addr, $uid]);
    $stmt   -> close();
    echo "Updated Address";
}
if(!empty($code)){
    $sql    = "UPDATE _MAIN_meta SET meta_code = ? WHERE meta_user = ?;";
    $stmt   = prepared_Query($con, $sql, [$code, $uid]);
    $stmt   -> close();
    echo "Updated Postal Code";
}
if(!empty($phone)){
    $sql    = "UPDATE _MAIN_meta SET meta_phone = ? WHERE meta_user = ?;";
    $stmt   = prepared_Query($con, $sql, [$phone, $uid]);
    $stmt   -> close();
    echo "Updated Phone";
}
if(!empty($phone_alt)){
    $sql    = "UPDATE _MAIN_meta SET meta_phone_alt = ? WHERE meta_user = ?;";
    $stmt   = prepared_Query($con, $sql, [$phone_alt, $uid]);
    $stmt   -> close();
    echo "Updated Alt. Phone";
}
$con -> close();

您可能会告诉我,我一遍又一遍地重复最后一个函数,而重复运行 $stmt 似乎很愚蠢,因为它可以通过一次执行来完成。根据我在 PHP 和编程基础方面所学的知识,这是非常 WET 的代码,其中 T 不再是两倍,而是 6 倍。为了解决这个问题,我创建了一个数组,然后设置了 foreach 输入;将列名添加到该数组。一旦完成implode 数组使用逗号分隔符。最后使用该内爆数组创建 SQL 语句。

这会根据设置的任何输入输出动态构建的 SQL 语句。因此,如果 City 和 Address 包含数据,则语句将如下所示:

$sql = "UPDATE _MAIN_meta SET meta_city = ?, meta_addr = ? WHERE meta_user = ?;";

到目前为止,我已经完成了这项工作,具体取决于我填写的输入,SQL 语句的最终输出正是我想要的。

问题...

...当涉及将该语句绑定到变量变量时,我有。当我不知道设置了哪些参数时,我不知道是否有可能将设置的 var-vars 的 ALL 放入此 prepared_Query() 函数的参数中。

可能我的逻辑是有缺陷的,但我已经非常接近让它发挥作用了,我不得不问是否有办法可以实现这一点,或者这样做是否有意义。这对我来说很有意义,但我以前用 PHP 遇到过一些非常讨厌的黑洞。我敢肯定,在做这样的事情时,有人比我更懂常识。

所以我的问题是,当每个变量都是可选的时,如何将变量变量绑定到准备好的 SQL 语句。有没有办法不必为每个输入编写单独的语句?或者更短/更快的方式来做到这一点?

这些 SO 问题相似,但不回答我的问题:

dynamically generating an SQL statement based on user form input

Dynamic SQL SELECT Statement with PHP based on user options

似乎还有其他一些类似这样的问题,但我找不到一个明确的例子来说明有人试图解决同样的问题。这就是让我觉得我的逻辑有某种缺陷的原因。

谁能指出我正确的方向,好吗?

注意:我想我已经粘贴了与问题相关的所有内容,如果有不清楚的地方请告诉我。

【问题讨论】:

    标签: php mysqli sql-update


    【解决方案1】:

    其他两个答案几乎都很好。

    @sh4dowb 的答案非常适合构建 SELECT 查询,其中可能是难以自动化的不同条件。但是对于 UPDATE 查询,手动选择每个子句是没有用的,循环将是一个更简洁的解决方案。

    @ooa 演示的方法更好,但他们忘记创建实际的更新子句。此外,它正在使用这些卑鄙的变量变量。

    但正确的解决方案几乎就在那里。我们将使用我的 PDO 示例来完成同样的事情,How to create a prepared statement for UPDATE query

    // the list of allowed field names
    $allowed = ['prov', 'city', 'addr', 'code', 'phone', 'phone_alt'];
    
    // initialize an array with values:
    $params = [];
    
    // initialize a string with `fieldname` = ? pairs
    $setStr = "";
    
    // loop over source data array
    foreach ($allowed as $key)
    {
        if (!empty($_POST[$key]) && $key != "id")
        {
            $setStr .= "`$key` = ?,";
            $params[] = $_POST[$key];
        }
    }
    $setStr = rtrim($setStr, ",");
    
    $params[] = $_POST['id'];
    prepared_query("UPDATE _MAIN_meta SET $setStr WHERE main_user = ?", [$params]);
    

    看在上帝的份上,忘记变量变量。它们绝对没有用例,但它们可能会造成相当大的伤害$_POST 数组是您数据的绝佳来源,不需要其他任何东西。

    【讨论】:

    • > 他们忘记了创建实际的更新子句——我没有“忘记”它,他们已经用一条 UPDATE 语句自己生成了正确的 SQL。我只是建议如何让它发挥作用。
    • @ooa 如果用户发送“city”参数为空,它将不起作用
    • 顺便说一句,您在第一行缺少 [ 但我无法发送编辑,因为它只有一个字符
    • SMH... 我知道我很接近,我只需要一点常识;)谢谢大家的回答。我会解决这个问题,在我得到它的工作后,我会回来并标记正确的答案。非常感谢。
    【解决方案2】:

    我想我会这样做;

    $city = $_POST['city'];
    $addr = $_POST['addr'];
    $phone = $_POST['phone'];
    $condition = $_POST['condition'];
    
    $bindParams = [];
    $query = "UPDATE _MAIN_meta SET ";
    
    if(!empty($city)){
        $query .= "meta_city = ?, ";
        $bindParams[] = $city;
    }
    if(!empty($addr)){
        $query .= "meta_addr = ?, ";
        $bindParams[] = $addr;
    }
    if(!empty($phone)){
        $query .= "meta_phone = ?, ";
        $bindParams[] = $phone;
    }
    
    if(count($bindParams) == 0)
        die("Nothing to update");
    
    $query = substr($query, 0, -2); //remove the ", " at the end
    $query = " WHERE uid=? ";
    $bindParams[] = $uid;
    
    if(!empty($condition)){
        $query .= "AND condition = ?";
        $bindParams[] = $condition;
    }
    $stmt = $con->prepare($query);
    
    //if you want to bind parameters one by one:
    $i = 0;
    foreach($bindParams as $param){
        $stmt->bindValue($i, $param, PDO::PARAM_STR);
        $i++;
    }
    
    //or:
    $stmt->execute($bindParams);
    

    您可以扩展数组并添加参数名称,以及 PARAM_INT 等类型。
    此外,您应该检查并在必要时仅添加一次WHERE,例如当您在查询中默认不使用它时,但如果用户提供则可选

    【讨论】:

    • 唯一的问题,他们使用的是mysqli,而不是PDO
    • 啊。我没注意到。但我想他们会明白的,它可以转换为 mysqli
    【解决方案3】:

    使用您生成的现有 SQL 查询,您可以简单地执行以下操作:

    $bindParams = []; // array to hold existing parameter values for binding
    foreach(['prov', 'city', 'addr', 'code', 'phone', 'phone_alt'] as $param) { // Check all possible parameters
        if (!empty($$param)) { // If the parameter is not empty
            $bindParams[] = $$param; // Add it to the array
        }
    }
    $bindParams[] = $uid; // Add $uid to the array cause we always need it as the final value
    $stmt = prepared_Query($con, $sql, $bindParams);
    // Do whatever with $stmt
    

    【讨论】:

      猜你喜欢
      • 2019-10-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-25
      • 2016-07-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多