生PHPについて知識を資産化していく③

#前提として

+ 自分が勉強してきた知識を保存するために書いています。
+ 本当に初歩的な事から書いていってます。
+ 万が一間違っている可能性もございますので、その時はご指摘頂ければ嬉しいです。
+ 常時更新していく予定です。
+ アウトプット用ですので、MAMPを使用していきます。

#####PHPについての記事はこちらから

+ [生PHPについて知識を資産化していく①](https://qiita.com/shotee/items/9d82034a0b2960c7b4cf)
+ [生PHPについて知識を資産化していく②](https://qiita.com/shotee/items/a23ae54814fa36289cb2)
+ [生PHPについて知識を資産化していく④](https://qiita.com/shotee/items/345da9bfa086c82c3e33)
+ [生PHPについて知識を資産化していく⑤](https://qiita.com/shotee/items/cf859e6ad4b503600924)

##データベースと接続

PHPからMySQLデータベースにアクセスするためのAPIは2種類ある(PHP7の場合)

```
①mysqli
②PDO_MySQL ※こちらで進めていきます
```

php.net：[MySQL用PHPドライバの概要](https://www.php.net/manual/ja/mysql.php)
[PHPでデータベースに接続するときのまとめ](https://qiita.com/mpyw/items/b00b72c5c95aac573b71)

###DB(データベース)操作の基本 CRUD

```
・Create　→　新規作成
・Read　→　表示
・Update　→　更新　※履歴を残すか、完全に上書きするか
・Delete　→　削除　※完全に消すか、非表示にするか

※データ量が膨大になるなら、
・パーティション(分割)
・レプリケーション(ミラーリング)

これらも要検討する
```

##phpMyAdmin

MAMPから起動させる

```
①「Open WebStart page」をクリックする
②ブラウザから、 ツール →　phpMyAdminで起動する
```

お問い合わせフォームの内容を入れていく

####新規作成する

![new.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556828/af6c74aa-aaa0-d384-33d3-ad1721b297c2.png)

####データベースを作成する

```
①データベース名を入れる　※例：infoma
②照合順序を選ぶ　※例：utf8_general_ci
```

![データベース作成](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556828/5ccfc3a5-9e15-7a5e-1b35-d199cbe53d3b.png)

####テーブルを作成

テーブル名は複数形で書く
例：users, articles など

```
①テーブル名を作成　※複数形で書く!!  //ここではcontacts と入力
②カラム数を決める　※今回はid、氏名、メールアドレス、url、性別、年齢、お問い合わせ内容、日付(created, updated)の9つ作成する
```

![テーブル作成](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556828/d70d3ba5-41b9-ccff-2fa5-6e0298b37f1b.png)

###テーブル作成時に抑えたいこと

```
・ストレージエンジン → 基本はInnoDB

・照合順序 →　現在はutf8mb4推奨

・データ型
　・数字（整数(int) 少数(float)、金額なら整数かdecimal）
  ・文字列(varchar)、boolean(真偽)、Dateなど想定される文字数に合わせる
```

![カラム挿入](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556828/8af5b536-f855-2cb0-8d6c-6a1249e0f0df.png)

カラム挿入後
↓↓

![挿入後](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556828/5e378b40-bfd8-1c1a-7799-3b19009c8d8b.png)


###新規作成

「挿入」をクリックして、データを追加する

![新規作成](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556828/1359d59a-20e9-d6fa-d0bd-fe189ac3d7d9.png)


###更新

作成したデータの横の「編集」をクリック

![データの編集](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556828/a09ba655-6a13-fd32-632a-8be3195fa804.png)

データ変更後に「実行」すると更新される。

###削除

作成したデータの横の「削除」をクリック

![データの削除](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556828/92b1de3c-bca3-23bb-757e-d0f25aa30fbb.png)

###表示

画面の「インラインを編集」をクリックする

![インラインを編集](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556828/16924168-7246-0675-ebf7-361c21ab9d2b.png)


```
・氏名だけ取り出す

SELECT your_name FROM contact;
```

![氏名だけ取り出す](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556828/0d190267-d5cc-3ee0-9dbd-734c0ca7866f.png)

それぞれの氏名だけ取り出せました!!
↓↓

![氏名取り出し2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556828/8a3b61d2-f9db-88d9-d474-e956f050439f.png)


###DB操作の基本(SELECT)

```
・SELECT　表示させたい項目
・FROM　table名
・Where　検索条件

・Group by　グループ化
・Order by　表示順序
・Join　テーブル結合(inner/outer/left)

etc...
```


##PDO(PHP Data Object)

データベースとやり取りできる方法。

###クラス

関連する機能をひとまとめに(責務の分離)

```
クラス(class)

変数/定数、関数をひとまとめにしたもの

・変数/定数　→　プロパティ
・関数　→　メソッド
```

php.net：[PDOクラス](https://www.php.net/manual/ja/class.pdo.php)

###クラスの使い方

2パターンある

```
①new(動的)
- $pdo = new class名;
- $pdo->プロパティ
- $pdo->メソッド

②static(静的)
- PDO::ATTR_ERRMODE
- PDO::ERRMODE_EXCEPTION


動的と静的
アロー演算子( -> )とスコープ演算子( :: )
```

php.net：[定義済み定数](https://www.php.net/manual/ja/pdo.constants.php)


###作成したデータベースに接続するためのユーザーを作成する

作成しているデータベースを選んでいる状態で「特権」をクリックする

![データベースに接続](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556828/c9571330-6198-19a4-0f50-8765edee89d3.png)

ユーザーアカウントを追加する

![ユーザーアカウント追加](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556828/82ebc311-5ceb-02d1-08b4-98bd6d2dd3d2.png)


+ ログイン情報入力
+ 「データベース infoma への全ての特権を与える。」にチェックを入れる
+ 「実行」をクリックする

![ログイン情報入力](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556828/1614c4d3-29df-bf4b-a7b7-dc572b7787a3.png)

新しいユーザーが追加されました!!


###PDOでデータベースと接続していく

```mainte/db_connection.php
<?php
//指定されたデータベースの接続を表すPDOインスタンスを生成する
const DB_HOST = 'mysql:dbname=infoma;host=127.0.0.1;charset=utf8';

// データベースにアクセスできるユーザー
const DB_USER = 'php_user';
// データベースにアクセスするためのパスワード
const DB_PASSWORD = 'password123';

$pdo = new PDO(DB_HOST, DB_USER, DB_PASSWORD);

// データベースと繋がっているかどうかをチェックする(例外処理) Exception

// トライする処理、エラーが出た時の処理
// try{データベースに接続する方法を書く} catch(PDOExecption $e){
//  echo '接続失敗' . $e->getMessage() . "\n";
// exit();  //接続失敗した時に抜ける
//}

try{
  $pdo = new PDO(DB_HOST, DB_USER, DB_PASSWORD, [
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,//連想配列
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, //例外
    PDO::ATTR_EMULATE_PREPARES => false, //SQLインジェクション対策
  echo '接続成功';
} catch(PDOException $e) {
  echo '接続失敗' . $e->getMessage() . "\n";
  exit;
}

// ログイン画面に設定したユーザー名とパスワードを入れて、合っていれば接続成功と出る

?>
```

php.net：[PDO::__construct](https://www.php.net/manual/ja/pdo.construct.php)
php.net：[例外](https://www.php.net/manual/ja/language.exceptions.php)
[SQLインジェクションを防ぐ対策とは｜わかりやすく仕組みを解説](https://www.amiya.co.jp/column/sql_injection_20200518.html)


###データベースの中身を表示させる

```mainte/index.php
<?php

require 'db_connection.php';

// ユーザー入力なし(query)
$sql = 'select * from contacts where id = 4'; //sql文

$stmt = $pdo->query($sql); //sql文実行 ステートメント

$result = $stmt->fetchAll();　//sql文の結果を表示

var_dump($result);

↓↓

// 接続成功
// array(1) {
//  [0]=>
//  array(9) {
//    ["id"]=>
//    int(2)
//    ["your_name"]=>
//    string(9) "あああ"
//    ["email"]=>
//    string(13) "test@test.com"
//    ["url"]=>
//    string(15) "http://test.com"
//    ["gender"]=>
//    int(0)
//    ["age"]=>
//    int(5)
//    ["contact"]=>
//    string(9) "テスト"
//    ["created_at"]=>
//    string(19) "2021-04-03 10:23:42"
//    ["updated_at"]=>
//    string(19) "2021-04-03 10:24:37"
//  }
// }

?>
```

```mainte/index.php
<?php

require 'db_connection.php';

// ユーザー入力あり(prepare, bind, execute)
$sql = 'select * from contacts where id = :id'; //名前付きプレースホルダ

$stmt = $pdo->prepare($sql); // プリペアードステートメント
$stmt->bindValue('id', 3, PDO::PARAM_INT); //数字の型として 3 が入力させる 紐付け
$stmt->execute(); //実行

$result = $stmt->fetchAll(); //sql文の結果を表示

echo '<pre>';
var_dump($result);
echo '</pre>';

↓↓

// 接続成功
// array(1) {
//  [0]=>
//  array(9) {
//    ["id"]=>
//    int(3)
//    ["your_name"]=>
//    string(9) "いいい"
//    ["email"]=>
//    string(15) "test2@test2.com"
//    ["url"]=>
//    string(16) "http://test2.com"
//    ["gender"]=>
//    int(1)
//    ["age"]=>
//    int(2)
//    ["contact"]=>
//    string(10) "テスト2"
//    ["created_at"]=>
//    string(19) "2021-04-03 10:24:47"
//    ["updated_at"]=>
//    string(19) "2021-04-03 10:26:21"
//  }
// }

?>

```

php.net：[PDOStatement クラス](https://www.php.net/manual/ja/class.pdostatement.php#class.pdostatement)
php.net：[プリペアドステートメントおよびストアドプロシージャ](https://www.php.net/manual/ja/pdo.prepared-statements.php)
[【PHP】PDOの静的プレースホルダと動的プレースホルダの違いを確認する](https://qiita.com/7968/items/7ddd59b94eb5a4fb6eaf)

###トランザクション(まとまって処理)

beginTransaction, commit, rollback

```
<?php

require 'db_connection.php';

$sql = 'select * from contacts where id = :id'; //名前付きプレースホルダ

$pdo->beginTransaction();

try{

//sql処理
$stmt = $pdo->prepare($sql); // プリペアードステートメント
$stmt->bindValue('id', 3, PDO::PARAM_INT); //数字の型として 3 が入力させる 紐付け
$stmt->execute(); //実行

$pdo->commit(); //全てまとめて処理をする

}catch(PDOException $e){
  $pdo->rollBack(); //更新のキャンセル
}

?>
```

##フォーム値をDBに保存

お問い合わせフォームで登録した内容をデータベースに保存してみる

```mainte/insert.php
// DB接続 PDO
require 'db_connection.php';

// 入力 DB保存(ユーザー入力あり) prepare, bind, execute(配列(全て文字列の場合))

//ダミーデータ
$params = [
  'id' => null,　//AUTO＿INCREMENTを設定していて自動的に値が入るのでnull
  'your_name' => 'なまえ',
  'email' => 'test@test.com',
  'url' => 'http://test.com',
  'gender' => '1',
  'age' => '2',
  'contact' => 'いいい',
  'created_at' => null, //TIME STAMPは自動的に設定してくれるのでnull
  'updated_at' => null　//TIME STAMPは自動的に設定してくれるのでnull
];

$count = 0;
$columns = '';
$values = '';

// array_keysで連想配列の左のキーを持ってくる
foreach(array_keys($params) as $key) {
  if($count++>0) {
    $columns .= ','; //0より大きかったら、「,」をつけて区切っていく
    $values .= ','; //0より大きかったら、「,」をつけて区切っていく
  }
  $columns .= $key;
  $values .= ':'.$key; //頭に「:」をつけて名前付きプレースホルダーになる

$sql = 'insert into contacts ('. $columns .')values('. $values .')'; //名前付きプレースホルダー

// var_dump($sql); //$sqlの中身を確認する
// exit; //ここで処理を止めた場合は保存されない。

// ↓↓成功するとこのように表示される

// 接続成功string(166) "insert into contacts 
// (id,your_name,email,url,gender,age,contact,created_at,updated_at)values(:id,:your_name,:email,:url,:gender,:age,:contact,:created_at,:updated_at)"

$stmt = $pdo->prepare($sql); // プリペアードステートメント
$stmt->execute($params); //実行 データベースに保存される

↓↓

//接続成功　と出る

?>
```

接続成功すると、ダミーデータで設定した値が反映される
(idの値で間が空いているのは、別で作ったダミーデータを消したため)

![ダミーデータ反映](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556828/863eeca8-3fbc-2988-851b-912295967a80.png)


###お問い合わせフォームの完了場所にデータベースを保存するコードを書く

```form/input.php
<?php
   ・
   ・
   ・
    <?php if ( $pageFlag === 2 ) : ?>
    <!-- 合言葉が合っていたか確認する -->
      <?php if ($_POST['csrf'] === $_SESSION['csrfToken']) :?>

      <!-- DB保存 -->
      <?php require '../mainte/insert.php';
      // 値を受け渡す
      insertContact($_POST);
      ?>

      送信が完了しました。

      <!-- 合言葉を削除する -->
      <?php unset($_SESSION['csrfToken']); ?>
      <?php endif; ?>
    <?php endif; ?>

?>
```

```mainte/insert.php
<?php

// DB接続 PDO
function insertContact($request) {
require 'db_connection.php';

// 入力 DB保存(ユーザー入力あり) prepare, execute(配列(全て文字列の場合))

$params = [
  'id' => null, //AUTO＿INCREMENTを設定していて自動的に値が入るのでnull
  'your_name' => $request['your_name'],
  'email' => $request['email'],
  'url' => $request['url'],
  'gender' => $request['gender'],
  'age' => $request['age'],
  'contact' => $request['contact'],
  'created_at' => null, //TIME STAMPは自動的に設定してくれるのでnull
  'updated_at' => null //TIME STAMPは自動的に設定してくれるのでnull
];

   ・
   ・
   ・

$stmt = $pdo->prepare($sql); // プリペアードステートメント
$stmt->execute($params); //実行

}
?>
```

####①お問い合わせフォームから値を入力していく

![フォーム入力](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556828/d3db4d56-1303-cc6b-ae48-0813ad8cadd1.png)

####②確認画面で問題なければ「送信」ボタンを押す

![フォーム確認画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556828/8ceab8b5-5a7f-830c-d506-f7f57ee232da.png)

####③送信完了画面

![送信完了画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556828/4d969e4e-a1ad-6f81-33b4-3cfed83a8ced.png)

####④送信完了後、データベースに保存されているか確認する

![データベース確認](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556828/65fb1571-1482-e37a-0e58-40871d5fceb2.png)

処理の内容としては、

```
①お問い合わせフォームの内容を一旦連想配列に入れる
②PDOでデータベースで繋ぐ
③プレースホルダーで値を渡す
④プリペアードステートメントで準備する
⑤executeで実行!!

※SQL文を複数作るなら トランザクション を使う
```


Laravelなどのフレームワークでは、ORM, OR/マッパーという方法がある

ただ、ベースとなる知識は、

```
・PDO
・プレースホルダー
・プリペアードステートメント
```

これらの基盤を抑えるのは大切!!

#####PHPについての記事はこちらから

+ [生PHPについて知識を資産化していく①](https://qiita.com/shotee/items/9d82034a0b2960c7b4cf)
+ [生PHPについて知識を資産化していく②](https://qiita.com/shotee/items/a23ae54814fa36289cb2)
+ [生PHPについて知識を資産化していく④](https://qiita.com/shotee/items/345da9bfa086c82c3e33)
+ [生PHPについて知識を資産化していく⑤](https://qiita.com/shotee/items/cf859e6ad4b503600924)