【完全自作】phpで検索ボックスを作ろう!SQL文と仕組みを詳しく解説!

この記事からわかること

  • phpで作る検索ボックスの作り方と仕組み
  • 検索ボックスに必要な機能
  • データベースの接続の仕方とSQL文の使い方
  • SQL文のLIKE句の挙動
  • サイドバーに設置して使い回しできる検索ボックス

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

Webサイトを運営する上で欠かせない機能の1つが「検索ボックス」です。

ここでいう検索ボックスとはこのようなwebサイト内の記事の中で指定されたキーワードに該当する記事を探し出し一覧で表示する機能のことです。

検索ボックスを作るのに必要な言語はHTMLとphp、そして少しのSQLです。

それでは私が自作した「検索ボックス」の作り方を解説していきます。

前準備

このサイトのサイドバーにもありますが↓こんな感じの検索ボックスを作っていきます。

実際にphpで作成した検索ボックス

環境

作成するファイル

今回作る検索ボックスの特徴

phpで作る検索ボックスの仕組みと構造

まずはどのような流れで作っていくのか整理します。

  1. データベースに記事の情報を格納する
  2. ページ内に検索ボックスをHTMLで作成する
  3. 入力されたキーワードが適切かどうか判断+エスケープ処理
  4. データベースに接続しSQL文で該当の記事を取得
  5. 取得した記事を一覧にして表示する

キーポイントになってくるのは適切なエスケープ処理とデータベースに接続する部分です。

記事情報をデータベースに登録しよう

まずは1番最初のデータベースに登録する作業からです。登録する内容は人それぞれですが最低限タイトルと説明文、記事のパス(URL)を格納しておきます。

登録内容

DBの項目名 記事
id(主キー) A_I 一意となるキー値 int
title タイトル text
desc 説明文 text
file ファイルのパス(URL) text

登録するためにまずはphpMyAdminにログインしてテーブルを作成します。テーブル名は「post_table」としておきました。作成方法は左側の「新規作成」をクリックしテーブル名を入力して「作成」を押すだけです。

phpMyAdminのデータベース画面

テーブルの作成が完了したらphpMyAdminの「挿入」から自由にデータを格納できますので記事の各情報を数記事入れ込んでおきます。

phpMyAdminのデータベース画面

これで下準備が終了しました。あとは記事が増えるごとにデータベースの更新をしていくだけで検索対象がどんどん増えていくので追加だけ忘れないように注意してください。

HTMLで枠組みを作る

続いてはHTMLで見た目と構造を作っていきます。

気を付けることはinput要素をformタグで囲うこととPOSTの送信先です。


<sidebar class="sidebar" id="sidebar">
  <div class="searchbox">
    <form  method="post" action="searchbox.php">
      <input type="text"  name="word" value="" placeholder="word..." required>
      <input type="submit" name="submit" value="検索" id="search" >
      <label for="search"><i  class="fas fa-search"></i> </label>
    </form>
  </div>
</sidebar>

form要素での送信先は「search.php」にしておきます。こうすることで入力されたキーワードを「search.php」で受け取り処理することができます。

検索ボタンを押せば、action属性に指定したファイル(パス)に自動で画面が切り替わるのでa要素やリダイレクト処理は必要ありません。

input要素name属性type属性はそれぞれ適したものにしておきます。さらに「required」をつけることでこの項目を入力必須項目にすることができます。これで未入力での検索は行えなくなりました。

ここはデザイン上のこだわりですが検索ボタンの見た目が好みではないのでFont Awesomeの「」アイコンをボタンがわりにしたいと思います。

そのために[type="submit"]のinput要素に「id」を振り、labelと「for」で組み合わせることで2つをリンクさせておきます。これで[type="submit"]のinput要素を非表示にすることで、アイコンをクリックすると検索ボタンが押されたことになります

デザインのcssも一応貼っておきます!よかったら真似してみてください〜!


#sidebar .searchbox {
    position: relative;
    margin: 30px auto;
}

#sidebar .searchbox input[type="text"] {
    width: 90%;
    padding: 15px;
    font-size: 2.0rem;
    border: transparent;
    background-color: #e7e7e7;
    border-radius: 5px;
    border: solid 3px transparent;
}

#sidebar .searchbox input[type="text"]:focus {
    outline: none;
    border: solid 3px #23527c93;
}

#sidebar .searchbox input[type="submit"] {
    display: none;
}

#sidebar .searchbox i::before {
    font-size: 3.0rem;
    background-color: #fff;
    background-color: #e7e7e7;
    cursor: pointer;
    padding: 10px;
    border-radius: 50%;
    position: absolute;
    top: 23px;
    right: 30px;
    transition: all 0.4s;
}

#sidebar .searchbox label:hover i::before {
    transform: rotate(-360deg);
    transition: all 0.4s;
}

入力値の確認とエスケープ処理

ここからは「searchbox.php」に記入していきます。

