PHPプログラミング

最終更新日:
公開日:

レシピ

データベース PDO

PDOについてと基本操作

様々な種類のデータベースへアクセスするための手段としてPHPが標準で用意しているPDO(PHP Data Objects)。今回はこのPDOがどんなものかご紹介します。

この記事のポイント

  • PDOを使うメリット/デメリットが分かる
  • PDOを使ったデータベースの基本的な操作を解説

目次

データベースの各種操作に便利なPDO

今回はPDOについて紹介をしていきます。
次のような方が対象です。

  • PDOってどんなもの?なにができるの?
  • PDOを使うメリット・デメリットを知りたい
  • PDOを使ってデータベースへ接続したい

Note

本記事の「データベース」とはMariaDBやMySQLなどの「リレーショナルデータベース」を指しています。

PDOについて

PDOは「PHP Data Objects」の略で、PHPからデータベースへ簡単にアクセスするための拡張モジュールです。
PHP 5.1以降から標準でバンドルされるようになり、現在主流のバージョンであればほぼ使うことができるようになっています。

PDOを使うメリットは、データベースの種類やバージョンの違いを意識せずにコードを書けることです。
データベースの接続部分など部分的にはデータベースごとにコードを変更する必要がありますが、基本的なSQLの実行、トランザクション、プリペアドステートメントを使うなど基本的な操作については共通のコードを使うことができます。

PDOから様々なデータベースへアクセスするイメージ

続いて、PDOを使うメリットとデメリットを紹介します。

PDOを使うメリット

データベースのバージョンの違いを吸収できる

プログラミング言語やデータベースは日々改良されているため頻繁にバージョンアップされていきます。
細かい修正のマイナーアップデートなら特に問題にならないことが多いですが、時々メジャーアップデートがあり、用意された関数に変更があったり、そもそもコードの書き方自体が変わることがあります。
その場合、現在動いているシステムについてもコード修正する必要が発生します。

具合的な例でいうと、PHPには元々MySQL関数の拡張モジュールが用意されていました。
そのモジュールでは「mysql_connect関数」「mysql_query関数」という関数が用意されていました。
しかし、次世代のMySQLにアクセスする拡張モジュールとして「MySQLi」が登場し、従来のMySQL拡張モジュールはPHPバージョン5.5で非推奨になり、7.0で削除されました。

MySQL拡張モジュールからMySQLi拡張モジュールへの変更で大きな点はオブジェクトベースになったところです。
変更を最小限にできるよう、オブジェクトを使わない関数のみを使った使用方法もありますが、関数に渡すパラメータの順番に変更があったりするため、「今までのコードをそのまま使い回す」という感覚では難しい点がありました。

このような変更があるとPHPやMySQLのバージョンを更新するごとにコードも更新する必要が生じます。
しかしPDOを使っているとPDOがバージョンの違いを吸収してくれるため、バージョンアップごとにコード修正する手間を省くことができます。

データベースの種類の違いを吸収できる

システムを運用していると技術的な潮流やシステム周りの事情は変化していきます。
将来的にはOSやソフトウェアの大きなアップデートが起こったり、システムをメンテナンス・改修するスタッフ(請負会社)の変更があります。
そのような中で、システムが使用するデータベース自体を変更する可能性もあります。

データベースを変更するときは、もちろんデータベースへアクセスするコードを修正しなければなりません。
そういった修正を必要最小限に抑える部分でもPDOは活躍します。

PDOのデメリット

データベースの機能をフルに扱うことができない

データベースはたくさんの種類がありますが、それぞれが独自の特徴・機能を持っています。
PDOは全てのデータベースが持つ基本的な機能については共通化することができますが、データベース独自の機能は使えないことがあります。

もう少し具体的に、それぞれのデータベースで異なる点を挙げてみます。

  • データの「型」の種類や扱い方
  • テーブル構造の扱い
  • 検索(インデックス関連)
  • 複雑なSQL

PDOも万能ツールではありません。
しかし、基本的なSQL(C:登録、R:取得、U:更新、D:削除)はデータベースの違いを超えて共通化されているため、データベース特有の機能を使いたい場合を除いてはPDOは有用な手段です。

以降はPDOとMySQLを使って、基本的な操作であるテーブルの作成と削除、データの登録/更新/削除/取得をそれぞれ実行して解説していきます。

データベースに接続

PDOオブジェクトのインスタンスを作成して、データベース「test」に接続します。

コード例

try {

  // (1) データベースに接続
  $pdo = new PDO('mysql:charset=UTF8;dbname=test;host=localhost', 'username', 'password');

} catch(PDOException $e) {

  // (2) エラーメッセージを出力
  echo $e->getMessage();

} finally {

  // (3) データベースの接続解除
  $pdo = null;
}

