PHP入門 データベース 結果セットの取得
結果セットとは、SQLのSELECT命令によって、1個または複数のテーブルから取り出されたレコード群を保持するために、メモリ上に用意された仮想テーブルのことを言います。
1個のテーブルをまるごと結果セットとして取り出すこともありますが、テーブルの列または行を部分的に取り出したり、複数のテーブルを結合(加工)したりするのが一般的です。
結果セットの取得方法
例:結果セット取得
<?php
require_once './DbManager.php';
require_once './Encode.php';
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>PHPサンプル</title>
</head>
<body>
<table class="table">
<thead>
<tr>
<th>ISBNコード</th>
<th>書名</th>
<th>価格</th>
<th>出版社</th>
<th>刊行日</th>
</tr>
</thead>
<tbody>
<?php
try {
// データベースへの接続を確立
$db = getDb();
// SELECT命令の実行
$stt = $db->prepare('SELECT * FROM book ORDER BY published DESC');//➊
$stt->execute(); //➊
// 結果セットの内容を順に出力
while($row = $stt->fetch (PDO::FETCH_ASSOC)) {
?>
<tr>
<td><?=e($row['isbn']) ?></td>
<td><?=e($row['title']) ?></td>
<td><?=e($row['price']) ?></td>
<td><?=e($row['publish']) ?></td>
<td><?=e($row['published']) ?></td>
</tr>
<?php
}
} catch (PDOException $e) {
die("エラーメッセージ: {$e->getMessage()}");
}
?>
</tbody>
</table>
</body>
</html>
結果セットを取得するには、まず(❶)prepare/executeメソッドを利用します。executeメソッドは、取得した結果セットを自分自身(PDOStatementオブジェクト)にセットします。
結果セットを取得できたら、結果セットは2次元表のため、行単位に読み出す必要があります。現在読み込み可能なレコードを表す内部的な目印のことをレコードポインター、また、レコードポインターが示す現在行のことをカレントレコードと言います。PDOStatementオブジェクトには、レコードポインターを次のレコードに移動しながら、移動した先のレコードを読み込むための便利なメソッドが用意されています。それがfetchメソッドです。なお、結果セットから個々の行を取り出すことをフェッチすると言います。
レコードポインターが次のレコードに移動できない場合は、fetchメソッドはfalseを返します。whileループを使用して、結果セット内のレコードを抽出しています。
構文:fetchメソッド
PDOStatement::fetch([int $style = PDO::FETCH_BOTH]):mixed
$style | フェッチモード |
フェッチモードとは、結果セットから取り出したデータをどのような形式の変数に格納するかを表すパラメータです。
主なフェッチモード(fetchメソッドの引数)
定数 | 取得の形式 | 値の取得(例) |
---|---|---|
PDO::FETCH_ASSOC | 連想配列 | $row[‘name’] |
PDO::FETCH_NUM | 一般配列 | $row[0] |
PDO::FETCH_BOTH | 一般配列/連想配列 | $row[0]/$row[‘name’] |
PDO::FETCH_NAMED | 連想配列 | $row[‘name’] |
PDO::FETCH_OBJ | オブジェクト | $row->name |
PDO::FETCH_COLUMN | スカラー値 | $row |
PDO::FETCH_BOUND | bindColumnメソッドでバインドされたPHP変数 | $name |
PDO::FETCH_CLASS | 指定されたフェッチ | $row->name |
サンプルでは、PDO::FETCH_ASSOCでフェッチしているので、個別のフィールドには$row[‘name’]のようにアクセスできます。フェッチモードは、fetchメソッドで個別に宣言する他にも、setFetchModeメソッドで指定することもできます。
$stt->setFetchMode(PDO::FETCH_ASSOC);
結果セットはforeachでも処理できる
結果セットは反復可能なオブジェクトなので、foreachメソッドでも処理することもできます。フェッチモードを変更する必要があるので、foreachループ前でsetFetchModeメソッドを呼び出します。
$stt->setFetchMode(PDO::FETCH_ASSOC);
foreach($stt as $row){
・
・
・
$stt = $db->query('SELECT * FROM book ORDER BY published DESC');
フェッチメソッド
データベースから得られた結果セットの内容を取得するために、PDOではさまざまなフェッチメソッドを用意しています。
fetchAllメソッド
fetchAllメソッドを利用することで、結果セットの内容をまとめて配列に変換できます。
構文:fetchAllメソッド
PDOStatement::fetchAll([int $fetch_style = PDO::FETCH_BOTH]): array|false
$fetch_style | フェッチモード |
結果セットの内容を素早く確認したいケースなどに使用します。(配列であれば、print_r関数で表示できます)
<?php
require_once './DbManager.php';
$db = getDb();
$stt = $db->query('SELECT * FROM book');
print_r($stt->fetchAll(PDO::FETCH_ASSOC));
fetchColumnメソッド
fetchColumnメソッドは、結果セットの先頭列のみを取得します。
構文:fetchColumnメソッド
PDOStatement::fetchColumn([int $column_number = 0]): mixed
$column_number | 取得する列のインデックス番号(先頭列は0) |
結果セットの内容が1列しかない(または1行1列しかない)ことが最初からわかっている場合に、このメソッドを利用します。
<?php
require_once './DbManager.php';
$db = getDb();
$stt = $db->query('SELECT count(*) FROM book');
print "件数:{$stt->fetchColumn()}";
結果セットに複数の列が含まれる場合にも、fetchColumnメソッドは既定で先頭行を取得します。
fetchObjectメソッド
fetchObjectメソッドは、フェッチした結果を指定のオブジェクトのプロパティにセットするメソッドです。
構文:fetchObjectメソッド
PDOStatement::fetchObject([string $class_name = "stdClass"[, array $ctor_args]]): object|false
$class_name | フェッチ先となるクラス名。既定では標準クラス |
$ctor_args | クラスおwインスタンス化する際に引き渡すパラメーター |
bookテーブルから取得した値を、Bookクラスに渡す例です。Bookクラスには、インスタンス化のタイミングで値引き率を渡せるものとします。また、値引き後の価格はBookクラスのdiscountメソッドで取得できるものとします。
fetchObjectメソッドは、フェッチモードFETCH_OBJ/FETCH_CLASSで代替することもできます。ただし、クラス名を明示する場合には、setFetchModeメソッドであらかじめ宣言する必要があります。
$stt->setFetchMode(PDO::FETCH_CLASS, 'Book', [ 0.1 ]);
if ($row = $stt->fetch()){
sample1.php
<?php
require_once './DbManager.php';
require_once 'Book.php';
$db = getDb();
$stt = $db->query("SELECT * FROM book where isbn='4768320001'");
if($row = $stt->fetchObject('Book', [0.1])){
print "{$row->title}:{$row->discount()}円";
}
book.php
<?php
class Book{
//フィールド値を設定/取得するためのプロパティ
public $isbn;
public $title;
public $price;
public $publish;
public $published;
public $_rate;
//インスタンス化時に値引き率をセット
public function __construct(float $rate){
$this->_rate = $rate;
}
public function discount():float{
return floor($this->price * (1 - $this->_rate));
}
}
フェッチモードの捕捉
fetch/fetchAllなどのメソッドでは、フェッチに際してレコードの取得形式(フェッチモード)を指定できます。利用可能なフェッチモードの捕捉について記載します。
PDO::FETCH_ASSOC/PDO::FETCH_NAMED
PDO::FETCH_ASSOC/PDO::FETCH_NAMEDは、どちらもレコードを連想配列の形式で取得するという意味では共通しています。両者が異なるのは、テーブル連結などでフィールド名が重複した場合です。PDO::FETCH_ASSOCではフィールド名が重複した場合、先に登場したフィールドが後ろのフィールドで上書きされます。それに対して、PDO::FETCH_NAMEDでは、入れ子の配列として双方の値を返します。
PDO::FETCH_BOUND
PDO::FETCH_BOUNDは、フェッチした各フィールドの値を、あらかじめマッピングしておいた変数に設定します。マッピングに際してはデータ型の定義もできるので、主にデータベースから取得した値を厳密な型指定の下で操作したいようなケースで使用します。
photoテーブル
フィールド名 | データ型 | 概要 |
---|---|---|
id | INT | 写真ID(主キー/自動連番) |
type | VARCHAR(50) | ファイルの種類 |
data | MEDIUMBLOB | 写真データ |
フェッチモードPDO::FETCH_BOUNDを使用する場合は、あらかじめbindColumnメソッドで取得列と変数のマッピングを宣言しておく必要があります。
例:データベースに登録されている画像データをブラウザに表示する
<?php
require_once './DbManager.php';
try {
// データベースへの接続を確立
$db = getDb();
// クエリ情報idをキーに、photoテーブルから画像データを取得(無指定の場合は1)
$stt = $db->prepare('SELECT * FROM photo WHERE id = ?');
$stt->bindValue(1, $_GET['id'] ?: 1);
$stt->execute();
//取得列と変数とをマッピング (type列は変数$type、data列は$dataに割り当て)
$stt->bindColumn('type', $type, PDO::PARAM_STR);
$stt->bindColumn('data', $data, PDO::PARAM_LOB);
// フェッチモード PDO::FETCH_BOUNDでレコードを取得
if ($stt->fetch(PDO::FETCH_BOUND)) {
// 取得に成功した場合は、ブラウザーに取得データを出力
header("Content-Type: {$type}");
print $data;
} else {
// 該当するレコードが存在しない場合はエラーメッセージを表示
print '該当するデータがありません。 ';
}
} catch (PDOException $e) {
die("エラーメッセージ: {$e->getMessage()}");
}
構文:bindColumnメソッド
PDOStatement::bindColumn(mixed $column, mixed &$param[, int $type [, int $maxlen]]): bool
$column | フィールド名またはインデックス番号(1スタート) |
&$param | フィールド値をバインドする変数 |
$type | データ型 |
$maxlen | データの最大長 |
引数$typeに指定できるデータ型
パラメーターのデータ型(bindValueメソッド)
定数 | 概要 |
---|---|
PDO::PARAM_BOOL | 真偽型 |
PDO::PARAM_NULL | NULL型 |
PDO::PARAM_INT | 整数型 |
PDO::PARAM_STR | 文字列型(既定) |
PDO::PARAM_LOB | ラージオブジェクト型 |
PDO::PARAM_STMT | SQLクエリ |
PDO::PARAM_INPUT_OUTPUT | 入出力パラメーター(「PDO::PARAM_INT | PDO::PARAM_INPUT_OUTPUT」のように他の定数と組み合わせでのみ使用) |
データ型を明示した場合、PDOはフェッチに際して取得列が指定されたデータ型であること前提にバインド処理します。bindColumnメソッドで変数の割り当てができてしまえば、あとはfetchメソッドで実際にレコードをフェッチするだけです。
PDO::FETCH_UNIQUE/PDO::FETCH_GROUP
フェッチモードPDO::FETCH_UNIQUE/PDO::FETCH_GROUPは、fetchAllメソッドとフェッチモードPDO::FETCH_COLUMNとの組み合わせで利用できる特殊なフェッチモードです。
PDO::FETCH_UNIQUE/PDO::FETCH_GROUPは、いずれも取得した結果セットの先頭列がキーに、2列目が値となるよに連想配列として、結果セットをフェッチします。両者の違いは、キーの値が重複している場合です。キーの値が重複している場合、PDO::FETCH_UNIQUEでは最後の値だけが結果に表れますが、PDO::FETCH_GROUPではすべての値を入れ子の配列として返します。