最初にやらなければいけないことはエスケープ処理です。エスケープ処理とは予期せぬ値を入力された時や意図しない行動をされた時にエラーや不具合を起こさないように事前に対策を打っておくことです。

やるべきエスケープ処理

  1. searchbox.phpへの直接アクセスされた場合のリダイレクト
  2. 「 」(半角空白)や「 」(全角空白)入力された時の処理
  3. 検索ボックスに悪意あるコードを埋め込まれた時の処理

まずは「searchbox.php」へ直接アクセスされた時にトップページへのリダイレクトが必要になります。直接アクセスしてきた場合は$_POST['word']中身が空の状態なのでその後のDB接続の際におかしな挙動を引き起こしてしまいます。

正しい変移で「searchbox.php」へアクセスされた($_POST['word']の中身に入力値が格納されている)場合は、htmlspecialchars関数などを使いエスケープ処理をしていきます。


<?php
// 直接アクセスされたらリダイレクト
if(!isset($_POST['word'])){
  header('Location:https://'.$_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF']).'/');
  exit();
}

// $_POST['word']で入力値を取得 文字前後の空白除去&エスケープ処理
$word = trim(htmlspecialchars($_POST['word'],ENT_QUOTES));
// 文字列の中の「 」(全角空白)を「」(何もなし)に変換
$word = str_replace(" ","",$word);
// 対象文字列が何もなかったらキーワード指定なしとする
if($word === ""){
  $word = "キーワード指定なし";
}
?>

header関数→"Location:絶対パス"とすることでリダイレクトさせる

htmlspecialchars関数→HTMLで特殊文字とされる「<」や「"」を文字列として変換

tirm関数→対象文字の前後の空白を除去

str_replace関数→対象文字列から指定文字1を指定文字2に変換

$_SERVER['HTTP_HOST']dirname($_SERVER['PHP_SELF'])を使って動的に絶対パスを取得しています。これでURLが変更になってもコードを変更する必要がなくなります

詳しくはこちら⇨【PHP】ファイル名や絶対パスの取得方法!パスは汎用性の高いコードを記述しよう

これである程度のエスケープ処理は完了です。セキュリティ対策はしっかりしておかないと痛い目をみますので注意してください。

try〜catch〜finally命令でエラーが起きても大丈夫

データベースに接続するというコードは何かとエラーが発生します。そのようなコードを記述する際はあらかじめエラーが起きても大丈夫なように「try〜catch〜finally命令」を書いておきます。

これによりエラーが起きても全体が停止することを防ぐことができます。

続きは先ほどのコードの下に記述していきます。


// 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
try{
  // 例外が発生する可能性のあるコード
  // ここにこれからのコードを記述していく
}catch(PDOException $e){
  // 例外発生時の処理
  // 例外のメッセージを格納しておく
  $error = "接続エラー : {$e->getMessage()}";
} 
finally{ 
  // 例外の有無に関わらす実行されるコード 
  // データベース接続を終了するコードは例外の有無に関わらず実行する
  $dbh = NULL;
}
?>

データベースに接続する

これで半分くらいの作業が終了しました。次は「データベースに接続」する作業をしていきます。

データベースの接続は基本的にいつも同じなのでコピペしたり、別ファイルにまとめておくと便利です。今回はPDOを使ってデータベースの接続をしていきます。ユーザーやパス、データベース名は適切なものに変更してください。

先程のtry〜の中に記述していきます。


try{
  $dsn = 'mysql:dbname=データベース名;host=localhost;charset=utf8';
  $user = 'ユーザーID';
  $password ='password';

  // PDO(PHP Data Objects)=異なるデータベースでも同じ命令で操作できるようにする
  $dbh = new PDO($dsn,$user,$password); 

  // エラーの通知方法設定 例外を発生に
  $dbh->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
  $dbh->query('SET NAMES utf8');//文字化け用
  // プリペアドステートメントとしてSQL文を送信
  $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);//セキュリティ対策

SQL文を組み立てる

次はSQL文を組み立てていきます。組み立てたいSQL文は以下の通りです。

「post_tableの中でタイトルか説明文にキーワードが含まれる記事の情報を全て取得」

これをSQL文に組み替えるとこのようになります。

'SELECT * FROM post_table WHERE title LIKE :word OR descri LIKE :word2'
// 選択/全て/post_tableから/条件は/titleの中で:wordに該当する/または/descriの中で:word2に該当する

phpでSQL文を組み立てるときは入力値をそのままSQL文に埋め込まないように注意が必要です。そのまま埋め込むとSQLインジェクションなどの攻撃の危険があるので適切なエスケープ処理を心がけましょう!

関連記事:【絶対必須】phpのセキュリティ対策!XSSとメールヘッダーインジェクションを防ごう

PDOのプレイスホルダ(入力値を場所だけ確保し後から値を入れ込む)を使用することで意識せずともエスケープ処理を施してくれます。SQL文にプレイスホルダを組み込むには「:名称」か「?」のどちらかで場所を確保します。

今回は「:word」と「:word2」としました。

次にここに入力値を入れ込まないといけません。プレイスホルダに値を入れ込むのはbindValueメソッドの役割です。

bindValueメソッド

$stmt->bindValue($param,$value,$type=PDO::PARAM_STR);
// bindValue(プレイスホルダの値,格納したい値,データ型);
// 「:名称」の場合はそのまま「:名称」と指定
// 「?」の場合は?が何番目にあるかを数字「1」や「2」などで指定する

これで値を格納することができます。しかしその値を格納するのには注意が必要です。

今回作成しているのは「検索ボックス」です。検索ボックスはキーワードが含まれる記事を探し出してくるのが役割です。プレイスホルダに格納する値にそのまま「$word」を入れてはいけません

そのまま入れてしまうとタイトルか説明文がキーワードに完全一致するものしか抽出できません。(例えばキーワードが「php」ならタイトルか説明文が「php」であるデータのみ)

今回はキーワードがタイトルか説明文のどこかに入っている記事を探すようにしたいのです。そこで重要となってくるのがLIKE句とよく組み合わせて使うワイルドカードです。

LIKE句とワイルドカードの使い方

SQL文の「LIKE句」の使い方を整理しておきます。

そもそもLIKE句はSQL文の1つで文字列の検索を行うことができるSQLの命令です。 コードは「LIKE '文字列'」とすることで指定したテーブルの指定した項目から検索文字列にヒットしたデータを抽出してくれます。

そんなSQLのLIKE句と切っても切り離せないのが「ワイルドカード」です。ワイルドカードとは文字検索を行う時に使える特殊な文字のことです。

ワイルドカード文字 意味
% 0文字以上の任意の文字
_ 1文字の任意の文字

今回のプログラムでもワイルドカードが必須となってきます。ワイルドカードをつけるのはSQL文の中ではなくプレイスホルダに値を格納する時に使います。

// 変数をそのまま引数にする時
bindValue(':word',$word,PDO::PARAM_STR);
// 変数+ワイルドカードで引数にする時
bindValue(':word',"%{$word}%",PDO::PARAM_STR);

今回はキーワードがどこにあっても抽出したいので前後に「0文字以上の任意の文字」の「%」をつけます。これでタイトルの最初にあろうが、最後にあろうが、中間にあろうが検索に引っ掛かるという仕組みです。

それではプログラムのコードを書いていきます。


// tryの中〜
  $sql = 'SELECT * FROM post_table WHERE title LIKE :word OR descri LIKE :word2' ;
  // SQL文をセット
  $stmt = $dbh->prepare($sql);
  // bindValueでプレイスホルダーに値(ワイルドカードで挟む)を入れ込む
  $stmt->bindValue(':word',"%{$word}%",PDO::PARAM_STR);
  $stmt->bindValue(':word2',"%{$word}%",PDO::PARAM_STR);
  // 実行処理
  $stmt->execute();
  // 記事があるかないかの指標用
  $judge = false;

ヒットした記事をHTMLの要素として表示させる

ここまできたらあとは抽出した記事の情報をもとに記事一覧としてHTMLに組み直し表示するだけです。

ヒットした記事の情報を1行ずつなくなるまで(falseを返すまで)取り出し,$result['名称']で各項目の情報を取得できます。

記事ごとに「li」を作り、最終的に$resultListに全て格納します。これであとは好きなところに「echo $resultList;」とすれば記事一覧もしくは「該当の記事がありませんでした」のメッセージが表示されます。

以下のコードではただこのような簡素な記事のリストを生成するだけです。

データベースに画像のパスなどを組み込んだり、日付やカテゴリ名を組み込むことでより記事一覧らしいものが出来上がると思います。より見やすいオリジナルの記事一覧を作ってみてください!


//〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
      $judge = false;
    // ここから
    // trueの間繰り返す($result=記事の情報を1行ずつ取り出す)
    while($result = $stmt->fetch(PDO::FETCH_ASSOC)){
      $judge = true;  // 記事がある時の
      $resultItem = $resultItem .'<li><a href="'.$result['file'].'"><div><p>'.$result['title'].':'.$result['descri'].'</p></div></a></li>';
    }
    if($judge === false){  // 記事があったかないか
      $resultItem  = '<p>該当の記事がありませんでした</p>';
      // ない場合はメッセージを格納する
    }

    $resultList = '<h2><i class="fas fa-search"></i> キーワード:'.$word.'</h2><ul>'.$resultItem."</ul>";
    // ここまで
}catch(PDOException $e){
// 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜

検索ボックス作成のまとめ

最後にsidebar.phpをフロントページなど好きなページ「include_once」で呼び出すことで簡単に検索ボックスを設置することができます。


<?php include_once 'sidebar.php';?>

ご覧いただき本当にありがとうございます。まだまだ勉強中の身ですので至らぬ点や不備などがございましたら気軽に教えていただけると嬉しいです。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index