new PDO()を実行するときにパラメータを3つ渡しています。
1つ目はデータベースの種類「mysql」、文字コード「charset=UTF8」、ホスト「host=localhost」と複数の情報を指定することができます。

2つ目はデータベースに接続するユーザー名、3つ目はパスワードを指定します。
今回は設定していませんが、4つ目にオプションを指定することもできます。

データベースに無事接続するとPDOオブジェクトのインスタンスが作成されますが、もし接続できなかった場合はエラーが発生するようになっています。
そのため、PDOに関する処理は上記のようにtry文で囲むと、エラーが発生したときにエラーコードを含むメッセージを取得して原因を知ることができます。

エラーメッセージはPDOExceptiongetMessageメソッドで取得します。
例えばユーザー名が正しくないときなどは以下のメッセージを取得できます。

SQLSTATE[HY000] [1045] Access denied for user 'testuser'@'localhost' (using password: YES)

接続するデータベースの種類によって、接続に必要な情報が異なります。
以下の例ではPostgreSQLとSQLiteに接続するコード例です。

PostgreSQLに接続するコード例

$pdo = new PDO('pgsql:dbname=test;options=\'--client_encoding=UTF8\';host=localhost', 'username', 'password');

SQLiteに接続するコード例

$pdo = new PDO('sqlite:./sqlite/test.sqlite3');

データベースの接続解除

データベースとの接続を解除するときは、接続したときに作成したPDOオブジェクトのインスタンスを削除します。
以下のコードでは変数$pdonullを代入することでデータベースの接続を解除します。

コード例

try {

  // (1) データベースに接続
  $pdo = new PDO('mysql:charset=UTF8;dbname=test;host=localhost', 'username', 'password');

} catch(PDOException $e) {

  // (2) エラーメッセージを出力
  echo $e->getMessage();

} finally {

  // (3) データベースの接続解除
  $pdo = null;
}

トランザクション

PDOでトランザクションを使うときは次の3つのメソッドを使って実装します。

  • beginTransactionメソッド – トランザクションを開始する
  • commitメソッド – 処理の確定
  • rollBackメソッド – 処理のキャンセル

まずはbeginTransactionメソッドを実行してトランザクションを開始し、全て正常にクエリを実行できたらcommitメソッドを実行します。
もし反対にクエリの途中でエラーが発生したときはrollBackメソッドを実行して、データベースをクエリ実行前の状態に戻します。

コード例

try {

  // (1) データベースに接続
  $pdo = new PDO('mysql:charset=UTF8;dbname=test;host=localhost', 'usename', 'password');

  // (2) トランザクション開始
  $pdo->beginTransaction();

  // (3) データを更新するSQL
  $stmt = $pdo->prepare('UPDATE users SET email = :email, tel = :tel WHERE id = :id');

  // (4) 値をセット
  $stmt->bindParam( ':id', $id, PDO::PARAM_INT);
  $stmt->bindParam( ':email', $email, PDO::PARAM_STR);
  $stmt->bindParam( ':tel', $tel, PDO::PARAM_STR);

  // (5) SQL実行
  $res = $stmt->execute();

  // (6) コミット
  if( $res ) {
    $pdo->commit();
  }

} catch(PDOException $e) {

  // (7) エラーメッセージを出力
  echo $e->getMessage();
  
  // (8) ロールバック
  $pdo->rollBack();

} finally {

  // (9) データベースの接続解除
  $pdo = null;
}

新しくテーブルを作成

データベース「test」に下記の構造を持つ新しい「users」というテーブルを作成します。

作成するデータベースの構造

 名前        | データ型      | 照合順序              | NULL   | その他
------------+--------------+---------------------+--------+---
 id         | int(11)      |                     | いいえ  | AUTO_INCREMENT
 first_name | varchar(100) | utf8mb4_general_ci  | いいえ  |
 last_name  | varchar(100) | utf8mb4_general_ci  | いいえ  |
 email      | varchar(100) | utf8mb4_general_ci  | いいえ  |
 tel        | varchar(20)  | utf8mb4_general_ci  | はい   |

コード例

<?php

try {

  // (1) データベースに接続
  $pdo = new PDO('mysql:charset=UTF8;dbname=test;host=localhost', 'username', 'password');

  // (2) トランザクション開始
  $pdo->beginTransaction();

  // (3) テーブルを作成するSQL
  $stmt = $pdo->prepare('CREATE TABLE users ( 
    id   INT AUTO_INCREMENT,
    first_name  VARCHAR(100) NOT NULL, 
    last_name   VARCHAR(100) NOT NULL,
    email   VARCHAR(100) NOT NULL,
    tel   VARCHAR(20) NULL,
    PRIMARY KEY(id)
  )');

  // (4) SQL実行
  $res = $stmt->execute();

  // (5) コミット
  if( $res ) {
    $pdo->commit();
  }

} catch(PDOException $e) {

  // (6) エラーメッセージを出力
  echo $e->getMessage();
  
  // (7) ロールバック
  $pdo->rollBack();

} finally {

  // (8) データベースの接続解除
  $pdo = null;
}

