【问题标题】:Search More than 1 value inside 1 input text of HTML search form在 HTML 搜索表单的 1 个输入文本中搜索多个值
【发布时间】:2017-05-14 12:17:41
【问题描述】:

我有一个使用 HTML 的常用搜索表单,它将从 mysql 中提取数据(连接使用 PHP 和 mysqli)。该函数在搜索 1 个值时效果很好,但我想让用户可以搜索多个值,这些值将由逗号 (,) 分隔。

例如: 搜索 1 个值效果很好。

但我想在搜索框中搜索 2 个或多个值,例如:42-7278954,53-1217544,07-2517487,...

我希望用户可以输入如下图所示的内容,结果将有 2 行 --> CN_no 42-7278954和53-1217544:

到目前为止我的查询是:

$sql = "SELECT * FROM mock_data WHERE CN_no IN ('{$CN_no}') OR doc_no IN ('{$doc_no}')";

注意:CN_no 是“Shipment Code”,doc_no 是“Reference No”

但是...由于语法不正确,这显然给了我一个错误。

请帮我修改一下。谢谢。

======== 根据 vp_arth 的回答更新查询 ========

$cn = explode(',', $CN_no);
$incn = str_repeat('?, ', count($cn)-1).'?';
$doc = explode(',', $doc_no);
$indoc = str_repeat('?, ', count($doc)-1).'?';

$query = "SELECT * FROM mock_data WHERE CN_no IN ({$incn}) or doc_no IN ({$indoc})";
$result = $conn->query($query , array_merge($incn, $indoc));

但它给了我一个error

【问题讨论】:

  • 我还是不明白查询是如何检测用户输入的逗号(,)并将值放入数组中的。
  • 不相关,但要警惕Sql Injection
  • 不使用机密数据并不意味着您可以忽略安全性。
  • 你有IN ('42-7278954,53-1217544,07-2517487'),应该是IN ('42-7278954', '53-1217544', '07-2517487')。尝试了解占位符,那么它就只是IN (?, ?, ?)。

标签: php mysql sql mysqli


【解决方案1】:
$input = '42-7278954,53-1217544,07-2517487';
$inputs = explode(',', $input);

$new_inputs = array_map(function ($item){
    $item = trim($item);
    return "'" . $item . "'";
}, $inputs);

$string = implode(',', $new_inputs);

// string is  '42-7278954','53-1217544','07-2517487'

$sql = "SELECT * FROM mock_data WHERE CN_no IN ({$string})";

【讨论】:

  • $columns = array('col1', 'col2'); 很抱歉,但我认为对于数组来说只有 1 col。我会更新我的问题。
  • 请查看我更新的问题,我添加了一张图片。我希望你能明白我的意思。提前谢谢你。
  • WHERE CN_no IN ('42-7278954', '53-1217544') ??
  • 是的......类似的东西。我很困惑将其放入变量中。
  • 嗯...输入并不总是42-7278954,53-1217544,07-2517487,对吧?输入由用户决定。那我可以改成 $input = '{$CN_no}'; 吗?
【解决方案2】:

一些抽象的php代码:

  $cn = explode(',', $input1);
  $incn = str_repeat('?, ', count($cn)-1).'?'
  $doc = explode(',', $input2);
  $indoc = str_repeat('?, ', count($doc)-1).'?'
  $sql = "SELECT ... WHERE cn_no IN ({$incn}) or doc_no IN ({$indoc})";
  $result = $db->rows($sql, array_merge($cn, $doc));

【讨论】:

  • 我觉得我有问题 $result = $db->rows($sql, array_merge($incn, $indoc)); 是不是和mysqli不兼容句法?特别是 array_merge 部分。我已经编辑了我的问题,让你看到我编辑的代码和错误。
  • 可能是因为如果不是数组,就会出错? (如果用户只想搜索 1 个值)
  • 不要复制粘贴标记为abstract code的sn-ps
  • @anakpanti,最后一行有错误,显然应该是合并的数据数组,而不是问号字符串。
  • 这个答案的关键部分是将您的数据与查询分开,这是避免sql注入的唯一正确方法。
【解决方案3】:

使用FIND_IN_SET()函数

语法:FIND_IN_SET (YOUR_INPUT_STRING_NAME, COLUMN_NAME);

YOUR_INPUT_STRING_NAME 可能是 42-7278954,53-1217544,07-2517487,...

如果对你有帮助,请告诉我。

