过滤器不起作用(根据参数执行不同的 PHP 查询)

Filters not working (Executing different PHP queries based on parameters)

我正在尝试实施过滤器,以帮助用户优化那里的其他用户搜索。这是我的搜索参数的图像,只是为了向您提供我即将传达的内容的图形表示:

共有三个过滤器:

  1. 性别
  2. 年龄
  3. 研究的相似性

默认情况下,我想传达系统上的所有用户。因此,当用户进入 users.php 时,将显示每个用户,然后,在应用过滤器时,相应地优化结果。

并非必须完成所有三个参数才能开始搜索,例如,用户可以简单地搜索女性用户,它应该在搜索点击时显示所有女性用户。

我尝试针对每个场景实现不同的查询,但始终显示所有用户。如果我指定我要搜索女性然后点击搜索,它什么也不会做,仍然显示所有用户。

此外,我正在努力处理 similarity in studies 参数。它的工作方式是,在一个名为 user_bio 的 table 中,我正在存储有关用户正在学习的内容的数据,用户可以选择不提供此信息,因此学习也可以在我的 table。

我希望它的工作方式是查看登录用户正在学习的内容,然后在其他人的简历中找到匹配的单词。例如,我目前以 Conor 身份登录,而 Conor 正在学习计算机科学。理想情况下,算法将 运行 从 user_bio table 搜索其他用户的个人资料,并且 return 所有拥有 computerscience 的用户在他们的简历中。我很确定这与 LIKE 子句有关,但我以前从未使用过它,所以我不能确定。

这是我目前的做法:

// processing filters
$refined_gender = htmlentities (strip_tags(@$_POST['gender']));
$age_from       = htmlentities (strip_tags(@$_POST['age_from']));
$age_to         = htmlentities (strip_tags(@$_POST['age_to']));
$studying       = htmlentities (strip_tags(@$_POST['studying']));

$get_all_users = mysqli_query ($connect, "SELECT * FROM users" );

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

if (isset($_POST['submit'])){   
        // if gender parameter is used ...
        if ($refined_gender){
                $gender_statement = mysqli_prepare ($connect, "SELECT * FROM users WHERE gender = ?");
                mysqli_stmt_bind_param($gender_statement, "s", $refined_gender); 
                mysqli_stmt_execute ($gender_statement);
                mysqli_stmt_close($gender_statement);
        }
        // if studying parameter used...
        if ($studying) {
            // see explanation below...
        }
        // if gender and age parameter used...
        if ($refined_gender && $age_from && $age_to){
                $gen_and_age_statement = mysqli_prepare ($connect, "SELECT * FROM users WHERE gender = ? AND age BETWEEN ? AND ?");
                mysqli_stmt_bind_param($gen_and_age_statement, "sss", $refined_gender, $age_from, $age_to); 
                mysqli_stmt_execute ($gen_and_age_statement);
                mysqli_stmt_close($gen_and_age_statement);
        }
}

总结一下,我需要的是:

  1. 默认在 users.php 上执行的 SELECT * FROM users 查询。这将显示系统中的所有用户。
  2. 对于要应用的任何过滤器。并非需要应用所有过滤器才能获得结果,用户可以搜索女性并点击搜索,加载系统中的所有女性用户。
  3. 我需要根据应用的过滤器更改查询。所以如果用户搜索过男性用户,并且没有选择其他两个选项,则查询将是"SELECT * FROM users WHERE gender = '$var_here'

代替此代码:

 htmlentities (strip_tags(@$_POST['gender']));

