PHP For Beginnersチュートリアル　その16　データベースのデータをCSVファイルで取り出す

##このシリーズの目的

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

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

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

[前回](https://qiita.com/shitikakei/items/6817df6942004338bb8d)

##内容

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

[PHP Tutorial: Export MySQL Data To CSV File](https://www.youtube.com/watch?v=G_1Cifxuvxc&list=PLgt-G5tEOlKX4LKrBCYfjcyrp7wmnRdAL&index=19)

**このチュートリアルでやること**

・データベースのデータをCSV形式で出力してダウンロードできるようにする。

**成果物**

```php:index.php

<?php
	$conn = new mysqli("localhost","root","","register");

if (isset($_POST['start'])) {
	$start = $conn->real_escape_string($_POST['start']);
	$allData = "";
	$sql =  $conn->query("SELECT * FROM users LIMIT $start,50");
	while($data = $sql->fetch_assoc())
		$allData .= $data['id'] . ',' . $data['name'] . ',' . $data['email'] .','. $data['password'] 
					. ',' . $data['isEmailConfirmed'] . ',' . $data['token'] .','. $data['tokenExpire'] . "\n";

	exit (json_encode(array("data" => $allData)));

	//ここまででデータの取得はできている。
}

$sql =  $conn->query("SELECT id FROM users");
$numRows = $sql->num_rows;

 ?>

 <!DOCTYPE html>
 <html>
 <head>
 	<title>Export MySQL Data To CSV</title>
 	<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
 	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js">
    </script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js" type="text/javascript">
    </script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js">
    </script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js">
    </script>
 </head>
 <body>
<p id="response">Please Wait......</p>
 <script type="text/javascript">
 	let data =  "data:text/csv;charset=utf-8,ID,NAME,EMAIL,PASSWORD,isEmailConfirmed,TOKEN,TOKEN EXPIRE\n";
 	//PHPで取得したデータをどのように出力するか定義している。
 	//data:データ形式（今回はテキスト）/ファイル形式（拡張子、今回はcsv形式）;文字コード（今回はutf-8）、以下テーブルに登録されている親カラムの項目名
 	$(document).ready(function() {
 		exportToCSV(0,<?php echo $numRows  ?>)
 		//ここでstartの値を定義する、今回は0。Maxに相当するのはPHP領域でのnum_rowsの結果

 	});

 	function exportToCSV(start,max) {
 		if (start > max) {
 			$("#response").html('<a href="'+data+'" download="users.csv">Download here!</a>');
 			return;
 		}

 		//通常startは最小値、maxは最大値を取得する、例えば10個のデータを取得できるとき、startは0、maxは10となる。

 		$.ajax({
 			url:"index.php",
 			method:"POST",
 			dataType:"json",
 			data: {
 				start:start
 			}})

 			.then(
 				function (response) {
 				data += response.data;
 				exportToCSV((start+50), max);
 			},
 				function(){
 					alert("ERROR");
		});
 		//Ajaxを使ってPHPにデータを送信する。今回はスタートのプロパティをPOSTメソッドで送っている。
 		//成功した場合、response関数を定義して、グローバル変数dataにPHPから返ってきた値を接続し、exportToCSV関数を呼び出してcsv出力する。
 	}

 </script>
 </body>
 </html>
```

## 大枠のアルゴリズムを考える

*解説は後に記載。

1.

 データベースにアクセスし、id（テーブルに登録されているデータの数）を取得、num_rowsでクエリに返ってきた件数を取得し変数に代入。

2.

exportToCSVを起動。開始点を0、終点を1で取得したnum_rowsの値を引数に設定する。

3.

 exportToCSVは開始点の値が終点を上回っていたら任意の処理とともに処理を止める、それ以外はAjaxでPHPにデータを送信する関数である。当然、2の時点での処理は後者であるのだからここでAjax通信が始まる。
 Ajaxは通信を行うPHP、通信方式、通信するデータの形式、PHPに送る値を設定して、PHPからデータが返ってきたかどうかでまた処理判定があるものである。
まず、この段階では開始点であるstartの値がPHPに送信される。

4.

 3の時点でのstartは0なので0が送られる。あとはそれをエスケープして変数に代入し、query文のLIMITでデータをどれだけ取得するかの開始点の値にする。

5.

 LIMIT 0,50により1番めのデータから数えて50件までのデータがquery文で返され、$sqlに格納される。
 さらにそれを連想配列で取得し、$dataに格納する。while文なので$data = $sql->fetch_assoc()が真である限り続く。当然、上限の50件あるいはそれ以下の取得できたデータの件数の分だけそれが続くということになる。

6.

 格納された連想配列から各カラムの値を書き出し、変数に代入していく。この時この変数は事前にからの文字列を代入しておく。

7.
 6で代入したデータをjson形式でエンコードする。そして、これをAjaxに返す。

8.

 Ajaxの成功判定、ここまでの処理がうまく行っていれば、var dataに返ってきたPHPのデータを接続してexportToCSV((start+50), max);を起動する。+50の部分は取得するデータの上限数だということはここまでくればわかるはず。

9.

 exportToCSVのifの部分を実行。書かれている処理を実行し、returnで処理から抜ける。



## 今回のコードの注釈

```php:LIMITについて

$sql =  $conn->query("SELECT * FROM users LIMIT $start,50");


```

SQL文においてLIMITを用いると、取得するデータの件数に上限をつけることができる。
第1引数に取得開始位置、第2引数に上限を指定する。
例えば今回の場合、上述の通り$startにはAjaxで送られてきた0という値がエスケープされて代入されているので、開始位置は1番目のデータからということになり、上限の数は50なので1番目のデータから50件までのデータを取得し、クエリを返し変数に代入するということになる。


```php:json_encode

exit (json_encode(array("data" => $allData)));


```
json_encode自体はこれまで度々出てきたので処理自体は割愛。
ここでこれを使う理由はAjaxの部分でdataTypeにjsonを指定しているからである。
これはPHPから返ってくるレスポンスの形式にjsonを指定するという意味である。
なので、当然PHP側で取得したデータをjson形式にして返さなければいけないということでdataをキー、取得したデータを値とした連想配列として格納したものをjsonにエンコードしている。Ajaxについては後述する。

```javascript:Ajaxについて(jQuery)

<script>

let data =  "data:text/csv;charset=utf-8,ID,NAME,EMAIL,PASSWORD,isEmailConfirmed,TOKEN,TOKEN EXPIRE\n";
//data:データ形式（今回はテキスト）/ファイル形式（拡張子、今回はcsv形式）;文字コード（今回はutf-8）、以下テーブルに登録されている親カラムの項目名の順。
 	$(document).ready(function() {
 		exportToCSV(0,<?php echo $numRows  ?>)
 		//ページを読み込んですぐ引数を設定してexportToCSVを実行する。引数には下記を見ればわかるように開始点と終点を設定する。startに0、Maxに相当するのはPHP領域でのnum_rowsの結果を設定しておく

function exportToCSV(start,max) {
 		if (start > max) {
 			$("#response").html('<a href="'+data+'" download="users.csv">Download here!</a>');
// 1
 			return;
 		}

$.ajax({
 			url:"index.php",// Ajaxリクエストを送信する宛先。URLやPHPなどを指定する
 			method:"POST",// 通信方法の指定
 			dataType:"json", // サーバーから返ってくるデータの形式を指定する
 			data: { // サーバーへ送信するデータ
 				start:start
 			}})

 			.then( // Ajax通信が成功し、サーバーからデータが返ってきたときの処理
 				function (response) {
 				data += response.data;// var dataにPHPから返ってきたデータを接続する
 				exportToCSV((start+50), max);
 			},
 				function(XMLHttpRequest, textStatus, errorThrown) { // Ajax通信が失敗した際の処理。エラーメッセージを出す
    alert('error!!!');
　　console.log("XMLHttpRequest:" + XMLHttpRequest.status);
　　console.log("textStatus:" + textStatus);
　　console.log("errorThrown:" + errorThrown.message);

  });
}

</script>
```

 Ajaxについては[この記事](https://qiita.com/hisamura333/items/e3ea6ae549eb09b7efb9)でわかりやすく解説されているので必読。
 画面遷移せずにサーバーと通信するために、通信するデータを絞ってやり取りしていると大まかなイメージは持っておこう。
 私は1について+data+ってどういうことだ？ と沼にハマりteratailで質問してしまったが単にJavaScript上においてaタグやそれに付随する属性設定などのコードは「ただの文字列」に過ぎないので、文字列と変数dataと文字列を結合演算子である+で接続しているという書き方になっているというだけのことである。賢明な皆さんはこんなことでは躓かないと思うが、一応以下に私がteratailで質問したものを記載しておく。
有志の方々が優しく解説してくださったのでよければ一読を。

[AjaxとaタグのDownload属性を用いてMySQLのテーブルのデータをCSV形式で落とす際に、href属性の値であるJSの変数をなぜ+で囲むのかがわからない。
](https://teratail.com/questions/211621)


## 参考

[初心者目線でAjaxの説明](https://qiita.com/hisamura333/items/e3ea6ae549eb09b7efb9)

[Google OAuth 2.0 認証を使ったログインの実装](https://qiita.com/kite_999/items/bddd62c395f260e745bc)