过滤器不起作用(根据参数执行不同的 PHP 查询)
Filters not working (Executing different PHP queries based on parameters)
我正在尝试实施过滤器,以帮助用户优化那里的其他用户搜索。这是我的搜索参数的图像,只是为了向您提供我即将传达的内容的图形表示:
共有三个过滤器:
- 性别
- 年龄
- 研究的相似性
默认情况下,我想传达系统上的所有用户。因此,当用户进入 users.php
时,将显示每个用户,然后,在应用过滤器时,相应地优化结果。
并非必须完成所有三个参数才能开始搜索,例如,用户可以简单地搜索女性用户,它应该在搜索点击时显示所有女性用户。
我尝试针对每个场景实现不同的查询,但始终显示所有用户。如果我指定我要搜索女性然后点击搜索,它什么也不会做,仍然显示所有用户。
此外,我正在努力处理 similarity in studies
参数。它的工作方式是,在一个名为 user_bio
的 table 中,我正在存储有关用户正在学习的内容的数据,用户可以选择不提供此信息,因此学习也可以在我的 table。
我希望它的工作方式是查看登录用户正在学习的内容,然后在其他人的简历中找到匹配的单词。例如,我目前以 Conor 身份登录,而 Conor 正在学习计算机科学。理想情况下,算法将 运行 从 user_bio
table 搜索其他用户的个人资料,并且 return 所有拥有 computer
或 science
的用户在他们的简历中。我很确定这与 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);
}
}
总结一下,我需要的是:
- 默认在
users.php
上执行的 SELECT * FROM users
查询。这将显示系统中的所有用户。
- 对于要应用的任何过滤器。并非需要应用所有过滤器才能获得结果,用户可以搜索女性并点击搜索,加载系统中的所有女性用户。
- 我需要根据应用的过滤器更改查询。所以如果用户搜索过男性用户,并且没有选择其他两个选项,则查询将是
"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 和静态绑定类型的方法,并列出绑定值是可行的。
但是当我们得到三个、四个、五个可能的过滤器以及所有可能的组合时,这将变得很笨拙。
所以我们采用更动态的方法,动态创建查询,并将我们的绑定值推送到数组上。
我正在尝试实施过滤器,以帮助用户优化那里的其他用户搜索。这是我的搜索参数的图像,只是为了向您提供我即将传达的内容的图形表示:
共有三个过滤器:
- 性别
- 年龄
- 研究的相似性
默认情况下,我想传达系统上的所有用户。因此,当用户进入 users.php
时,将显示每个用户,然后,在应用过滤器时,相应地优化结果。
并非必须完成所有三个参数才能开始搜索,例如,用户可以简单地搜索女性用户,它应该在搜索点击时显示所有女性用户。
我尝试针对每个场景实现不同的查询,但始终显示所有用户。如果我指定我要搜索女性然后点击搜索,它什么也不会做,仍然显示所有用户。
此外,我正在努力处理 similarity in studies
参数。它的工作方式是,在一个名为 user_bio
的 table 中,我正在存储有关用户正在学习的内容的数据,用户可以选择不提供此信息,因此学习也可以在我的 table。
我希望它的工作方式是查看登录用户正在学习的内容,然后在其他人的简历中找到匹配的单词。例如,我目前以 Conor 身份登录,而 Conor 正在学习计算机科学。理想情况下,算法将 运行 从 user_bio
table 搜索其他用户的个人资料,并且 return 所有拥有 computer
或 science
的用户在他们的简历中。我很确定这与 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);
}
}
总结一下,我需要的是:
- 默认在
users.php
上执行的SELECT * FROM users
查询。这将显示系统中的所有用户。 - 对于要应用的任何过滤器。并非需要应用所有过滤器才能获得结果,用户可以搜索女性并点击搜索,加载系统中的所有女性用户。
- 我需要根据应用的过滤器更改查询。所以如果用户搜索过男性用户,并且没有选择其他两个选项,则查询将是
"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 和静态绑定类型的方法,并列出绑定值是可行的。
但是当我们得到三个、四个、五个可能的过滤器以及所有可能的组合时,这将变得很笨拙。
所以我们采用更动态的方法,动态创建查询,并将我们的绑定值推送到数组上。