你应该验证它,像这样:

 $gender = filter_input(INPUT_POST, 'gender', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^[mf]$/i']]);
 $ageFrom = filter_input(INPUT_POST, 'age_from', FILTER_VALIDATE_INT, [ 'default' => 1, 'min_range' => 1, 'max_range' => 100]);
 $ageTo = filter_input(INPUT_POST, 'age_to', FILTER_VALIDATE_INT, [ 'default' => 1, 'min_range' => 1, 'max_range' => 100]);
 $studying = filter_input(INPUT_POST, 'gender', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^(similar|different|same)$/i']]);

这样更简单也更安全。

应正确验证每个输入。

避免使用 @.

获得这些值后,您可以将它们连接到您的查询中,如下所示:

$types = '';
$values = [];
$query = 'SELECT * FROM users';
$where = [];

// empty tests for both null (no data in input) and false (invalid data)
if (!empty($gender)) {
    $where[] = 'gender = ?';
    $types .= 's';
    $values[] = &$gender;
}
if (!empty($ageFrom)) {
    $where[] = 'age >= ?';
    $types .= 'i';
    $values = &$ageFrom;
}
if (!empty($ageTo)) {
    $where[] = 'age <= ?';
    $types .= 'i';
    $values = &$ageTo;
}
if (!empty($studying)) {

    $field = 'user_bio';

    // Get the $user_bio value of the current user from the database

    // Change the $user_bio into a regular expression collection of words
    $regexp = '('.str_replace(' ','|',$user_bio).')';

    // Set up the where 
    switch ($studying) {
        case 'same':
            $comparison = '= ?';
            break;
        case 'different':
            $comparison = 'NOT REGEXP (?)';
            break;
        case 'similar':
            $comparison = 'REGEXP (?)';
            break;                
    }
    $where[] = $field.' '.$comparison;
    $types .= 's';
    $values[] = &$user_bio;
}

if (count($where) > 0) {
    $query .= ' WHERE '.implode(' AND ',$where);
}

// new mysqli ( host, 
$mysqli = new mysqli('localhost','root','','stuff');
$stmt = $mysqli->prepare($query);

// This allows you to use a variable number of arguments with the prepared statement
// Note the use of the ampersands on the array assignment, this ensures they are passed by reference
$params = array_merge([$types],$values);
call_user_func_array([$stmt,'bind_param'],$params);

$stmt->execute();

// Bind a variable for each column
$stmt->bind_result($user_name);

while ($stmt->fetch()) {
var_dump($result);
}

我在这里提供代码,这样你怎么能在一个查询中写多个过滤器选项..但是我在这里没有提到你的第三个过滤器选项研究,因为它是关于另一个 table 而你是没有明确提到它,因此它使用外键或以下关系数据库 structure.any 链接到此 table 方式多过滤器选项如下..这里我添加了数据库连接和转义注入的功能...如果你不需要忽略那部分..

function escape($e_string)
{
    global $connect;
    if(!isset($connect))
    {
        // DATABASE CONNECTION QUERY
        $connect = mysqli_connect("servername", "username", "password", "");
        if (!$connect) 
            die("Connection failed: " . mysqli_connect_error());
    }   
    $e_string = trim(utf8_encode($e_string));   
    $e_string = mysqli_real_escape_string($connect,$e_string);
    return $e_string;   
}

// processing filters
$refined_gender = isset($_POST['gender']) ? escape($_POST['gender']) : '';
$age_from       = isset($_POST['age_from']) ? escape($_POST['age_from']) : '';
$age_to         = isset($_POST['age_to']) ? escape($_POST['age_to']) : '';
$studying       = isset($_POST['studying']) ? escape($_POST['studying']) : '';

$query = "SELECT * FROM users WHERE 1=1";

if (isset($_POST['submit'])){

        $addstring1 = $addstring2 = $addstring3 =  $and1 = $and2 = $and3 = "";
        $andcnt =3;

        if($refined_gender != '') 
        $addstring1 = " gender = '$refined_gender'";    

        if($age_from != '') 
        $addstring2 = " age >= '$age_from'";    

        if($age_to != '') 
        $addstring3 = " age <= '$age_to'";    

        for($i=1;$i<=$andcnt;$i++)
        ${"and".$i} =  ${"addstring".$i} != '' ? " AND" : "";

        $query .= $and1.$addstring1.$and2.$addstring2.$and3.$addstring3;

}
$get_all_users = mysqli_query ($connect, $query);
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

此 Html 页:

<form method="POST" action="">
    <input type="radio" name="rbo_gender" value="male">Male
    <input type="radio" name="rbo_gender" value="female">Female
    Age From<select name="agefrom">
        <?php
        for($i=10;$i<50;$i++):
        ?>
        <option value="<?php echo $i?>"><?php echo $i?></option>
        <?php
        endfor;
        ?>
    </select>
    Age To<select name="ageto">
        <?php
        for($i=10;$i<50;$i++):
        ?>
        <option value="<?php echo $i?>"><?php echo $i?></option>
        <?php
        endfor;
        ?>
    </select>
    Studying:
    <input type="radio" name="rbo_type" value="similar">Similar
    <input type="radio" name="rbo_type" value="exact">Exactly same
    <input type="radio" name="rbo_type" value="different">Different
    <input type="submit" name="btnsearch" value="Search">
</form>

这是 php 部分:

if($_POST["btnsearch"])
{
   if(!empty($_POST["rbo_gender"]))
   {
       $gender = $_POST["rbo_gender"];
       $cond .= " and gender = '".$gender."'";
   }
   if(!empty($_POST["agefrom"]))
   {
       $agefrom = $_POST["agefrom"];
       $cond .= " and age >= '".$agefrom."'";
   }
   if(!empty($_POST["ageto"]))
   {
       $ageto = $_POST["ageto"];
       $cond .= " and age <= '".$ageto."'";
   }
   if(!empty($_POST["rbo_type"]))
   {
       $user_type = $_POST["rbo_type"];
       switch($_POST["rbo_type"])
       {
           case "similar": $cond .= " and user_bio like '%".$ageto."%'";
               break;
           case "exact": $cond .= " and user_bio = '".$ageto."'";
               break;
           case "different":$cond .= " and user_bio ! like '%".$ageto."%'";
               break;       
       }
   }

   $query = "select * from users where 1 ".$cond;
}

请根据 mysqli() 更新查询并使用绑定参数。另外,不要使用 @,而是尝试使用 filter_input,您可以使用 REGEXP 而不是 like also。我已经创建了使用 bind_param 目的的变量。

(我不确定为什么已经提供的答案不能充分解决您的问题。)

我会这样处理。首先,摆脱第一次查询执行以拉动所有用户。相反,只使用一个查询。

动态准备 SQL 文本。语句以 "SELECT ... FROM users" 开始。 (我们将在最后一步处理附加 ORDER BY。

我会有条件地检查每个 "filter",看看我是否需要将条件附加到 WHERE 子句。

在 SQL 的开头,我们将包含一个 "WHERE 1=1"。

$sql = "SELECT ... FROM users u" 
$sql .= " WHERE 1=1";

"WHERE 1=1"基本没用。优化器将把它扔掉。我们添加它的原因只是为了让我们的代码以后更容易。我们可以只用“AND 条件”附加我们的下一个过滤器,而不用担心这是否是第一个,我们需要使用 WHERE 而不是 AND。

我们将初始化一个字符串和一个数组,以保存我们的绑定类型字符串 "sssis" 无论它需要什么,以及对我们要传入的值的引用数组。

  $bind_type = "";
  $bind_vals = array();

每个过滤器的处理过程都会很麻烦...但我们可以做到。检查我们是否需要向 SQL 添加任何内容。如果我们这样做,找出需要添加的内容,包括任何绑定占位符。并将绑定参数的类型("i"、"s" 等)附加到 $bind_type 字符串,并将值(引用)推入我们的 $bind_vals数组。

if ( $refined_gender ) {
   // figure out what that SQL text needs to look like
   // append the string to the SQL text
   $sql .= " AND u.gender = ?";

   // append type to string, and push a reference to the value into array 
   $bind_types .= "s";
   $bind_val[] = &$refined_gender;
}

我们的代码会比那更复杂。那只是处理相等比较。我们现在只是让事情变得简单,以说明模式。

我们对可能需要添加的每个过滤器重复相同的操作。检查是否需要它,找出我们需要附加到 SQL 文本的内容,附加到 bind_types 字符串并将值推入(引用)到 bind_vals 数组中。

为了解决这个问题,我将从只处理一个条件开始,然后让它发挥作用,以解决问题。当我们添加更多过滤器时,出现问题时,我知道在哪里寻找问题。 (我知道之前的工作是什么。)

当我完成 WHERE 子句后,我附加了我需要的任何 ORDER BY 和 LIMIT。这可能是有条件的,但最后,我们将结束做这样的事情:

   $sql .= " ORDER BY u.id DESC LIMIT 50";

当我完成所有这些后,我得到了一个包含 SQL 文本的字符串,看起来像这样:

  SELECT ...
    FROM users u 
   WHERE 1=1
     AND u.gender = ?
     AND u.age_from >= ?
     AND u.age_to   <= ?
   ORDER BY u.id DESC
   LIMIT 50

(在这个例子中,它包含三个绑定占位符。如果我们做对了,

我们将得到一个包含三个字符的 $bind_types 字符串,例如"sii"

我们将有一个包含对三个值的引用的 $bind_vals 数组。

现在,我们可以调用mysqli_stmt_prepare。如果我们的 SQL 中没有错误,我们应该取回语句句柄。

$stmt = mysqli_prepare($conn,$sql);

(检查准备中的 return。)

现在我们只需要绑定我们的参数。这就是 mysqli 使事情变得有点毛茸茸的地方。如果我们使用 PDO(或 Perl DBI),调用 "bind parameter/bind value" 会很容易。这些会让我们传递一个绑定值数组。但不是mysqli。他不会让我们用数组作为参数调用 mysqli_stmt_bind_param。

我们需要运行这样的函数调用:

mysqli_stmt_bind_param($stmt, $bind_types, &$refined_gender, &$age_from, ... );

我们的问题是我们有一个变量个参数。

有一个解决方法。

我们可以使用call_user_func_array函数。

因为代码使用的是过程式风格而不是面向对象的风格,准备语句的句柄是第一个参数,第二个参数是绑定类型字符串,然后是绑定值。绑定值已经在一个数组中。我们只需要将所有这些都放入一个 hugh jass 数组中即可。

array_merge 函数似乎是专门为此而设计的。

 // array_merge(array($stmt), array($bind_types), $bind_vals)

这将 return 我们一个数组。这正是我们调用 call_user_func_array 函数所需要的。我们在其他任何地方都不需要该数组(除非我们正在调试,并且我们想将其打印出来)。

如果我们的语句中至少有一个绑定占位符,我们只需要调用 mysqli_stmt_bind_param。因此,如果我们的 $bind_types 字符串为空,我们可以绕过它。 (而且我们知道 $bind_types 不会是“0”,因为我们的代码从未向它附加“0”。)

 if ($bind_types) {
    call_user_func_array('mysqli_stmt_bind_param', array_merge(array($stmt), array($bind_types), $bind_vals) );
 }

第一个参数(to call_user_func_array)是我们要执行的函数的名称,第二个参数是我们要转换成列表的hugh jass数组。

这样做的全部意义在于使其动态化,我们可以传入一、二、三、绑定值。

此时,我们已准备好执行语句并获取结果。

再次强调:mysqli_stmt_bind_param 期望绑定值通过引用 传递,而不是通过值传递。这就是为什么我们将 references 推入 bind_vals 数组的原因。

我不确定你问的是什么问题。

但绝对放弃对 mysqli_query 的第一次调用。这将 return 用户 table.

中的所有行

有一个或两个条件,静态 SQL 和静态绑定类型的方法,并列出绑定值是可行的。

但是当我们得到三个、四个、五个可能的过滤器以及所有可能的组合时,这将变得很笨拙。

所以我们采用更动态的方法,动态创建查询,并将我们的绑定值推送到数组上。