もし同じ名前のテーブルがすでに存在する場合は(4)のexecuteメソッドの返り値はfalseになります。
この時点でテーブルの作成は失敗しますが、続く(5)でもcommitメソッドは実行されません。

テーブルを削除

データベースtestからusersテーブルを削除します。
テーブル内にデータが入っていても削除は実行されます。

コード例

<?php

try {

  // (1) データベースに接続
  $pdo = new PDO('mysql:charset=UTF8;dbname=test;host=localhost', 'username', 'password');

  // (2) トランザクション開始
  $pdo->beginTransaction();

  // (3) テーブルを削除するSQL
  $stmt = $pdo->prepare('DROP TABLE users2');

  // (4) SQL実行
  $res = $stmt->execute();

  // (5) コミット
  if( $res ) {
    $pdo->commit();
  }

} catch(PDOException $e) {

  // (6) エラーメッセージを出力
  echo $e->getMessage();
  
  // (7) ロールバック
  $pdo->rollBack();

} finally {

  // (8) データベースの接続解除
  $pdo = null;
}

データを新規登録

データベースtestにあるusersテーブルへ新しくデータを登録します。

コード例

<?php

// (1) 登録する値を設定
$first_name = '太郎';
$last_name = '佐藤';
$email = 'sato@gray-code.com';
$tel = '080-xxxx-xxxx';

try {

  // (2) データベースに接続
  $pdo = new PDO('mysql:charset=UTF8;dbname=test;host=localhost', 'username', 'password');

  // (3) トランザクション開始
  $pdo->beginTransaction();

  // (4) データを登録するSQL
  $stmt = $pdo->prepare('INSERT INTO users (
    first_name, last_name, email, tel
  ) VALUES (
    :first_name, :last_name, :email, :tel
  )');

  // (5) 値をセット
  $stmt->bindParam( ':first_name', $first_name, PDO::PARAM_STR);
  $stmt->bindParam( ':last_name', $last_name, PDO::PARAM_STR);
  $stmt->bindParam( ':email', $email, PDO::PARAM_STR);
  $stmt->bindParam( ':tel', $tel, PDO::PARAM_STR);

  // (6) SQL実行
  $res = $stmt->execute();

  // (7) コミット
  if( $res ) {
    $pdo->commit();
  }

} catch(PDOException $e) {

  // (8) エラーメッセージを出力
  echo $e->getMessage();
  
  // (9) ロールバック
  $pdo->rollBack();

} finally {

  // (10) データベースの接続解除
  $pdo = null;
}

データを更新

データベースtestusersテーブルに入っている特定のデータを更新します。
今回はidカラムに1が入っているデータのemailカラムとtelカラムのデータを更新していきます。

コード例

<?php

// (1) 登録する値を設定
$id = 1;
$email = 'sato@gray-code.co.jp';
$tel = '090-11xx-xxxx';

try {

  // (2) データベースに接続
  $pdo = new PDO('mysql:charset=UTF8;dbname=test;host=localhost', 'username', 'password');

  // (3) トランザクション開始
  $pdo->beginTransaction();

  // (4) データを更新するSQL
  $stmt = $pdo->prepare('UPDATE users SET email = :email, tel = :tel WHERE id = :id');

  // (5) 値をセット
  $stmt->bindParam( ':id', $id, PDO::PARAM_INT);
  $stmt->bindParam( ':email', $email, PDO::PARAM_STR);
  $stmt->bindParam( ':tel', $tel, PDO::PARAM_STR);

  // (6) SQL実行
  $res = $stmt->execute();

  // (7) コミット
  if( $res ) {
    $pdo->commit();
  }

} catch(PDOException $e) {

  // (8) エラーメッセージを出力
  echo $e->getMessage();
  
  // (9) ロールバック
  $pdo->rollBack();

} finally {

  // (10) データベースの接続解除
  $pdo = null;
}

データを削除

データベースtestusersテーブルに入っている特定のデータを削除します。
今回はidカラムに3が入っているデータを削除していきます。

コード例

<?php

// (1) 削除するデータを特定するための値を設定
$id = 3;

