表单有时会被执行,有时不会


Form sometimes get executed, sometimes not

我有一个表格,其中select选项用于年龄,radiobuttons选项用于性别。其想法是,该表单可以用于按年龄和性别搜索特定用户。

目前,表单有时执行header(见下文),有时不执行。因此,假设我以Conor的身份登录,Conor指定他想搜索年龄在20-21岁之间的男性用户。点击提交后,有时表单会找到人,有时不会。我希望查询一直运行,直到找到用户为止,除非数据库中不存在任何用户。

在这种情况下,应该执行头部,将用户带到messages.php,因为数据库中存在一名20岁的男性。

这是我的方法:

表单

 <form action="random_chat.php" method="POST" enctype="multipart/form-data">
    <input type="hidden" name="age_from" id="age_from" value="0"/>
    <input type="hidden" name="age_to" id="age_to" value="50"/>
        <label for="amount">Age:</label>
        from:
        <select name="age_from" id="age_a" onchange="checkages_a()"> 
            <option value="none"></option>
            <?php
            for($i = 17; $i <= 50; ++$i) {
                echo "'t", '<option value="', $i. '">', $i, '</option>', "'n";
            }
            ?>
        </select>
        to: 
        <select name="age_to" id="age_b" onchange="checkages_b()"> 
            <option value="none"></option>
            <?php
            for($i = 18; $i <= 50; ++$i) {
                echo "'t", '<option value="', $i, '">', $i, '</option>', "'n";
            }
            ?>
        </select>
        <!-- I have input type submit above the radio buttons due to table layout -->
        <input type="submit" class="btn btn-info" name="submit" value="Click to start chat! " />
               <label for="amount">Gender:</label> 
                      <input type="radio" name="gender" value="male">Male</input> <br />
                      <input type="radio" name="gender" value="female">Female</input><br />
                      <input type="radio" name="gender" value="any">Any</input>     
</form>

PHP代码处理表单:

<?php
$refined_gender = htmlentities (strip_tags(@$_POST['gender']));
$age_from       = htmlentities (strip_tags(@$_POST['age_from']));
$age_to         = htmlentities (strip_tags(@$_POST['age_to']));
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
if (isset($_POST['submit'])){
    // if age parameter used...
    $defined_chat = mysqli_prepare ($connect, "SELECT * FROM users WHERE gender =? AND age BETWEEN ? AND ? ORDER BY RAND() LIMIT 1");
    mysqli_stmt_bind_param($defined_chat, "sss", $refined_gender, $age_from, $age_to); 
    mysqli_stmt_execute ($defined_chat);
    while ($get_user = mysqli_fetch_assoc($defined_chat)){
        $rand_name = $get_user['username'];
        $acc_type = $get_user['account_type'];
        if ($acc_type != "admin" ){
            // if the name genereated by db is same as logged in users name, then run query again until name is found.  
            if ($rand_name == $username){
                $defined_chat;
                } else {
                    header ("Location: /messages.php?u=$rand_name");
                }
        } else {
            echo "No user found fitting those requirements.";
        }
    } // while closed
    mysqli_stmt_close($defined_chat);   
} 
?>

我试图将表单操作更改为"#",认为这可能只是刷新页面,但没有成功。

此外,我如何做到这一点,即使一个参数被填充,仍然执行搜索?例如,如果我搜索一个没有定义年龄的男性用户,它会找到一个男性用户。如果我搜索年龄在26-31岁之间且没有性别定义的人,那么仍然执行标题?

编辑

$username是会话变量,它是在random_chat.php一开始就定义的。

不要依赖提交按钮的值来确定表单是否已提交。这并不适用于所有浏览器,尤其是较旧的浏览器,该值并不总是传递回服务器,而是只查看表单中的任何值,以验证是否已提交,或者是否存在$_POST。

乍一看,你要做的事情看起来比你实际尝试实现的方式更简单。

构造正确的SQL查询可能是这里唯一复杂的事情。实际上,只有更改您的查询才能消除您对account_type的if/else和检查当前用户是否与被查询用户相同的if/erse的需求:

$sql = "SELECT 
          * 
        FROM 
          users 
        WHERE 
          gender like ? AND 
          age BETWEEN ? AND ? AND 
          # with this condition you do not need to test if the user logged is the queried one
          username != ? AND 
          # and with this one, you do not care about exclude adimn either
          account_type != 'admin'
        ORDER BY RAND() 
        LIMIT 1";
$defined_chat = mysqli_prepare (
    $connect, $sql
);
mysqli_stmt_bind_param(
    $defined_chat,
    "ssss",
    $refined_gender,
    $age_from,
    $age_to,
    $username
);

然后,如果您希望能够在不选择性别和年龄的情况下进行搜索,您可以使用SQL的通配符%、运算符like和PHP的三元运算符的组合(您可能已经看到我在上面的查询中将gender =?更改为gender like ?)。

// Means if gender is different than 'any', it will assign the posted value to the variable, otherwise, it will assign the sql wildcard %
$refined_gender = (htmlentities (strip_tags(@$_POST['gender'])) != 'any' ? htmlentities (strip_tags(@$_POST['gender'])) : '%');
// Means if age is different than 'none', it will assign the posted value to the variable, otherwise, it will assign the lowest possible age, 0
$age_from       = (htmlentities (strip_tags(@$_POST['age_from'])) != 'none' ? htmlentities (strip_tags(@$_POST['age_from'])) : '0');
// Means if age is different than 'none', it will assign the posted value to the variable, otherwise, it will assign an age bigger than anyone could attain, 9999
$age_to         = (htmlentities (strip_tags(@$_POST['age_to'])) != 'none' ? htmlentities (strip_tags(@$_POST['age_to'])) : '9999');

