从多个表单输入

时间:2017-02-15 22:22:25

标签: php sql mysqli

我正在寻找一种获取大量用户输入的方法,将它们连接成一个sql查询,并从我的数据库返回结果。到目前为止,我尝试了一些不同的技术,包括将所有变量放入一个数组,然后使用implode(),但我无法使其工作。为简单起见,我决定只使用几个if语句来检查每个变量是否有值。如果确实那么它应该添加一些sql。我的错误信息如下:

  

您的SQL语法有错误;检查手册   对应于您的MySQL服务器版本,以便使用正确的语法   靠近'AND type = AND(city LIKE'%%')或(addressLineOne LIKE   '%%')OR(第1行的`addres'

即使我在测试期间输入了输入,看起来$ type也没有被选中。除了$ type和$ bedroom之外,我没有给出任何其他输入值。 任何有关代码的帮助和改进将不胜感激。我是PHP和SQL的新手,很抱歉,如果它是愚蠢的,但我已经尝试解决这个问题多年了。

HTML

<form action="searchresults.php" method="get">
        <fieldset>
          <legend><h3>Search</h3></legend>
            <p>Please enter criteria for your search.</p>
            <label for="location">Location</label>
            <input type="text" name="location" />
            <select name="type">
              <option value="Studio Flat" selected>Studio Flat</option>
              <option value="Flat">Flat</option>
              <option value="Detached">Detached</option>
              <option value="Semi-detached">Semi-detached</option>
              <option value="Terraced">Terraced</option>
              <option value="Bungalow">Bungalow</option>
            </select>
            <label for="bedroom">Bedrooms</label>
            <select name="bedroom">
              <option value="1" selected>1</option>
              <option value="2">2</option>
              <option value="3">3</option>
              <option value="4">4</option>
              <option value="5">5</option>
              <option value="6">6</option>
              <option value="7">7</option>
              <option value="8">8</option>
              <option value="9">9</option>
              <option value="10">10</option>
            </select>
            <label for="min">Min Price</label>
            <input type="number" name="min" />
            <label for="max">Max Price</label>
            <input type="number" name="max" />
                <br />
            <input type="submit" value="Search" />
        </fieldset>
      </form>

PHP

<?php
session_start();
include './auth.php'; // connection to db

$location = trim($_POST['location']);
$location = strip_tags($location);
$location = htmlspecialchars($location);

$bedroom = trim($_POST['bedroom']);
$bedroom = strip_tags($bedroom);
$bedroom = htmlspecialchars($bedroom);

$type = trim($_POST['type']);
$type = strip_tags($type);
$type = htmlspecialchars($type);

$max = trim($_POST['max']);
$max = strip_tags($max);
$max = htmlspecialchars($max);

$min = trim($_POST['min']);
$min = strip_tags($min);
$min = htmlspecialchars($min);


// build query
$query = "SELECT * FROM Listings WHERE `bedroom` = ".$bedroom." AND `type` = ".$type."";

if(isset($location)){
  $query .= " AND (`city` LIKE '%".$location."%') OR (`addressLineOne` LIKE '%".$location."%') OR (`addressLineTwo` LIKE '%".$location."%') OR (`county` LIKE '%".$location."%')";
}
if(isset($max)){
  $query .= " AND (`price` <= '%".$price."%')";
}
if(isset($min)){
  $query .= " AND (`price` >= '%".$price."%')";
}

$query .= "ORDER BY price;";

// send query to database and return error if it fails
$input = mysqli_query($connect, $query) or die(mysqli_error($connect));
  // output results
  if(mysqli_num_rows($input)>0){ // if one or more results returned do this code
    while($result = mysqli_fetch_array($input)){ // puts data in array then loops the following code
      echo "<p><h3>".$result['addressLineOne']." ".$result['addressLineTwo']."
      ".$result['location']."</h3><h4>£".$result['price']."</h4>".$result['information']."</p><br /><hr />";
    }
    }else{ // no results then print the following
      echo "Sorry, we couldn't find any results.
        Please refine your search and try again.";
  }

  echo $query;
  // close the connection
  mysqli_close($connect)
?>

2 个答案:

答案 0 :(得分:1)

我知道你目前正在使用mysqli,但PDO使构建动态查询变得更加容易,所以如果你在这个项目上不是很远的话,我强烈建议你切换到它。

mysqli prepared statement中,您必须调用mysqli_stmt::bind_param(),传递参数列表中的每个参数。相反,PDO不需要绑定,参数全部传递给数组中的PDOStatement::execute()。这个答案将向您展示您的代码如何与PDO一起使用。

<?php

$connection = new \PDO("mysql:host=$db_host;dbname=$db_name", $db_user, $db_pass);

$query = "SELECT * FROM Listings WHERE bedroom = :bed AND type = :type AND (city LIKE :loc OR addressLineOne LIKE :loc OR addressLineTwo LIKE :loc OR county LIKE :loc)";
$parameters = [
    ":bed" => $_POST["bedroom"],
    ":type" => $_POST["type"],
    ":loc" => "%$_POST[location]%",
];

