PHP For Beginnersチュートリアル　その6　基本的な検索フォームの実装

##このシリーズの目的

体系的なwebコーディングの訓練ができるようになるために、PHPの初学のきっかけかつ、PHPでログインフォームやフォームを実装することができるようになるために

[PHP For Beginners](https://www.youtube.com/playlist?list=PLgt-G5tEOlKX4LKrBCYfjcyrp7wmnRdAL)

上記のチュートリアルを進めているのでその備忘録。

[前回](https://qiita.com/shitikakei/items/4055a9d95385531b3294)

##内容

**今回のチュートリアル**

[PHP Search Box Tutorial - Filter Data in HTML Table Using PHP & MySQL Database](https://youtu.be/XVsMCtA1Jow?list=PLgt-G5tEOlKX4LKrBCYfjcyrp7wmnRdAL)

**始める前に**

ここまでのチュートリアルを踏まえると、PHPでやることとは基本的には

・データベースを作る
・データベースにアクセスする
・データベースとやり取りして、任意の処理を実行する

この3つがセットになったことであるということがわかった。
データベースを作ることに関してはMySQLの領域のような気もするが、作ったデータベースに対してデータを追加・編集・削除する作業はPHPでもできる。
アクセスは言わずもがな、そして今回のチュートリアルでは1番肝となるであろうデータベースとやり取りした結果、任意の処理を実行するという点について実例を踏まえてやっていくということになる。


**成果物**

```php:search.php
<?php 

if (isset($_POST['submit'])) {
	$connection = new mysqli ("localhost", "root" , "" , "phpSearch");
	$q = $connection -> real_escape_string($_POST['q']);
	$column = $connection -> real_escape_string($_POST['column']);
	// echo $q; //データ取得できているかのテストコード

	if ($column == "" || ($column != "firstName" && $column != "lastName"))
		$column = "firstName";


	$sql = $connection -> query("SELECT firstName FROM users WHERE $column LIKE '%$q%'");
	if($sql -> num_rows > 0) {

		while ($data = $sql -> fetch_array())
			echo $data['firstName']."<br>";

	} else

		echo "Your search query dosen`t match any data!";


}


?>

<!DOCTYPE html>
<html>
<head>
	<title>search form Tutorial</title>
</head>
<body>

<form action="search.php" method="post">
	<input type="text" name="q" placeholder="Search Query......">
	<select name="column">
		<option value="">Select Fillter</option>
		<option value="firstName">FirstName</option>
		<option value="lastName">lastName</option>}
	</select>
	<input type="submit" name="submit" value="find">
</form>

</body>
</html>
```




## 改めて ～GETを使うのか、POSTを使うのか～

ここまでのチュートリアルでも度々言及しているとは思うが、おさらい。
簡単に言えば、サーバーから情報を取得するときはGET、サーバーに登録するときはPOSTである。
それ以外にセキュリティの観点や送信できるデータ量等々考慮すべき要因は色々あるなか、こと検索フォームは通常GETが使われるらしい。
それなのに今回はなんでPOSTをなの？ と疑問に思い、調べてると下記のページによく纏まっていたのでそれを読むとなるほどと腑に落ちた。

[【PHP超入門】HTTP（GET・POST）について](https://qiita.com/7968/items/4bf4d6f28284146c288f)

>GETメソッドの利点は、クエリストリングを付与することで一つのURLとして扱われることです。
PHPというワードで検索した結果のページを別の方に伝えたいときは、http://qiita.com/search?q=PHP のURLを送れば、相手側にもPHPで検索した結果のページが表示されます。
これがGETの利点であり、特徴の一つです。

要は、他者と検索結果が共有できるのとブックマークができるということが肝なのだ。
GETメソッドでクエリストリングを付与されたページはブックマークするとそこから読み込んだ時に同じ処理がなされるが、POSTの場合はそれが無い。
つまり、それらが必要なのか必要じゃないのかでどちらを使うか判断できるということ。
キーと値の問題は？ という話はそもそもバレたら困るようなキーと値のやり取りはGETで行わないのが大前提だとして、それでも納得が行かないなら同記事のq=PHPの部分を見てほしい。
まとめると、

・サーバーにデータを送信する必要があるか？ それとも取得してくるだけでいいのか？
・ブックマークや検索結果の共有を許可するのかしないのか

この2点でまずは考えてみると理解しやすいのではないだろうか。
ちなみに今回はコードを見ればわかるがフォームから送信した値がデータベースに存在するか検索するということなのでPOSTなのである。
同じコードでGETの場合はフォームに入力した値がデータベースに存在する場合、それを取得してくるという処理になるのだろう。
字面だと同じことを言っているようにしか思えないし、実際GETでもやれてしまうのだから罪深いメソッドである。

## 今回のコードの注釈


```php:mysqliについて

/*以前に出てきたPDOと同じくデータベースにアクセスするための関数であり、*/

$connection = mysqli_connect("Host or IP", "User", "Pass", "DBName");
$connection = new mysqli ("Host or IP", "User" , "" , "DBName");

/*という二種類の書き方がある。
前者は手続き型といい、インスタンスを生成しない代わりにクラス名を付随して処理を決める書き方で後者はオブジェクト型といい、その名の通り最初にmysqliクラスをオブジェクト化してインスタンス生成を行うことで、処理自体はメソッドで呼び出すことができるという書き方である。
今回用いられているのは後者である。*/
?>
```
```php:今回使用したmysqliクラス（メソッド）について

$q = $connection -> real_escape_string($_POST['column']);

/*real_escape_stringはSQL文で使用する文字列の特殊文字をエスケープするための関数である。今回は引数に指定してあるPOSTの値に対して、適用される。
エスケープに関しては参考記事を読んでほしいが、特にSQL文で後述するLIKE演算子を用いて検索処理を行う場合は必ず特殊文字をエスケープしないと、正しい検索結果が保証されないので必ず行う必要がある。
調べると方法は色々あり。この辺りは浅い領域ではないので、今回はこのメソッドを脳死で使うが私のような初学者及び不安のある方は改めて参考記事等々で勉強することをおすすめする。*/

$sql -> num_rows

/*num_rowsはクエリの結果セットの行数を調べる。
例えば検索した結果2件該当したならば、結果セットの行数は2行となる。*/

$data　= $sql　→　fetch_array()

/*fetchはデータを取得するメソッドであるが、そのうちの一つであり、結果の行を連想配列・数値添字配列あるいはその両方の形式で取得し、フィールド名をキーとする連想配列にもデータを格納することができるというメソッド。
簡単に言うと、実行されたクエリで取得した結果の行を変数に連想配列として格納するということである。


つまり、今回の*/

if($sql -> num_rows > 0) {

        while ($data = $sql -> fetch_array())
            echo $data['firstName']."<br>";

    } else

        echo "Your search query dosen`t match any data!";


}