请参阅PHP文档中的三元运算符并查看类似MySQL和通配符的使用

总而言之,您的处理PHP脚本可能如下所示:

$refined_gender = (htmlentities (strip_tags(@$_POST['gender'])) != 'any' ? htmlentities (strip_tags(@$_POST['gender'])) : '%');
$age_from       = (htmlentities (strip_tags(@$_POST['age_from'])) != 'none' ? htmlentities (strip_tags(@$_POST['age_from'])) : '0');
$age_to         = (htmlentities (strip_tags(@$_POST['age_to'])) != 'none' ? htmlentities (strip_tags(@$_POST['age_to'])) : '9999');
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
if (isset($_POST['submit'])){
    $sql = "SELECT 
              * 
            FROM 
              users 
            WHERE 
              gender like ? AND 
              age BETWEEN ? AND ? AND 
              # with this condition you do not need to test if the user logged is the queried one
              username != ? AND 
              # and with this one, you do not care about exclude adimn either
              account_type != 'admin'
            ORDER BY RAND() 
            LIMIT 1";
    $defined_chat = mysqli_prepare (
        $connect, $sql
    );
    mysqli_stmt_bind_param(
        $defined_chat,
        "ssss",
        $refined_gender,
        $age_from,
        $age_to,
        $username
    );
    mysqli_stmt_execute ($defined_chat);
    while ($get_user = mysqli_fetch_assoc($defined_chat)){
        $rand_name = $get_user['username'];
        header ("Location: /messages.php?u=$rand_name");
    } // while closed
    echo "No user found fitting those requirements.";
    mysqli_stmt_close($defined_chat);
}

您有一些混合逻辑,因此一些解释可能会有所帮助。

1) header("location:…")将告诉浏览器将页面重新加载到新位置。这似乎不是你想要的——你只是想继续执行吗?注意:您还应该[几乎]在标头('location:…')之后始终有"exit();";行,否则执行将继续,这几乎不是您想要的!)

2) while循环将在条件为true时继续。因此,循环在返回行的同时继续。

3) 再次运行查询不会返回任何新结果——您可以使用相同的结果。所以,只要跳过,直到你找到你需要的结果!

所以,用英语写的,在运行DB查询后你想做的是:

set a tally count to zero
while we have some rows coming from the db {
    if that row is not admin {
        if that row does not match the current user {
            show the result
            increase tally count
        }
    }
}
if tally count is zero {
    say "no entries found"
}

所以,在代码中,这是

$foundUsers = 0;
while ($get_user = mysqli_fetch_assoc($defined_chat)){
    $rand_name = $get_user['username'];
    $acc_type = $get_user['account_type'];
    if ($acc_type !== "admin" ){
        // if the name genereated by db is same as logged in users name, then run query again until name is found.  
        if ($rand_name !== $username) {
            $foundUsers = $foundUsers + 1;   // Or $foundUsers++ for short
            echo 'Matched User: ' . $rand_name . '<br>';
        }
    }
} // while closed
if ($foundUsers == 0) {
    echo "No user found fitting those requirements.";
}

好的,首先,如果你想从查询中排除一个参数,你必须构建一些逻辑来排除那个变量。

因此,如果是$refined_gender = "any",则需要将其从查询中排除。我会将您的组合框默认值更改为:

<select name="age_from" id="age_a" onchange="checkages_a()"> 
    <option value="-1"></option>
    <?php
    for($i = 17; $i <= 50; ++$i) {
        echo "'t", '<option value="', $i. '">', $i, '</option>', "'n";
    }
    ?>
</select>
to: 
<select name="age_to" id="age_b" onchange="checkages_b()"> 
    <option value="999"></option>
    <?php
    for($i = 18; $i <= 50; ++$i) {
        echo "'t", '<option value="', $i, '">', $i, '</option>', "'n";
    }
    ?>
</select>

然后,现在你已经固定了两者之间的年龄,以过滤掉性别。此外,我在WHERE子句中添加了一个子句:AND account_type != 'admin',这将过滤掉SQL端的管理员帐户,而不是在PHP端进行检查。

// If gender is specified, query gender
if($refined_gender !== "any"){
    $defined_chat = mysqli_prepare ($connect, "SELECT * FROM users WHERE gender =? AND age BETWEEN ? AND ? AND account_type != 'admin' ORDER BY RAND() LIMIT 1");
    mysqli_stmt_bind_param($defined_chat, "sii", $refined_gender, $age_from, $age_to); 
} else {
    $defined_chat = mysqli_prepare ($connect, "SELECT * FROM users WHERE age BETWEEN ? AND ? AND account_type != 'admin' ORDER BY RAND() LIMIT 1");
    mysqli_stmt_bind_param($defined_chat, "ii", $age_from, $age_to); 
}
mysqli_stmt_execute ($defined_chat);

建议#1:可能的竞争条件请参阅代码中的注释。

   if ($acc_type != "admin" ){
        // if the name genereated by db is same as logged in users name, then run query again until name is found.  
        if ($rand_name == $username){
            $defined_chat;<-- don't you need to re-execute this?  Seems like you are hitting a race condition since the statement result will never change
            } else {
                header ("Location: /messages.php?u=$rand_name");
            }
    } else {
        echo "No user found fitting those requirements.";
    }

建议#2:
除此之外,您应该确保当前用户没有WHERE name NOT LIKE '%?%'在初始查询的前面,并去掉if语句

建议#3:
或者更好的方法是使用用户ID。如果有另一个用户与搜索者同名,但他们是不同的人,该怎么办?基于UID而非名称匹配当前用户

建议4:
您绝对不应该在PHP(或任何脚本语言)循环中运行选择查询/语句。总有更好的方法。在高效的数据库中筛选数据。即使对于插入,您也可以比一堆插入查询更高效地执行单个大容量插入。