if(!empty($_POST["max"])) {
    $query .= " AND price <= :max";
    $parameters[":max"] = $_POST["max"];
}

if (!empty($_POST["min"])) {
    $query .= " AND price >= :min";
    $parameters[":min"] = $_POST["min"];
}

$query .= " ORDER BY price";

$stmt = $connection->prepare($query);
$stmt->execute($parameters);
$results = $stmt->fetchAll(\PDO::FETCH_ASSOC);

if (empty($results)) { // no results then print the following
    echo "Sorry, we couldn't find any results. Please refine your search and try again.";
}
foreach ($results as $result) {
    //escape for HTML output
    $result = array_map("htmlspecialchars", $result);
    echo <<< HTML
<p>
<h3>$result[addressLineOne] $result[addressLineTwo] $result[location]</h3>
<h4>£$result[price]</h4>
$result[information]
</p>
<br />
<hr />

HTML;
}

我还使用heredoc字符串简化了HTML输出,但您应该将HTML和PHP分开。

如果这是一个更大的现有项目的一部分,你可能会坚持使用mysqli,在这种情况下,我建议你学习如何使用准备好的语句;使用字符串连接构建查询的日子已经过去了!

答案 1 :(得分:0)

使用PDO和绑定参数@ miken32建议是可能的,但我建议反对它,因为它有几个缺点:

  • 您目前正在使用mysqli,它也支持绑定参数,所以没有理由只是为了获取参数绑定而切换到PDO
  • @miken32的解决方案在查询中多次使用命名参数(只有PDO支持的内容)。当启用客户端参数绑定(即PDO::ATTR_EMULATE_PREPARES设置为true)时,此有效,其本身有多个问题:
    • 我无法快速找出PDO::ATTR_EMULATE_PREPARES的默认值,显然&#34;这取决于驱动程序&#34;。
    • 启用PDO::ATTR_EMULATE_PREPARES后,您无法再在LIMIT子句中使用绑定参数
    • 从PDO返回的错误完全不同,具体取决于您是使用客户端参数绑定还是服务器端参数绑定,例如缺少参数会导致SQLSTATE为HY000与服务器 - side和HY093与客户端参数绑定。这使正确的错误处理变得复杂。
  • 无法查看实际查询,这是一种非常有用的功能(在我看来是必不可少的),用于调试。

所以,我说要继续手动构建您的查询,但要以干净和安全的方式。要做到这一点,您需要了解这些概念:

HTML编码

通常,您需要htmlspecialchars()的唯一地方是将数据(例如从数据库)传递到浏览器。

所以,改变每一个

$location = trim($_POST['location']);
$location = strip_tags($location);
$location = htmlspecialchars($location);

$location = trim($_POST['location']);

此时无需strip_tags()htmlspecialchars()。来自浏览器的表单数据在没有任何编码的情况下到达PHP端。当然,如果某人实际上将some<br>city输入到位置字段中,他就找不到任何房间,但如果您正确使用其余的应用程序,则不会破坏任何房间。

同时更改每个

echo "<p>".$result['addressLineOne']." ... </p><br /><hr />";

echo "<p>".htmlspecialchars($result['addressLineOne'])." ... </p><br /><hr />";

否则,人们将数据输入进入数据库可以运行&#34;跨站点脚本攻击&#34; - 恶意或意外。

SQL编码

当作为查询的一部分将数据发送到数据库时,您必须以数据库知道它的可变数据而不是SQL命令的一部分的方式对其进行编码。

所以,而不是例如

$query .= " AND (`city` LIKE '%".$location."%') ";

你必须写

$query .= " AND (`city` LIKE '%".addslashes($location)."%') ";

这可以确保如果'变量中有引号字符($location),它将被转义,因此它不会在该点结束字符串。

如果您使用UTF-8以外的字符集,则应使用mysql_real_escape_string()代替addslashes(),但您可能使用的是UTF-8,所以它很好。< / p>

此外,在这种情况下,您可能希望删除或转义值中的%_,因为它们在LIKE查询中具有特殊含义。

您的代码中还有两个问题:

$query = "SELECT * FROM Listings WHERE `bedroom` = ".$bedroom." AND `type` = ".$type."";

在这里,您不仅需要添加addslashes(),还需要添加值周围的引号:

$query = "SELECT * FROM Listings WHERE `bedroom` = '".addslashes($bedroom)."' AND `type` = '".addslashes($type)."'";

在这里:

$query .= " AND (`price` <= '%".$price."%')";

显然百分号在这里毫无意义,你的意思是:

$query .= " AND (`price` <= '".addslashes($price)."')";

库和模板引擎

稍后你会建议使用一个为你做这些事情的图书馆或框架(因为它很容易忘记addslashes()某个地方),但它不会伤害到首先手动完成,以了解它是如何工作的。