/*という文はまず、num_rowsで結果の行を取得し、それが0以下ならばメッセージを、
0より大きければ、whileで$dataに得られた分だけ結果の行を格納し続けて、さらにそれをfirstNameをキーとしてその値を改行しながら書き出し続けるという処理になる。

＊列と行について

| id               |    firstName      |      lastName      |
|:-----------------|------------------:|:------------------:|
| 1                |      Tanaka       |        taro        |
| 2                |      Yamada       |       kotaro       |
| 3                |      Takeda       |       misato       |


このようなテーブルがあったとして、列は縦軸、行は横軸を指す。
つまり、例えばid=1である行を取得するという場合、取得するデータは以下の通りである。

キー| id               |    firstName      |      lastName      |
　　|:-----------------|------------------:|:------------------:|
値　| 1                |      Tanaka       |        taro        |
*/

```

```php:サルでもわかる、LIKE演算子について

/* LIKE演算子とはMySQLで特定のキーワードを含むデータだけを抽出する方法である。
今回は%を用いるワイルドカードを使ったやり方で、%の位置で検索方法が変わる。

・末尾一致検索→'%文字列'
・前方一致検索→'文字列%'
・部分一致検索→'%文字列%'
・aから始まりbで終わる任意の文字数の文字列→'a%b'

と以上のような4パターンがある。
実際に使う際には*/

