如何在PHP中创建动态MySQL更新查询

时间:2015-12-12 22:43:47

标签: php mysql pdo

这对我来说至少是一个稍微复杂的话题,我正在创建一个网站,要求能够更新已输入并保存到数据库的信息。我已经设法使用单个属性有效地工作,但我希望修改它以便能够更改任何选定的属性。将从名为update.php

的页面的下面代码中的下拉菜单中选择所选属性
<form action="updatejob.php" method="POST">
            <label for="jobs_id">Job ID:</label><input type="text" name="jobs_id" />
            <select name="updatecategory">
                <option value="title">Title</option>
                <option value="category">Category</option>
                <option value="salary">Salary</option>
                <option value="location">Location</option>
                <option value="description">Description</option>
            </select>
            <label for="newcontent">Updated Information: </label><input type="text" name="newcontent" />
            <input type="submit" value="Submit" name="submit"/>
    </form>

我试图编写的查询位于名为updatejob.php的文件中:

if(isset($_POST['jobs_id'])){
         $updatetitle= $pdo->prepare('UPDATE jobs SET :updatecategory  =  :newcontent WHERE jobs_id = :jobs_id');
         unset($_POST['submit']);
         $updatetitle->execute($_POST);
        header("Location:admin.php");
}

正如我上面提到的,当我将它设置为一个不在选择框中的特定属性时,这可以正常工作。以前我设置它,所以用户只能使用文本框编辑记录的标题。说实话,我甚至不确定这是否可能,或者我是否特别接近正确的方式,所以所有的帮助都表示赞赏!

2 个答案:

答案 0 :(得分:0)

有趣。 According to the PDO docs,您不能绑定列名等标识符,因此您需要在动态查询中使用一些变体,这通常会让您注入攻击攻击。我们想到的最强大的解决方案是使用查找表将<select>值索引到列名称,这些值用于填充空白&#34;在准备好的声明中。

column_lookup
+----------+----------+
| selval   | colname  |
+==========+==========+
| title    | title    |
| category | category |
| location | location |
| etc.     | etc.     |

然后你会做类似

的事情
$colsql = "SELECT colname FROM column_lookup WHERE selval = :selval"
$colstmt = $dbh->prepare($colsql);
$res = $colstmt->execute(array(':selval' => $_POST['updatecategory']));

$updsql = "UPDATE jobs
             SET " . $res->fetchColumn(0) . " =  :newcontent
             WHERE jobs_id = :jobs_id";
$updstmt = $dbh->prepare($updsql);
$updstmt->bindValue(':newcontent', $_POST['newcontent'], PDO::PARAM_STR);
$updstmt->bindValue(':jobs_id', $_POST['jobs_id'], PDO::PARAM_whatever);
$updstmt->execute;

这样可以通过准备好的语句清理所有输入,同时仍然动态生成UPDATE查询。

我的习惯是将错误处理留给读者练习。特别是,我省略了任何验证jobs_id的数据类型的尝试。

答案 1 :(得分:0)

我在你当前的方法中看到了一个拦截器。除了不清理您的数据。您正在尝试参数化列。 prepare语句的工作方式是发送/缓存查询计划并分别发送参数。因此,您无法绑定部分查询计划,因为它事先已发送/缓存。

  

为语句调用PDO :: prepare()和PDOStatement :: execute()   将使用不同的参数值多次发出   通过允许驱动程序优化应用程序的性能   协商客户端和/或服务器端缓存查询计划和   元信息,并有助于防止SQL注入攻击   无需手动引用参数。

php docs

这并不意味着你不能拥有查询计划的动态方面,你就是不能让它们受到约束。

动态查询计划示例。

<?php 
$whiteListedColumns = array(
    "valid", "schema", "etc"
);

$updateCategory = in_array($_POST["updatecategory"], $whiteListedColumns) ? $updateCategory : null;
if($updateCategory) {
    //Sanitize Here
    //Its really important to understand what the filter reference docs.
    //You can skip for now. 
    $params = array(
        "newcontent" => filter_var($_POST["newcontent"], FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES),
        "jobs_id" => (int) $_POST["jobs_id"],
    );

    //Notice I simply concatenated the query string (didn't bind)
    $sql = "UPDATE jobs 
    SET {$updateCategory}  =  :newcontent 
    WHERE jobs_id = :jobs_id";

    $stmt = $pdo->prepare($sql);
    $stmt->execute($params); //This is what is binding the params

    //redirect 
    header("Location:admin.php");
    exit();
} else {
    //Invalid Column
    //Throw
}

除了拥有静态白名单之外,您还可以发出另一个查询来读取数据库架构。

<强>建议: 使用像Doctrine这样的DBAL。它将为您完成大部分基础工作。

希望这有用!