try {

  // (2) データベースに接続
  $pdo = new PDO('mysql:charset=UTF8;dbname=test;host=localhost', 'username', 'password');

  // (3) トランザクション開始
  $pdo->beginTransaction();

  // (4) データを削除するSQL
  $stmt = $pdo->prepare('DELETE FROM users WHERE id = :id');

  // (5) 値をセット
  $stmt->bindParam( ':id', $id, PDO::PARAM_INT);

  // (6) SQL実行
  $res = $stmt->execute();
  var_dump($res);

  // (7) コミット
  if( $res ) {
    $pdo->commit();
  }

} catch(PDOException $e) {

  // (8) エラーメッセージを出力
  echo $e->getMessage();
  
  // (9) ロールバック
  $pdo->rollBack();

} finally {

  // (10) データベースの接続解除
  $pdo = null;
}

データを全件取得

データベースtestusersテーブルに入っている全てのデータを取得します。

コード例

<?php

try {

  // (1) データベースに接続
  $pdo = new PDO('mysql:charset=UTF8;dbname=test;host=localhost', 'username', 'password');

  // (2) データを取得するSQL
  $stmt = $pdo->prepare('SELECT * FROM users');

  // (3) SQL実行
  $res = $stmt->execute();
  // var_dump($res);

  // (4) データを先頭から順に出力
  while($data = $stmt->fetch()) {
    var_dump($data['first_name']);
  }

} catch(PDOException $e) {

  // (5) エラーメッセージを出力
  echo $e->getMessage();

} finally {

  // (6) データベースの接続解除
  $pdo = null;
}

(4)のfetchメソッドは取得したデータを先頭から順に1つずつ取得することができます。
そのため、while文は取得したデータの数だけループを繰り返してfirst_nameカラムのデータをvar_dump関数で出力します。

条件に合うデータのみ取得

データベースtestusersテーブルに入っているデータから、指定した条件に合うデータのみ取得します。
以下の例ではidカラムの値が2より大きく、かつtelカラムがNULLでないデータを全て取得します。

コード例

<?php

try {

  // (1) データベースに接続
  $pdo = new PDO('mysql:charset=UTF8;dbname=test;host=localhost', 'username', 'password');

  // (2) データを取得するSQL
  $stmt = $pdo->prepare('SELECT * FROM users WHERE 2 < id AND tel IS NOT NULL');

  // (3) SQL実行
  $res = $stmt->execute();
  // var_dump($res);

  // (4) データを先頭から順に出力
  while($data = $stmt->fetch()) {
    var_dump($data['first_name']);
  }

} catch(PDOException $e) {

  // (5) エラーメッセージを出力
  echo $e->getMessage();

} finally {

  // (6) データベースの接続解除
  $pdo = null;
}

取得したデータを並び替え

先ほどの取得する条件(WHERE句)に加えて、ORDER BY句を使って取得したデータを並び替えます。
今回はidカラムの値が大きい順になるよう指定しています。

コード例

<?php

try {

  // (1) データベースに接続
  $pdo = new PDO('mysql:charset=UTF8;dbname=test;host=localhost', 'username', 'password');

  // (2) データを取得するSQL
  $stmt = $pdo->prepare('SELECT * FROM users WHERE 2 < id ORDER BY id DESC');

  // (3) SQL実行
  $res = $stmt->execute();
  // var_dump($res);

  // (4) データを先頭から順に出力
  while($data = $stmt->fetch()) {
    var_dump($data['first_name']);
  }

} catch(PDOException $e) {

  // (5) エラーメッセージを出力
  echo $e->getMessage();

} finally {

  // (6) データベースの接続解除
  $pdo = null;
}

もし値の小さい順(昇順)に並び替えたいときはDESCASCに変更してください。

取得するデータの最大件数を指定

取得するデータの件数をあらかじめ指定するときはLIMIT句を使います。
今回は最大件数を5件に設定しています。

コード例

<?php

try {

  // (1) データベースに接続
  $pdo = new PDO('mysql:charset=UTF8;dbname=test;host=localhost', 'username', 'password');

  // (2) データを取得するSQL
  $stmt = $pdo->prepare('SELECT * FROM users WHERE 2 < id ORDER BY id DESC LIMIT 5');

  // (3) SQL実行
  $res = $stmt->execute();
  // var_dump($res);

  // (4) データを先頭から順に出力
  while($data = $stmt->fetch()) {
    var_dump($data['first_name']);
  }

} catch(PDOException $e) {

  // (5) エラーメッセージを出力
  echo $e->getMessage();

} finally {

  // (6) データベースの接続解除
  $pdo = null;
}

こちらの記事は役に立ちましたか?

ありがとうございます。
もしよろしければ、あわせてフィードバックや要望などをご入力ください。

コメントありがとうございます!
運営の参考にさせていただきます。