$sql = $connection -> query("SELECT firstName FROM users WHERE $column LIKE '%$q%' ");

/*といった形で使用され、データベースのどのテーブル（FROM）から何を取得し（SELECT）、それをどういう検索条件（WHERE)で、LIKE演算子でどういう文字列を抽出して検索するか指定する必要がある。
今回はphpsearchというデータベースの中のusersというテーブルの中にあるデータのfirstNameという値を検索するのに$columnに代入された変数によって検索条件を設定し（今回はユーザーにfirstNameで検索するか、lastNameで検索するか選択させ、どちらも指定してない場合はfirstNameで自動的に検索するという処理）$qに代入された値を部分一致検索で検索するという処理になる。
つまり、$q = "taro" かつ $column = lastNameだった場合、lastNameの値にtaroが含まれているfirstNameを取得するということになるので例えばデータベースが以下の通りならば

| id               |    firstName      |      lastName      |
|:-----------------|------------------:|:------------------:|
| 1                |      Tanaka       |        taro        |
| 2                |      Yamada       |       kotaro       |
| 3                |      Takeda       |       misato       |

抽出されるのはTanaka及びYamadaということになる。*/


```

##余談

今回は検索フォームの実装についてあれこれやってきたわけだが、サイト内検索や全文検索ってどう実装するの？ と思ったのは私だけではないはず。
実は実装するだけならGoogleが[カスタム検索エンジン](https://cse.google.com/cse/?hl=ja)というものを提供しているのでそれを用いればいい。
これを自力で実装しようとなるとこれが大変で形態素解析で文字列を検索するコードを書いたりするなど、なかなか骨が折れる。
なので、今回は私達初学者にとっての宿題として、以下のサイトにJavascriptでサイト内の記事検索を行える検索フォームを実装する手段が解説されているのでそれを紹介する。

[JavaScriptでサイト内検索ボックスの作り方](https://yukimonkey.com/js-application/search-engine/)


## ひとこと

私のように基本情報技術者試験程度の座学的な知識やPHPを教本からやらず、とりあえずチュートリアルから初めてみた、習うより慣れろだろといって学習を進めてる人にとってはいよいよ、自分に上手く落とし込むまでに時間がかかるようになってきた。
ただ、本番実装ならともかく学習の段階としてはそれでも今回私がこの記事で書いてある程度の理解ができていればまず先に進めてみるべきである。
チュートリアルの一周目は種撒きをしているようなものだと、私は進めている中感じている。
経験と最低限の知識があれば、後で基礎固めや深度を深める学習をする上でより効率に進むと思う。
理解が難しくても焦らず、先に進もう。

下記の2サイトは教本等ない方は一読することをおすすめする。
[[PHP] mysqli使い方まとめ（MySQL接続～SELECT実行まで）](https://qiita.com/yasumodev/items/bd2ba476f31804d527d3)
[PHPでデータベースに接続するときのまとめ]
(https://qiita.com/mpyw/items/b00b72c5c95aac573b71)


## 参考

[[PHP] mysqli使い方まとめ（MySQL接続～SELECT実行まで）](https://qiita.com/yasumodev/items/bd2ba476f31804d527d3)
[PHPでデータベースに接続するときのまとめ]
(https://qiita.com/mpyw/items/b00b72c5c95aac573b71)
[real_escape_string](https://www.php.net/manual/ja/mysqli.real-escape-string.php)
[myspliクラス]
(https://www.php.net/manual/ja/class.mysqli.php)
[お洒落にJavaScript。-JavaScriptでサイト内検索ボックスの作り方](https://yukimonkey.com/js-application/search-engine/)
[mysqli_num_rows]
(https://www.php.net/manual/ja/mysqli-result.num-rows.php)
[fetch_array](https://www.php.net/manual/ja/mysqli-result.fetch-array.php)