PHP CSVダウンロードの実装をしよう。 ※MySQLi、オブジェクト指向も学べます！

# はじめに
・PHPで、CSVでダウンロードを実装したい方
・MySQLiを使用して、DB操作を実装したい方。
・オブジェクト指向でコードを実装した方

## CSVダウンロードの実装をしよう

CSVとは、**`Comma Separated Value`**の略称で、直訳するとカンマで区切った値という意味になります。なお、PHPでCSVを実装するには、**`header関数`**を使用します。

######基本形（サンプルコード）
```php:csv.php
<?php 

// 出力情報の設定
header("Content-Dispositionntent-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=GRAYCODE.csv");
header("Content-Transfer-Encoding: binary");

// 変数の初期化
$member = array();
$csv = null;

// 出力したいデータのサンプル
$member = array(
	array(
		'id' => 1,
		'name' => '山田太郎',
		'furigana' => 'やまだたろう',
		'email' => 'taroyamada@sample.com'
	),
	array(
		'id' => 3,
		'name' => '加藤明美',
		'furigana' => 'かとうあけみ',
		'email' => 'akemikato@sample.com'
	),
	array(
		'id' => 5,
		'name' => '佐藤健夫',
		'furigana' => 'さとうたけお',
		'email' => 'takeosato@sample.com'
	)
);

// 1行目のラベルを作成
$csv = '"ID","氏名","ふりがな","メールアドレス"' . "\n";


// 出力データを生成
foreach( $member as $value ) {
	$csv .= '"' . $value['id'] . '","' . $value['name'] . '", "' . $value['furigana'] . '","' . $value['email'] . '"' . "\n";
}

// CSVファイル出力
echo $csv;
return;
 ?>
```

```html:csv_index.php
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<title></title>
	<link rel="stylesheet" href="">
</head>
<body>
	<h1>CSV Download</h1>
	<form method="get" action="./csv.php">
    <input type="submit" name="btn_download" value="ダウンロード">
</form>
</body>
</html>
```

#### コード抜き出し解説①

```php:コード抜き出し①
header("Content-Dispositionntent-Type: application/octet-stream");
```
**`header関数`**で、HTTPヘッダの情報を送信します。**`ContentDispositionntentType`**では、クライアントに返されたコンテンツがどのようなものかを伝え、**`application/octet-stream`**はファイルの種類が分からないときなどに使用する、MIMEタイプを指定しています。

#### コード抜き出し解説②
```php:コード抜き出②
header("Content-Disposition: attachment; filename=GRAYCODE.csv");
```
**`Content-Disposition: attachment`**で、ダウンロードファイルとして表示させ、**`filename=GRAYCODE.csv`**でファイル名を指定しています。
※今回のファイル名は、**`GRAYCODE.csv`**になります。

#### コード抜き出し解説③
```php:コード抜き出③
header("Content-TransferEncoding:binary");
```
**`Content-Transfer-Encoding:`**で、エンコーディング形式を伝え、**`binary`**でバイナリーデータを指定します。

## MySQLiを使ってDB操作を実装しよう
MySQLiとは、**`MySQL improved extension`**の略称です。MySQLと、MariaDBを操作するためのPHPの拡張機能で、オブジェクト指向で記述が可能です。

#### MySQLiのインスタンスを作成

```php:MySQLiのインスタンスを作成
$mysqli = new mysqli('host_name', 'user_name', 'password','database_name'); 
```
インスタスの引数の説明

|引数名|詳細|
|:---:|:---:|
|host_name|ホスト名（localhost、127.0.0.1など）|
|user_name|使用するDBのユーザー名|
|password|DBにログイン際のパスワード|
|database_name|使用するDBの名前|
※引数は、各自の環境に合わして変更してください。

#### MySQLiでSQLクエリの実行
```php:
$mysqli->query(`SELECT * FROM test_table`);
```
今回の場合**`SELECT * FROM test_table`**で、**`test_table`**のレコードを全て取得するSQL文を実行しています。
※他のSQL文でも可能。

#### DBに接続（オブジェクト指向での書き方）
```php:
// データベースへ接続
$mysqli = new mysqli('host_name', 'user_name', 'password', 'database_name');

// 接続エラーの確認
if ($mysqli->connect_errno) {
	echo $mysqli->connect_errno .':' .$mysqli->connect_error;
}

// 接続解除
$mysqli->close();
```
MySQLiのインスタンスを作成でDBに接続し、MySQLiプロパティを実装することでエラー処理などを記述。
※**empty関数**を使用し、**`if (!empty ($mysqli->connect_errno))`**でも可能。

|プロパティ|詳細|
|:---:|:---:|
|connect_errno|接続エラーの番号を返す|
|connect_error|接続エラーのメッセージを返す|

## オブジェクト指向で、実装しよう

##### 全体のサンプルコード

