質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.99%

MySQL、PHPで複数の項目から部分検索ができる検索システム

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 202

TaraIkura

score 1

前提・実現したいこと

以前使っていたMySQL、PHPで複数の項目から検索システムをPDO、bindValueを使って更新しているのですが
実装すると上手くいきません。お力を貸していただけると幸いです。
列の項目には複数の文字が含まれるためLIKEの部分検索を取り入れています。

以前のPHP&HTML コード

//search.phpのコード
function getUserData( $params ) {
  //DBの接続情報
  include_once( 'database.php' );

  //DBコネクタを生成
  $Mysqli = new mysqli( $dsn, $usr, $passwd );
  $Mysqli->set_charset( 'utf8' );
  $query = "SELECT * FROM TABLE1";
  //接続状況チェック
  if ( $Mysqli->connect_error ) {
    error_log( $Mysqli->connect_error );
    exit;
  }
  //入力された検索条件からSQl文を生成
  $where = [];
  if ( !empty( $params[ 'search_name' ] ) ) {
    $where[] = "search_name like '%{$params['search_name']}%'";
  }
  if ( !empty( $params[ 'color' ] ) ) {
    $where[] = "color like '%{$params['color']}%'";
  }
  if ( !empty( $params[ 'category' ] ) ) {
    $where[] = "category like '%{$params['category']}%'";
  }
  if ( $where ) {
    $whereSql = implode( " AND ", $where );
    $sql = "select * from TABLE1 where " . $whereSql;
  } else {
    $sql = "select * from TABLE1";
  }
  $query .= $where;

  //SQL文を実行する
  $UserDataSet = $Mysqli->query( $sql );

  //扱いやすい形に変える
  $result = [];
  while ( $row = $UserDataSet->fetch_assoc() ) {
    $result[] = $row;
  }
  return $result;
}

 //ーーー HTML側 ーーー
<!doctype html>
<?php
include_once( 'search.php' );
$userData = getUserData( $_POST );
?>
<html>
<head>
</head>
<body>
  //キーワード検索、チェックボックス、セレクションの3タイプで検索
  <input name="search_name" value="<?php echo isset($_POST['search_name']) ? htmlspecialchars($_POST['search_name']) : '' ?>">
   <input type="checkbox" name="color" value="白" <?php echo isset($_POST['color']) && $_POST['color'] == '白' ? 'selected' : '' ?>><label for="color">白</label>//他省略
  <select name="category">
   <option value="0" <?php echo empty($_POST['category']) ? 'selected' : '' ?>>分類A</option>//他省略
   </select>
   <button type="submit" name="search">検索</button>
  
//上記項目以外のデータで結果表示
   <?php if(isset($userData)): ?>
   <?php foreach($userData as $row): ?>
     <img src="../<?php echo htmlspecialchars($row['img']); ?>">
     <?php echo htmlspecialchars($row['original_name']) ?>
   <?php endforeach; ?>
   <?php endif; ?>
</body>
</html>

改定後

//search.phpのコード
function getUserData() {

  include_once( 'database.php' );

   try {
    $db = new PDO( $dsn, $usr, $passwd );
    $db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
    $db->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );

    $q = ( string )filter_input( INPUT_GET, 'q' );
    $maxKeywords = -1;
    $keywords = preg_split( '/(?:\p{Z}|\p{Cc})++/u', $q, $maxKeywords, PREG_SPLIT_NO_EMPTY );

    if ( $keywords ) {
      foreach ( $keywords as $keyword ) {
        $holders[] = "((search_name LIKE ? ESCAPE '!') OR (color LIKE ? ESCAPE '!') OR (category LIKE ? ESCAPE '!'))";
        $values[] = $values[] = '%' . preg_replace( '/(?=[!_%])/', '!', $keyword ) . '%';
      }

      $sql = "SELECT * FROM TABLE1 WHERE (' . implode(' AND ', $holders) . ')";

      $UserDataSet = $bd->prepare( $sql );
      $UserDataSet->execute( $values );
      $userData = $UserDataSet->fetchAll( PDO::FETCH_ASSOC );
    }

    print '接続に成功しました';

  } catch ( PDOException $e ) {
    header( 'Content-Type: text/plain; charset=UTF-8', true, 500 );
    exit( '接続できませんでした' . $e->getMessage() );
  }

  function h( $str ) {
    return htmlspecialchars( $str, ENT_QUOTES, 'UTF-8' );
  }
}

 //ーーー HTML側 ーーー
<!doctype html>
<?php
include_once( 'search.php' );
$userData = getUserData( $_GET );
?>
<html>
<head>
</head>
<body>
  //キーワード検索、チェックボックス、セレクションの3タイプで検索
  <input name="search_name" value="<?php echo isset($search_name) ?= h($search_name) : '' ?>">
   <input type="checkbox" name="color" value="白" <?php echo isset($color) && $color == '白' ? 'selected' : '' ?>><label for="color">白</label>//他省略
  <select name="category">
   <option value="0" <?php echo empty($category) ? 'selected' : '' ?>>分類A</option>//他省略
   </select>
   <button type="submit" name="search">検索</button>
  
  //$holders[]に指定した列以外のデータで結果表示
   <?php if (isset($err)) : ?>
      <p><?= h($err->getMessage()); ?></p>
    <?php endif; ?>
    <?php if (0 < count($userData)): ?>
      <?php foreach ($userData as $row) : ?>
        <div class="box_warp">
          <div><img class="circular" height="80" src="../<?= h($row['img']); ?>"></div>
          <div class="break-word"><?= h($row['original_name']); ?></div>
      </div>
      <?php endforeach; ?>
    <?php endif; ?>