【讨论】:

    【解决方案4】:
    $input = '42-7278954,53-1217544,07-2517487';
    
    echo $strNew=str_replace(",","','",$input);
    
    $sql = "SELECT * FROM mock_data WHERE CN_no IN ('".$strNew."')";
    $row=mysqli_query($conn,$query)or die("not fire");
    

    试试这个代码

    【讨论】:

    • 无法避免 SQL 注入。
    【解决方案5】:

    这是在mysqli 中将prepared statements 与动态输入一起使用的可能解决方案。 parameter binding 的输入是静态的,在这种情况下,参数是 strings。

    /**
     * connecting to the database
     * defining in how manye columns you want to search (important to create the correct amount of arguments)
     */
    $conn = new mysqli('localhost', 'root', '', 'test');
    $columnsToSearch = 2;
    
    /**
     * the numbers you want to search delimited by ","
     */
    $CN_no = '42-7278954,53-1217544,07-2517487';
    $cn = explode(',', $CN_no);
    
    /**
     * writing the numbers to search into variables
     * putting the references of those variables into an array
     * 
     * the references will be used as arguments for the prepared statement
     */
    $values = array();
    for ($i = 0; $i < $columnsToSearch; $i++) {
        foreach ($cn as $k => $value) {
            $temp{$k}{$i} = $value;
            $values[] = &$temp{$k}{$i};
        }
    }
    /**
     * putting together the "types"-part for the binding of the prepared statement
     */
    $types = array(str_repeat('s', count($cn) * $columnsToSearch - 1) . 's');
    /**
     * merging types and references
     */
    $argumentsArray = array_merge($types, $values);
    /**
     * creating placeholder string for the query
     */
    $placeholder = str_repeat('?, ', count($cn) - 1) . '?';
    
    $stmt = $conn->prepare('SELECT CN_no, doc_no FROM mock_data WHERE CN_no IN (' . $placeholder . ') or doc_no IN (' . $placeholder . ')');
    
    /**
     * check http://us3.php.net/manual/en/mysqli-stmt.bind-param.php#104073 to read what is happening here
     */
    $ref = new ReflectionClass('mysqli_stmt');
    $method = $ref->getMethod("bind_param");
    $method->invokeArgs($stmt, $argumentsArray); 
    
    $stmt->execute();
    
    /**
     * fetching the result
     */
    $stmt->bind_result($CN_no, $doc_no);
    $row_set = array();
    while ($row = $stmt->fetch()) {
        $row_set[] = array('CN_no' => $CN_no, 'doc_no' => $doc_no);
    }
    var_dump($row_set);
    exit;
    

    我调整了 http://us3.php.net/manual/en/mysqli-stmt.bind-param.php#104073 的评论,使其适合您的情况。

    顺便说一句。使用PDO 作为数据库API,这将更容易编写和阅读。稍后我可能会为PDO 添加一个示例。

    【讨论】:

      【解决方案6】:
      $mysqli = new mysqli("localhost", "my_user", "my_password", "world");
      
      /* check connection */
      if (mysqli_connect_errno()) {
          printf("Connect failed: %s\n", mysqli_connect_error());
          exit();
      }
      // search input
      $input = '42-7278954,53-1217544,07-2517487';
      // create and filter your search array
      $iArr = array_filter(array_map('trim', explode(",", $input)), 'strlen');
      // create your search string from CN_no array 
      $CN_no = implode("|",$iArr); 
      
      /* create a prepared statement */
      if ($stmt = $mysqli->prepare("SELECT * FROM `mock_data` WHERE `CN_no` RLIKE ?")) {
      
          /* bind parameters for search */
          $stmt->bind_param("s", $CN_no);
      
          /* execute query */
          $stmt->execute();
          // get all the rows returned
          // IMPORTANT
          // Use the below syntax in case you do use native mysqlnd driver.
          // If you don't have mysqlnd installed/loaded, you will get an error
          // that "mysqli_stmt_get_result()" method is undefined. 
          $result = $stmt->get_result()->fetch_all();
          // in case you don't have native mysqlnd loaded uncomment 
          // and try the below syntax to get the result
          /* $meta = $stmt->result_metadata(); 
          while ($field = $meta->fetch_field()) { 
              $params[] = &$row[$field->name]; 
          } 
          call_user_func_array(array($stmt, 'bind_result'), $params); 
          while ($stmt->fetch()) { 
              foreach($row as $key => $val) { 
                  $c[$key] = $val; 
              } 
          $result[] = $c; 
          } */
          /* *********** */
          // print output
          print_r($result);
      
          /* close statement */
          $stmt->close();
      }
      
      /* close connection */
      $mysqli->close();
      

      【讨论】:

      • 你为什么不直接使用$result = $stmt-&gt;get_result() 并停在那里? OP应该知道如何使用它。这将使您的解决方案看起来不那么复杂。
      • @PaulSpiegel 你是对的,并且已经相应地改变了谢谢!
      • @PaulSpiegel 我做了更多的挖掘工作,并使用get_result 方法在 phpfiddle 中尝试了 sn-p,它返回未定义的方法。正如此处php.net/manual/en/… 中所述,这就是原因。所以我在原生mysqlnd的情况下改变了答案。
      【解决方案7】:

      试试

      $cn = explode(',', $CN_no);
      $incn = str_repeat('?, ', count($cn)-1).'?';
      $doc = explode(',', $doc_no);
      $indoc = str_repeat('?, ', count($doc)-1).'?';
      
      $query = "SELECT * FROM mock_data WHERE CN_no IN ('.$incn.') or doc_no IN ({$indoc})";
      $result = $conn->query($query , array_merge($incn, $indoc));
      

      【讨论】:

      • 欢迎来到 StackOverflow。这基本上是一个代码唯一的答案。请详细说明您的代码示例。
      • 这是获得合适数量“?”的聪明方法在适当的地方。但是,应该避免引用,因为query() 会添加引号。
      • mysqli::query() 不接受数组作为第二个参数。请阅读documentation。
      【解决方案8】:

      您的实现似乎没有正确生成in-clause。

      要正确生成 in-clause,您需要将用户输入拆分为数组,然后重新加入 in-list。例如,如果用户将 42-7278954,53-1217544,07-2517487 作为输入,in-list 应如下所示:

      CN_No in ( '42-7278954' , '53-1217544' , '07-2517487');
      

      鉴于发货代码和参考代码是字符串值,您必须将它们括在引号中' '.

      以下示例可以帮助您使用implode 和explode 生成列表内表达式(我对php 知之甚少)

      $in_list_cns = implode( "' , '", explode(",", $CN_no)); 
      $sql = "SELECT * FROM mock_data WHERE CN_no IN ( '{$in_list_cns}')" 
      

      希望对您有所帮助。

      另外,正如其他人所提到的,您可能需要清理用户输入以防止SQL-Injection 攻击。

      【讨论】:

      • 无法避免 SQL 注入。
      猜你喜欢
      • 1970-01-01
      • 2017-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多