```php:db.php
<?php

// DB操作に必要な変数をゲッター、セッターで実装したクラス
class DbData {
	private static $sql_result = null;
	private static $message_array = array();

	public static function getSqlResult() {
		return self::$sql_result;
	}
	public static function getMessageArray() {
		return self::$message_array;
	}
	public static function setSqlResult($sql_result) {
		self::$sql_result = $sql_result;
	}
	public static function setMessageArray($message_array) {
		self::$message_array = $message_array;
	}
}

// 条件分岐で必要なSELECT文を実装するクラス
class DbSlect {
	private static $sql_select = null;
	private static $limt = "LIMIT";

	public static function getSqlSelect() {
		if (self::$sql_select == null) {
			self::$limt = null;
		}
		return "SELECT * FROM message ORDER BY post_date ASC"." ".self::$limt." ".self::$sql_select;
	}
	public static function setSqlSelect($sql_select) {
		if (!is_null($sql_select) && !is_numeric($sql_select)){
			throw new Exception('Error: Please enter a number or a string');
		}
		self::$sql_select = $sql_select;
	}
}
 ?>
```

```php:csv.php
<?php 
// csvの操作を実装するクラス
class csvDownlod {
	private static $csv = null;

	public static function csvOpretion() {
		self::$csv = '"ID","表示名","メッセージ","投稿日時"'."\n";
		foreach (DbData::getMessageArray() as $value) {
			self::$csv .= '"' . $value['id'] . '","' . $value['view_name'] . '","' . $value['message'] . '","' . $value['post_date'] . "\"\n";
		}
		echo self::$csv;
	}
}return;
 ?>
```


```php:dowonload.php
<?php 

// 外部ファイルの呼び出し
require_once('db.php');
require_once('csv.php');

// DB情報を定数で実装
define('DB_HOST', '127.0.0.1');
define('DB_USER', 'root');
define('DB_PASS', '各自のpass');
define('DB_NAME', 'board');


// mysqliクラスを使用し、GETパラメータの値で条件分岐
$mysqli = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);

if (!empty ($mysqli->connect_errno)) {
	echo $mysqli->connect_errno .':' .$mysqli->connect_error;
}
$get_data = $_GET['download_number'];
if (!empty($get_data)) {
	if ($get_data === '10') {
		DbSlect::setSqlSelect(10);
	}elseif ($get_data === '30') {
		DbSlect::setSqlSelect(30);
	}else{
		DbSlect::setSqlSelect(null);
	}
}
DbData::setSqlResult($mysqli->query(DbSlect::getSqlSelect()));
DbData::setMessageArray(DbData::getSqlResult());
DbData::getMessageArray()->fetch_all(MYSQLI_ASSOC);
$mysqli->close();

// ヘッター関数を使用し、出力情報の設定
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=メッセージデータ.csv");
header("Content-Transfer-Encoding: binary");
csvDownlod::csvOpretion();
```

```php:csv_index.php
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<title></title>
	<link rel="stylesheet" href="">
</head>
<body>
	<form action="test_download.php" method="get">
	<select name="download_number">
		<option value="">全て</option>
		<option value="10">10件</option>
		<option value="30">30件</option>
	</select>
	<button type="submit" name="btn-submit" value="btn-submit">ダウンロード</button>
</form>
</body>
</html>
```

### DB情報について
今回使用する、DB情報は以下になります↓


|DB名|ユーザー名|テーブル名|カラム名①|カラム名②|カラム名③|
|:---:|:---:|:---:|:---:|:---:|:---:|
|message|root|id（int）|view_name（varchar(100)）|message（text）|post_date（datetime）|

#### コード抜き出し解説①

```php:コード抜き出し解説①
class DbData {
	private static $sql_result = null;
	private static $message_array = array();

// 中略

```

ここでは、**`DbDataクラス`**を定義しています。**`private static　〜　変数名`**とすることでクラスの外部からアクセス、変更不可の**`静的なプロパティ`**を定義できます。


#### コード抜き出し②

```php:コード抜き出し②
// 中略
public static function getSqlResult() {
        return self::$sql_result;
    }
    public static function getMessageArray() {
        return self::$message_array;
    }
// 中略
```
**`public static function get~() { ...`**とすることで、**`静的なメソッド`**を定義できまふす。このメソッドは**`Getter`**と呼ばれ、**`private`**でクラス外部からからアクセス変更不可な、**`プロパティの値だけを返すメソッドの総称`**のことを指します。クラス内で静的なプロパティにアクセスする際には、**`self::$プロパティ名`**　になります。

※**`動的なメソッド`**の場合は**`static`**をつけず、**`public function メソッド名() { ...`** 
になり、動的なプロパティにアクセスする際には、**`this->プロパティ名`**となります。

#### コード抜き出し③