</body>
</html>
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • tanat

    2019/11/24 20:34

    > TaraIkuraさん

    質問は編集できるので、こちらでは無く質問に追記をお願いします。

    キャンセル

  • m.ts10806

    2019/11/25 06:46

    $usertable
    ↑これどこで定義されてるんでしょ。

    キャンセル

  • TaraIkura

    2019/11/25 10:16 編集

    ご回答有難うございます。
    returnの位置を修正し、database.phpに記載していた$usertableをそのままTABLE1と記載し直しました。

    キャンセル

回答 3

checkベストアンサー

+4

追記・修正依頼欄

早速に有難うございます。上手くいかない部分ですが、改定後の上部、データベースの読み込みはできているのですが、 ”//queryのデータ”以下が実装できなく、検索結果が1と表記されてしまいます。
アドバイスお待ちしております。

と言うのがまだよくわかりませんが、

addcslashesについてaddslashesと誤解していたので削除

そもそも論として

  //扱いやすい形に変える
  $result = [];
  while ( $row = $UserDataSet->fetch(PDO::FETCH_ASSOC)) {
    $result[] = $row;
  }
  return $result;
  $UserDataSet->execute();

で、
SQLが実行されるのは
$UserDataSet->execute();
なのに、returnより後に記述されているのでSELECT文が実行されていません。


まずはPHPマニュアルを参考にして、最低限の機能とコードでPDOの挙動について把握されることをお勧めします。


$qPart = array_fill( 0, count( $userData ), "(?, ?, ?, ?, ?)" );

$userDataが未定義なので

foreach ( $userData as $row ) { //bind the values one by one

↑foreachの中の処理が多分実行されていない

$stmt->bindValue( $i++, $row[ '%' . preg_replace( '/(?=[!_%])/', '!', $search_name ) . '%' ], PDO::PARAM_STR );

$stmtも未定義なので、foreachの処理が実行されたらエラーになるはず

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/11/27 09:03

    うーんと、もう一段階最小限の、
    SQL自体を固定文字列にして(プレースホルダを使わない)にして結果を表示させる
    というのは試されましたか?
    今のコードは置いておいて、1から書いてみるのが確実です。

    質の高い具体的なコードを参考にされること自体はとても良いのですが、まずは
    PHP デバッグ 方法
    とかで検索してみて、問題箇所を効率的に特定する方法を調べてみてください。


    今回のケースであれば、

    $userData = getUserData( $_GET );
    var_dump($userData);
    とすると、getUserDataが結果を返せていない事がわかるはずです。

    php 自作関数
    あたりで調べると、returnの役割が把握できると思います。

    キャンセル

  • 2019/12/03 18:10

    ご丁寧にご回答有難うございました。ご指摘いただいた部分、調べながら1から書き直してみています。もう少し時間がかかりそうですが、頑張ってみます。

    キャンセル

  • 2019/12/03 18:36

    フィードバックありがとうございます。
    1から書いてみると、分からない部分の把握もしやすくなるので、そこに絞って質問されるとそのものズバリな回答も得られやすいかと思います。

    キャンセル

0

あいまい検索であればどうせインデックスの効かないlike処理より
regexpの方が書き方は楽です
(ただしregexpのワイルドカードを含む場合のエスケープ処理は必要)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

とりあえず、動作確認できるだけのデータや情報がないので、気になったところだけ

<?php

function getUserData()
{

    //include_once('database.php');

    $dsn = 'mysql:host=localhost;database=database:charset=utf8';
    $user = 'root';
    $password = 'password';

    try {
        $db = new PDO($dsn, $user, $password);
        $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

        $q = filter_input(INPUT_GET, 'q');

        $maxKeywords = -1;
        $keywords = preg_split('/(?:\p{Z}|\p{Cc})++/u', $q, $maxKeywords, PREG_SPLIT_NO_EMPTY);

        if (count($keywords) > 0) {

            $values = []; // add

            foreach ($keywords as $keyword) {
                $holders[] = "((search_name LIKE ? ESCAPE '!') OR (color LIKE ? ESCAPE '!') OR (category LIKE ? ESCAPE '!'))";
                //$values[] = $values[] = '%' . preg_replace( '/(?=[!_%])/', '!', $keyword ) . '%';
                $values[] = '%' . preg_replace('/(?=[!_%])/', '!', $keyword) . '%';
            }

            //$sql = "SELECT * FROM TABLE1 WHERE (' . implode(' AND ', $holders) . ')";
            $sql = 'SELECT * FROM TABLE1 WHERE (' . implode(' AND ', $holders) . ')';

            //$UserDataSet = $bd->prepare( $sql );
            $UserDataSet = $db->prepare($sql);
            $UserDataSet->execute($values);
            //$userData = $UserDataSet->fetchAll(PDO::FETCH_ASSOC);
            return $UserDataSet->fetchAll(PDO::FETCH_ASSOC);
        }

        //print '接続に成功しました';

    } catch (PDOException $e) {
        header('Content-Type: text/plain; charset=UTF-8', true, 500);
        exit('接続できませんでした' . $e->getMessage());
    }

//    function h($str)
//    {
//        return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
//    }
}

function h($str)
{
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.99%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る