###### tags: `ready for review`
# PDOとプリペアードステートメント
前章では`GET`と`POST`、送信されてきたデータを受け取る方法を解説しました。
今回はデータを受け取るだけで終わるのではなく、受け取ったデータをデータベースに保存したり、データベースにあるデータを取得できるようにします。
データベースにデータを保存したり、データベースからデータを取得したりするためには、まず、データベースとPHPを接続する必要があります。
この接続処理を行ってくれるのが`PDO(PHP Data Objects)`です。
`プリペアードステートメント`がなんだったのか忘れてしまった方は、[SQLの基本を抑えよう](https://hackmd.io/@GH-wNvm-QfG5Y89-RmIY8g/HJV6FLXtu)に戻って復習しましょう。
それではPHPとデータベースの接続をし、`POST`でリクエストしたデータをデータベースに保存できるようにしていきます!
## PHPとデータベースを接続しよう
PDOクラスはインスタンスを生成する時、接続先のデータベースの情報を引数に渡します。
引数に渡す値は以下のようになっています。
| 引数 | 属性 | パラメータ |
| :- | :-: | :-: |
| 1(必須) | string | mysql:dbname=[データベース名];host=[ホスト名];charset=utf8 |
| 2 | string | データベースのユーザー名 |
| 3 | string | データベースのパスワード |
今回使用するデータベースは`SQL_lesson`でそのデータベースのホスト名は`localhost`です。
ユーザー名は`root`、パスワードは設定していません。
:::info
MySQLは標準でホスト名がlocalhostになります
:::
これらの情報を整理するとこのようになります。
| 引数 | 属性 | パラメータ |
| :- | :-: | :-: |
| 1(必須) | string | mysql:dbname=[データベース名];host=[ホスト名];charset=utf8 |
| 2 | string | root |
| 3 | string | なし |
引数に渡すパラメータが分かったので、db.phpに記述していきます。
PDOインスタンスは使い回すので変数に入れておきましょう。
```php=
<?php
$db = new PDO("mysql:dbname=SQL_lesson;host=localhost;charset=utf8", "root");
```
これだけでデータベースに接続ができます。
しかし、このままではファイルを実行しても正常に接続できたのかどうか分かりません。
そこで、try-catch構文で接続できなかった時にエラーメッセージを表示するようにしてみます。
```php=
<?php
try {
// 慣習としてPDOインスタンスはdbh(データベースハンドルの略)にする
$dbh = new PDO("mysql:dbname=SQL_lesson;host=localhost;charset=utf8", "root");
echo "接続完了";
} catch(PDOException $e) {
echo "エラーメッセージ : " . $e -> getMessage();
}
```
ここまで記述できたら実行して「接続完了」が表示されるかどうか確認してみて下さい。
「接続完了」が表示された方は`echo "接続完了"`を削除しておきましょう。
:::info
エラーメッセージが表示された場合は、引数に渡す値が間違ってないかどうか確認してみましょう。
:::
## SQLを実行してみよう
PHPからSQLを実行するときは`queryメソッド`を使用します。
queryメソッドは引数にSQLの文字列を渡すだけで簡単に使用できます。
### データを追加してみよう
queryメソッドを使用してblogsテーブルにデータを追加してみます。
```php=
<?php
try {
// 慣習としてPDOインスタンスはdbh(データベースハンドルの略)にする
$dbh = new PDO("mysql:dbname=SQL_lesson;host=localhost;charset=utf8", "root");
$sql = "INSERT INTO blogs (content) VALUES ('test')";
// 慣習としての返り値がPDOStatementになるものはstmt(ステートメント)もしくはsth(ステートメントハンドル)にする
$stmt = $dbh -> query($sql);
} catch(PDOException $e) {
echo "エラーメッセージ : " . $e -> getMessage();
}
```
### データを取得してみよう
blogsテーブルのレコードを全て取得し、取得したデータを表示してみます。
```php=
<?php
try {
// 慣習としてPDOインスタンスはdbh(データベースハンドルの略)にする
$dbh = new PDO("mysql:dbname=SQL_lesson;host=localhost;charset=utf8", "root");
$sql = "SELECT * FROM blogs";
// 慣習としての返り値がPDOStatementになるものはstmt(ステートメント)もしくはsth(ステートメントハンドル)にする
$stmt = $dbh -> query($sql);
foreach($stmt as $record) {
print_r($record);
}
} catch(PDOException $e) {
echo "エラーメッセージ : " . $e -> getMessage();
}
```
:::info
queryメソッドの引数に渡したSQLが間違っていてもエラーになりません。
エラーを発生させるには以下の記述が必要になります。
```php=
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
```
[setAttributeについて](https://www.php.net/manual/ja/pdo.setattribute)
:::
## プリペアードステートメント
PHPでデータベースを操作するときにも[プリペアードステートメント](https://hackmd.io/@GH-wNvm-QfG5Y89-RmIY8g/HJV6FLXtu)を使用することができます。
まずはデータを登録するときに実行される共通部分を考えてみます。
blogsテーブルにデータを登録する時に共通している部分は以下のようになります。
```sql=
INSERT INTO blogs (content) VALUES ( );
```
次に、プレースホルダを用意します。
```php=
<?php
try {
// 慣習としてPDOインスタンスはdbh(データベースハンドルの略)にする
$dbh = new PDO("mysql:dbname=SQL_lesson;host=localhost;charset=utf8", "root");
$sql = "INSERT INTO blogs (content) VALUES (:content)";
} catch(PDOException $e) {
echo "エラーメッセージ : " . $e -> getMessage();
}
```
[SQLの基本を抑えよう](/-WJPZ4tYTRKI4URJg32mtA)で学んだ`?`のプレースホルダでも問題ありませんが、
本講座ではプレースホルダを`:〇〇`の形で管理します。
これを`名前付きプレースホルダ`と言います。
`:コロン`に続く英単語は何でも構いませんが、カラム名と一致させておく方が分かりやすいです。
このままでは文字列を変数に代入しただけなので、
`prepareメソッド`を使用して引数に先ほど用意した文字列を渡すことで、
プリペアードステートメントとして認識されるようになります。
```php=
<?php
try {
// 慣習としてPDOインスタンスはdbh(データベースハンドルの略)にする
$dbh = new PDO("mysql:dbname=SQL_lesson;host=localhost;charset=utf8", "root");
$sql = "INSERT INTO blogs (content) VALUES (:content)";
$stmt = $dbh->prepare($sql);
} catch(PDOException $e) {
echo "エラーメッセージ : " . $e -> getMessage();
}
```
今度はプレースホルダに値をバインドさせます。
バインドするには`bindValueメソッド`を使用します。
`bindValue`の引数には`("名前付きプレースホルダ", 値, データ型)`を渡します。
第3引数のデータ型はPHPがあらかじめ用意している定数で指定します。([定義済み定数](https://www.php.net/manual/ja/pdo.constants.php))
```php=
<?php
try {
// 慣習としてPDOインスタンスはdbh(データベースハンドルの略)にする
$dbh = new PDO("mysql:dbname=SQL_lesson;host=localhost;charset=utf8", "root");
$sql = "INSERT INTO blogs (content) VALUES (:content)";
$stmt = $dbh->prepare($sql);
$stmt->bindValue(":content", "初めてのプリペアードステートメント", PDO::PARAM_STR);
} catch(PDOException $e) {
echo "エラーメッセージ : " . $e -> getMessage();
}
```
これで値をバインドすることができました。
この状態でPHPを実行してもまだSQLが実行されないので、
準備したSQLを実行する処理を記述します。
`executeメソッド`を使用すると値がバインドされた状態でSQLが実行されます。
```php=
<?php
try {
// 慣習としてPDOインスタンスはdbh(データベースハンドルの略)にする
$dbh = new PDO("mysql:dbname=SQL_lesson;host=localhost;charset=utf8", "root");
$sql = "INSERT INTO blogs (content) VALUES (:content)";
$stmt = $dbh->prepare($sql);
$stmt->bindValue(":content", "初めてのプリペアードステートメント", PDO::PARAM_STR);
$stmt->execute();
} catch(PDOException $e) {
echo "エラーメッセージ : " . $e -> getMessage();
}
```
SQL文に変動する箇所があれば`prepare`、それ以外は`query`で問題ないという風に覚えておきましょう!
今回はPHPからデータベースに接続する方法、プリペアードステートメントについて解説しました。
今までの章に比べて非常に難しくなっています。
一回で理解しようとするのではなく、何度も繰り返して学んでいくようにしましょう!