```php:コード抜き出し③
// 中略
public static function setSqlResult($sql_result) {
		self::$sql_result = $sql_result;
	}
	public static function setMessageArray($message_array) {
		self::$message_array = $message_array;
	}
// 中略
```
**`public static function set~() { ...`**とすることで、**`コード抜き出し②`**と同様に、**`静的なメソッド`**を定義することができる。このメソッドは**`Setter`**と呼ばれ、**`private`**でクラス外部からからアクセス変更不可な、**`プロパティの値を変更するメソッドの総称`**のことを指します。
※**`Setter`**を使用しないと、**`privateのプロパティ`**を変更できない仕組みになっています。


#### コード抜き出し④

```php:コード抜き出し④
class DbSlect {
	// 中略
	public static function getSqlSelect() {
		if (self::$sql_select == null) {
			self::$limt = null;
		}
		return "SELECT * FROM message ORDER BY post_date ASC"." ".self::$limt." ".self::$sql_select;
	}
// 中略
```
ここでは、**`DbSlectクラス`**を定義しています。

**` if (self::$sql_select == null) {
            self::$limt = null;
        }`**
のコードで、ダウンロード件数を指定しない場合はプロパティの**`$limt`**の値を**`null`**にしている。これにより、ダウンロード件数を指定しない場合は、DBから全てのデータを取得する条件分分岐が可能になる。

また、**`return "SELECT * FROM message ORDER BY post_date ASC"." ".self::$limt." ".self::$sql_select;`**で、**`SELECT文`**に当たる文字列を実装しています。

### コード抜き出し⑤

```php:コード抜き出し⑤
class csvDownlod {
	private static $csv = null;

	public static function csvOpretion() {
		self::$csv = '"ID","表示名","メッセージ","投稿日時"'."\n";
		foreach (DbData::getMessageArray() as $value) {
			self::$csv .= '"' . $value['id'] . '","' . $value['view_name'] . '","' . $value['message'] . '","' . $value['post_date'] . "\"\n";
		}
		echo self::$csv;
	}
}return
```
ここでは、**`csvDownlodクラス`**を定義しています。

**`self::$csv = '"ID","表示名","メッセージ","投稿日時"'."\n";
        foreach (DbData::getMessageArray() as $value) {
            self::$csv .= '"' . $value['id'] . '","' . $value['view_name'] . '","' . $value['message'] . '","' . $value['post_date'] . "\"\n";
        }
        echo self::$csv;
    }
}return`**
で、DBから取得したデータをcsvに実装するために、**`カンマ区切り`**で、**`$value`**に値を格納し、DBのデータも同様に配列として格納することで、カンマ区切りになっています。

#### コード抜き出し⑥

```php:コード抜き出し⑥
// 中略
public static function setSqlSelect($sql_select) {
		if (!is_null($sql_select) && !is_numeric($sql_select)){
			throw new Exception('Error: Please enter a number or a string');
		}
		self::$sql_select = $sql_select;
	}
}return
 ?>
```
**`if (!is_null($sql_select) && !is_numeric($sql_select)){
			throw new Exception('Error: Please enter a number or a string');
		}`**では、**`Setter`**でプロパティの値をセットしたり変更する際に、値の検査を行なっている。

### コード抜き出し⑦
```php:コード抜き出し⑦
// 中略
DbData::setSqlResult($mysqli->query(DbSlect::getSqlSelect()));
DbData::setMessageArray(DbData::getSqlResult());
DbData::getMessageArray()->fetch_all(MYSQLI_ASSOC);

// 中略
csvDownlod::csvOpretion();
```
ここでは、DbDataクラスとDbSlectクラスの静的メソッドを呼び出しています。静的メソッドは、インスタンスを作成せずに呼び出すことが可能で、**`クラス名::静的メソッド名`**と記述します。


## まとめ
・ヘッター関数を使用して、CSVの出力の設定をする。

・MySQLiクラスのインスタスを作成し、引数にDB情報を設定する。

・**`private static プロパティ名;`**で、クラスの外部からアクセスや変更できない、静的なプロパティを定義。

・**`private プロパティ名;`**で、クラスの外部からアクセスや変更できない、動的なプロパティを定義。

・**`public static function メソッド名(){ ...`**で、クラス外部からアクセス可能な静的なメソッドを定義。

・**`public function メソッド名(){ ...`**で、クラス外部からアクセス可能な動的なメソッドを定義。

・静的なプロパティにアクセスするには、**`self:: $プロパティ名`**で、アクセス可能。

・動的なプロパティにアクセスするには、**`this->プロパティ名`**で、アクセス可能。

#### 参考サイト
・[mysqliを使ってMySQL/MariaDBのデータベースへ接続](https://gray-code.com/php/connection-db-by-using-mysqli/)

・[データをCSVファイルとしてダウンロードする](https://gray-code.com/php/download-for-csv-file/)