【PDO】PHPでデータベースに接続する方法【INSERT編】

>この記事では、《PHPでデータベースに接続する方法》について、
業務を通して学習した内容をまとめています。

- __PDOって何なの？__
- __PHPでデータベースにどうやって接続するの？__
- __データベースに登録したユーザーを検索する機能を実装したい__

こういった疑問に答えます。

__※本記事は、自分で学習したことのまとめ用として書いています。
尚、PHPの解説で誤った点があれば、スローして頂ければ喜んでキャッチしますのでお願い致します。__

#PDOの基礎
![school-2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255884/c8bd2bbd-b7dc-21bd-3752-c54ea54228b1.jpeg)

現在、PHPでデータベースに接続する際は、『PDO』を使う方法が主流です。そこで、PDOの基本的な使い方を解説していきます。

###概要：PDOとは？
『[PDO](https://www.php.net/manual/ja/class.pdo.php)』とは『PHP Data Objects』の略で、ざっくり言うと…

__PHPからデータベースへいい感じにアクセスしてくれるもの__

です。

通常データベースサーバーへのアクセスは、以下のようなデータベース固有のドライバが使用されます。

- MySQL
- PostgreSQL
- SQLite
- Oracle
- MS SQL Server　etc…

ですが・・・

PDOを使用することにより、接続するデータベースにかかわらず、同じ操作でデータにアクセスすることが可能になります。

##PDOを使ってPHPでデータベースに接続する方法【ユーザー検索】

![startup-1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255884/fbc58f1e-0495-e7b7-a95d-3c9c16d14196.jpeg)

PDOを使ってデータベースに接続しユーザー検索するのは、下記の手順で行います。

1. __&ensp;PDOクラスをインスタンス化__
2. __&ensp;プリペアドステートメントを準備する__
3. __&ensp;値をバインドする__
4. __&ensp;プリペアドステートメントを実行する__
5. __&ensp;SELECTの結果を取得する__

それでは、ひとつずつ見ていきましょう。

###Step①：PDOクラスをインスタンス化

PDOクラスをインスタンス化して、データベースに接続します。

~~~php
$pdo = new PDO(
    'DSN',
    'ユーザーネーム',
    'パスワード',
    'オプション'
);
~~~

上記の通り、インスタンス化するときに引数に必要な情報を記述することで、データベースに接続できます。

___※データベースに接続する際は、例外処理を記述する必要がありますが、現時点ではデータベースへ接続する箇所だけ見ていきます。___

また、コンストラクタには、以下のような引数を指定します。

- __DSN (Data Source Name)　※必須__
- __ユーザー名__
- __パスワード__
- __オプション__

それぞれ簡単に解説していきます。

####DSN (Data Source Name)　※必須

データベースに接続するために必要な情報です。
以下に各データベース製品に応じたDSNの書き方が掲載されています．

[≫ PHP Manual – PDOクラスのデータベース別DSN一覧]
(https://www.php.net/manual/ja/pdo.drivers.php)

例えば、MySQLの場合は以下のように記述します。

~~~php
pdo = new PDO(
    'mysql:host=サーバー名;dbname=DB名;charset=文字エンコード',
    'ユーザー名',
    'パスワード',
    'オプション'
);
~~~

__● mysql:host__
ホスト名またはIPアドレスを指定します。

__● dbname__
データベース名を指定します。基本的には必須ですが、データベースを後で USE test のようにSQL文で選択する場合は、省略することができます。

__● charset__
文字セットを指定します。SET NAMES とするのは避けて、ここで指定するべきです。UTF-8ではなくutf8であることに注意してください。ハイフンは入りません。

####ユーザー名
ユーザー名を指定します。ルート権限を使う場合は、デフォルトで root です。

####パスワード
パスワードを指定します。ルート権限を使う場合は、デフォルトで空白にします。

####オプション
接続時のオプションを連想配列で渡します。
オプションについては、次で詳しく解説します。

###インスタンス化するときにオプションを設定する方法

データベースに接続するコードを思い出してください。

~~~php
pdo = new PDO(
    'mysql:host=サーバー名;dbname=DB名;charset=文字エンコード',
    'ユーザー名',
    'パスワード',
    'オプション'
);
~~~

上記のように、PDOクラスをインスタンス化する際にオプションを記述することが可能です。

オプションは連想配列で指定し、下記のような記述になります。

~~~php
pdo = new PDO(
    'mysql:host=サーバー名;dbname=DB名;charset=文字エンコード',
    'ユーザー名',
    'パスワード',
    [
        PDO::ATTR_ERRMODE = PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE = PDO::FETCH_ASSOC
    ]
);
~~~

また、よく使われるオプションについては、以下の通りです。

1. __&ensp;PDO::ATTR_ERRMODE__
2. __&ensp;PDO::ATTR_DEFAULT_FETCH_MODE__

ひとつずつ簡単に解説していきます。

####オプション①：PDO::ATTR_ERRMODE

SQLでエラーが起こった際の処理について設定します。
尚、デフォルトでは “PDO::ERRMODE_SILENT” と設定されています。

- __PDO::ERRMODE_EXCEPTION__<br>
__設定すると、[例外（PDOException）](https://www.php.net/manual/ja/class.pdoexception.php)を投げてくれます。基本的にこれを選択しておきます。__
- PDO::ERRMODE_WARNING<br>
 設定すると、E_WARNINGメッセージを出力します。
この設定が返すのはE_WARNINGなので、スクリプトは停止されません。
- PDO::ERRMODE_SILENT<br>
デフォルトのエラー設定。エラー内容は保持しているが、エラーメッセージは出力しません。 

####オプション②：PDO::ATTR_DEFAULT_FETCH_MODE

[fetchメソッド](https://www.php.net/manual/ja/pdostatement.fetch.php)や、[fetchAllメソッド](https://www.php.net/manual/ja/pdostatement.fetchall.php)で引数が省略された場合のフェッチスタイル(配列のパターン)を設定します。
尚、デフォルトでは “PDO::FETCH_BOTH” と設定されています。

- __PDO::FETCH_ASSOC<br>
カラム名をキーとする連想配列を返します。基本的にこれを選択しておきます。__
- PDO::FETCH_BOTH<br>
カラム名と、0から始まるカラム番号をキーとする配列を返します。
- PDO::FETCH_NUM<br>
0から始まるカラム番号をキーとする配列を返します。
- PDO::FETCH_OBJ<br>
カラム名と同名のプロパティを有するオブジェクトを返します。

###Step②：プリペアドステートメントを準備する
[executeメソッド](https://www.php.net/manual/ja/pdostatement.execute.php)によって実行されるプリペアドステートメントを準備します。

- __プレースホルダ__<br>
ユーザが入力した内容を後から挿入するために、予め確保した場所のこと。
- __プリペアドステートメント__<br>
SQL文で値がいつでも変更できるように、変更する箇所だけ変数のようにしたSQL文を作る仕組みのことです。
通常「プリペアドステートメント」は「プレースホルダ」を使うために作られます。

プレースホルダには…

- __疑問符プレースホルダ__
- __名前付きプレースホルダ__

の２種類がありますが、仮にこれらが混ざってしまうと…

~~~php
SQLSTATE[HY093]: Invalid parameter number: mixed named and positional parameters
~~~

が発生するので、どちらか片方を選択してください。

尚、ここでは『名前付きプレースホルダ』についてのみ解説します。

####名前付きプレースホルダ
- __：を頭につけ、半角英数字とアンダースコアにて構成__
- __バインド時の頭の：は省略可能__

PDOクラスの[prepareメソッド](https://www.php.net/manual/ja/pdo.prepare.php)を使い、SQL文を実行する準備を行います。

[prepareメソッド](https://www.php.net/manual/ja/pdo.prepare.php)は、返り値としてPDOStatementのインスタンスを返します。

仮に、データベースサーバーが文を準備できなかった場合、 [prepareメソッド](https://www.php.net/manual/ja/pdo.prepare.php)は FALSE を返すか、[例外（PDOException）](https://www.php.net/manual/ja/class.pdoexception.php)をスローします。(エラー処理の方法に依存します)

下記では、作成されたインスタンスを変数$stmtに代入しています。

~~~php
$spl = (
    'INSERT INTO
        'テーブル名'(USER_ID, USER_NAME, PASSWORD)
    VALUES
        (:USER_ID, :USER_NAME, :PASSWORD)'
);

$stmt = $pdo->prepare($sql);
~~~

###Step③：値をバインドする
値をバインドするには、[bindValueメソッド](https://www.php.net/manual/ja/pdostatement.bindvalue.php)または[bindParamメソッド](https://www.php.net/manual/ja/pdostatement.bindparam.php)を使います。
ここでは、bindValueを使って値をバインドします。

bindValueメソッドを使うときは、下記のように記述します。

~~~php
bindValue(パラメータ, バインドする値, データ型）;
~~~
第１引数には、『パラメータ』を指定します。
パラメータは、『名前付きプレースホルダ』の場合は『:パラメータ』となります。

また・・・

『疑問符プレースホルダ』の場合は、『1からはじまる値』になります。

第２引数には、『バインドする値』を指定します。

第３引数には、『データ型』を指定します。
データ型は、[定義済み定数](https://www.php.net/manual/ja/pdo.constants.php)を指定します。

ちなみに・・・

主な[定義済み定数](https://www.php.net/manual/ja/pdo.constants.php)は、下記の通りです。

- __PDO::PARAM_BOOL ⇨ 真偽型__
- __PDO::PARAM_NULL ⇨ NULL型__
- __PDO::PARAM_INT ⇨ 整数型__
- __PDO::PARAM_STR ⇨ 文字列型 （デフォルト設定）__


デフォルトでは、 PDO::PARAM_STR が指定されているので、何も指定しないとデフォルト値になります。

『名前付きプレースホルダ』を用いた場合は、下記のように記述します。

~~~php
// パラメータ
$spl = (
    'SELECT
        USER_ID,
        USER_NAME,
        PASSWORD
    FROM
        テーブル名
    WHERE
        USER_ID = :USER_ID
        AND USER_NAME = :USER_NAME
        AND PASSWORD = :PASSWORD'
);

// プリペアドステートメントを用意
$stmt = $pdo->prepare($sql);

// 値をバインドする
$stmt->bindValue(':USER_ID', $user_id, PDO::PARAMS_STR);
$stmt->bindValue(':USER_NAME', $user_name, PDO::PARAMS_STR);
$stmt->bindValue(':PASSWORD', $password, PDO::PARAMS_STR);
~~~

###Step④：プリペアドステートメントを実行する

プリペアドステートメントを実行するには、[executeメソッド](https://www.php.net/manual/ja/pdostatement.execute.php)を使います。

~~~php
// パラメータ
$spl = (
    'SELECT
        USER_ID,
        USER_NAME,
        PASSWORD
    FROM
        テーブル名
    WHERE
        USER_ID = :USER_ID
        AND USER_NAME = :USER_NAME
        AND PASSWORD = :PASSWORD'
);

// プリペアドステートメントを用意
$stmt = $pdo->prepare($sql);

// 値をバインドする
$stmt->bindValue(':USER_ID', $user_id, PDO::PARAMS_STR);
$stmt->bindValue(':USER_NAME', $user_name, PDO::PARAMS_STR);
$stmt->bindValue(':PASSWORD', $password, PDO::PARAMS_STR);

// 実行
$stmt->execute();
~~~

###Step⑤：SELECTの結果を取得する

データを取得するには、下記の２つの方法があります。

1. __&ensp;fetchメソッドでデータを取得する__
2. __&ensp;fetchAllメソッドでデータを取得する__

それでは、ひとつずつ見ていきましょう。

####方法①：fetchメソッドでデータを取得する
[fetchメソッド](https://www.php.net/manual/ja/pdostatement.fetch.php)は、該当するデータを配列として１行のみ取得します。

また・・・

該当するデータがない場合は常に FALSE を返します。

~~~php
// プリペアドステートメントを用意
$stmt = $pdo->prepare($sql);

// 値をバインドする
$stmt->bindValue(':USER_ID', $user_id, PDO::PARAMS_STR);
$stmt->bindValue(':USER_NAME', $user_name, PDO::PARAMS_STR);
$stmt->bindValue(':PASSWORD', $password, PDO::PARAMS_STR);

// 実行
$stmt->execute();

// 結果を取得する
$row = $prepare->fetch();
~~~

####方法②：fetchAllメソッドでデータを取得する

[fetchAllメソッド](https://www.php.net/manual/ja/pdostatement.fetchall.php)は、該当する全てのデータを配列として返します。

取得結果がゼロ件だった場合は空の配列を返し、 失敗した場合は FALSE を返します。

~~~php
// プリペアドステートメントを用意
$stmt = $pdo->prepare($sql);

// 値をバインドする
$stmt->bindValue(':USER_ID', $user_id, PDO::PARAMS_STR);
$stmt->bindValue(':USER_NAME', $user_name, PDO::PARAMS_STR);
$stmt->bindValue(':PASSWORD', $password, PDO::PARAMS_STR);

// 実行
$stmt->execute();

// 結果を取得する
$row = $prepare->fetchAll();
~~~

##まとめ：基本コーディング
そして、まとめると、下記が書き方のテンプレになります。

~~~php
<?php

try {

    // リクエストから得た変数をチェックする等の処理
    $user_id = "ここにユーザーIDが入ります";
    $user_name = "ここにユーザー名が入ります";
    $password = "ここにパスワードが入ります";

    // データベース接続設定
    $pdo = new PDO(
        'mysql:host=サーバー名;dbname=DB名;charset=文字エンコード',
        'ユーザー名',
        'パスワード',
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
        ]
    );

    // パラメータ
    $spl = (
        'SELECT
            USER_ID, USER_NAME, PASSWORD
        FROM
            テーブル名
        WHERE
            USER_ID = :USER_ID
            AND USER_NAME = :USER_NAME
            AND PASSWORD = :PASSWORD'
    );

    // プリペアドステートメントを用意
    $stmt = $pdo->prepare($sql);

    // 値をバインドする
    $stmt->bindValue(':USER_ID', $user_id, PDO::PARAMS_STR);
    $stmt->bindValue(':USER_NAME', $user_name, PDO::PARAMS_STR);
    $stmt->bindValue(':PASSWORD', $password, PDO::PARAMS_STR);

    //　実行
    $stmt->execute();

    // 結果を取得する
    $row = $prepare->fetch();

} catch (PDOException $e) {

    // エラー処理

}
~~~