【PHP】PDO接続周り

Laravelなどのフレームワークでは、クエリビルダやEloquentを使って簡単にDBから値を取ってくることができます。
今回は、PDOを使ってDB接続し、データを取ってくる流れを記載していきます。

1. DBに接続(例外処理も含めて)
2. SQL文の準備
3. 準備したSQL文に値をバインドする
4. 準備したSQLを実行する
5. 実行結果を配列などで取得する

この順番で解説していきます。

## 1. DBに接続(例外処理も含めて)
```php
$pdo = new PDO($dsn, $username, $password, $driver_options);
```
のような形で、DB接続するためのPDOインスタンスを作成します。

```php
try {
  $dbh = new PDO(
    'mysql:host=サーバー名;dbname=データベース名;charset=utf8', // $dsn
    'ユーザー名', // $username
    'パスワード', // $password
    array( // $driver_options
      PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // DB接続でエラーが発生したときに、PDOExceptionの例外を捕捉する
      PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // 実行結果を配列で返すオプション
      PDO::ATTR_EMULATE_PREPARES => false, // 静的プレースホルダーを使うように設定
    )
  );
} catch (PDOException $e) {
  $error = $e->getMessage();
}
```
- `new PDO` で、DBに接続するためのPDOインスタンスを作成します。
- `$driver_options`の部分は、PDOで使用するoptionを指定します。
  基本的には配列で指定します。
  `PDO::ATTR_EMULATE_PREPARES`の部分は後々解説します。

## 2.SQL文の準備
```php
$sql = 'SELECT * FROM article WHERE id = ?';
$prepare = $dbh->prepare($sql);
```
- `$sql`には、SQL文を書きます。 `?`の部分は`プレースホルダー`になっていて、後々そこに値を割り当てます。
  このような仕組みを`プリペアドステートメント`というらしいです。
 - `?`のプレースホルダーを使用する時は、`静的プレースホルダー`
 - `:カラム名`のプレースホルダーを使用する時は`動的プレースホルダー`と言います。
 - 動的プレースホルダーの方が処理速度が早いらしいですが、SQLインジェクション対策のため、基本的には静的プレースホルダーを使用することが推奨されます。
  
[【PHP超入門】クラス～例外処理～PDOの基礎 - Qiita](https://qiita.com/7968/items/6f089fec8dde676abb5b#%E9%9D%99%E7%9A%84%E3%83%97%E3%83%AC%E3%83%BC%E3%82%B9%E3%83%9B%E3%83%AB%E3%83%80%E3%81%A8%E5%8B%95%E7%9A%84%E3%83%97%E3%83%AC%E3%83%BC%E3%82%B9%E3%83%9B%E3%83%AB%E3%83%80%E3%81%AE%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88%E3%83%87%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88)

- また、`SQLインジェクション対策`にもなるため必須の方法になります。
[SQLインジェクションとクエリの書き方について考える - Qiita](https://qiita.com/nagimaruxxx/items/f52629044033e47e078e)  

- `$prepare`では、PDOのprapareメソッドを使って、SQL文を実行する準備を行っています。

## 3. 準備したSQL文に値をバインド(割り当て)する
```php
bindValue(パラメータID,バインドする値,データ型）
```
という形でバインドします。

```php
$prepare->bindValue(1, (int)$id, PDO::PARAM_INT);
```
整数型の引数を割り当てるには、(int)という形で`型キャスト`をする必要があります。
[【PHP入門講座】 型変換 - Qiita](https://qiita.com/mpyw/items/3aac811629622f7f4d8b)

整数以外は、デフォルトで`PDO::PARAM_STR`が指定されます。

静的プレースホルダーの場合、パラメータIDの部分には1から始まる値を入れます。
例えば、

```php
$sql = 'SELECT * FROM article WHERE id = ? and title = ?';
```
のように、?のプレースホルダーが2つある場合は、

```php
$prepare->bindValue(1, $id, PDO::PARAM_INT);
$prepare->bindValue(2, $title, PDO::PARAM_STR);
```
のように、第一引数が1,2...と数字が続いていきます。

## 4. 準備したSQLを実行する
```php
$sql = 'SELECT * FROM article WHERE id = ?';
$id = 1;
$prepare = $dbh->prepare($sql);
$prepare->bindValue(1, (int)$id, PDO::PARAM_INT);
$prepare->execute();
```
execute()メソッドでSQLを実行します。

## 5. 実行結果を配列などで取得する
```php
$result = $prepare->fetchAll(PDO::FETCH_ASSOC);
```
`fetchAll`メソッドで、条件に合致した全てのデータを配列として返します。

## 全体像
```php
<?php
function dbConnect()
{
  try {
    $dsn = 'mysql:host=' . 'db' . '; dbname=' . 'test_php' . ';charset=utf8;';
    $driver_options = [
      PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
      PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
      PDO::ATTR_EMULATE_PREPARES => false,
    ];
    $dbh = new PDO($dsn, 'root', 'test', $driver_options);
  } catch (PDOException $e) {
    print('ERROR:' . $e->getMessage());
    exit;
  }
  return $dbh;
}

function getData()
{
  $dbh = dbConnect();
  $sql = 'SELECT * FROM article WHERE id = ?';
  $prepare = $dbh->prepare($sql);
  $id = 1;
  $prepare->bindValue(1, (int)$id, PDO::PARAM_INT);
  $prepare->execute();
  $result = $prepare->fetchAll(PDO::FETCH_ASSOC);
  return $result;
}

$result = getData(); // $result をHTML側で表示する場合は、HTML側でループして表示させます。
?>
```