前回作成したDBクラスはシンプルに書くことに努めた結果、足りない部分があったのでここでフォローしていきたいと思います。
ひとまず改善すべき点を整理します。
1.fetchモードで無駄なデータセットが発生しないよう、「カラム名」のみで返すよう指定する。
2.大規模システムにありがちな接続ホストやスキーマがいくつも分かれてる場合においても汎用的に使いやすくする工夫。
3.「操作」をするメソッドをもっとシンプルに使えるようオーバーライドする。
※オーバーライド = 継承メソッドの上書き実装
前回、「操作」は継承したままをそのまま使う形だったので、もっとシンプルになるよう工夫します。
具体的には接続時に返される「PDOオブジェクト」と、SQL実行時に返される「PDOStatementオブジェクト」とに分かれてるのが面倒なので統一化を試みます。
つまり$obj = new DB()で取得したオブジェクトですべて操作できるようにしようという試みです。[$obj->Method()形式]
ではでは書いていきましょ。
var_dumpの結果
はいっ!
狙い通り今度は1要素が返ってきました。改善点1が成功したということです。
コンストラクタ内に以下を追加することでfetchモード変更を行っています。
同じ場所に以下を追加してるのは、処理エラーが発生した場合にExceptionをスローさせるようにするためです。
これでエラー発生の可能性がある部分をtry,catchブロックで囲むことでハンドリングが楽になると思います。
では次の改善点2についてですが、大規模システム等にある多ホスト、多スキーマへの対応として_set_info()メソッドをワンクッション入れています。
これによって、このDBクラスを継承した子クラスではコンストラクタ部分をイジらずに_set_info()メソッドをオーバーライドすることで自分の環境に合った調整がしやすくなると思います。
例えばhost1のときはBLOGスキーマに接続して、host2のときはBBSスキーマに接続するといったサービスごとに違った接続先になる場合を考えます。
以下のようにオーバーライドできると思います。
また、HTTPリクエスト先によって接続先を分けるなど自由自在です。
$_SERVER['REQUEST_URI']や$_SERVER['SCRIPT_NAME']などから$host,$dbnameへのセット値を判断するなどなど・・
当然$_SERVER['SERVER_NAME']が"www1.example.com"ならBLOGで、"www2.example.com"ならBBSなどサーバ名で分けることもできますね。
そして改善点3ですが、query()メソッドが返すPDOStatementオブジェクトを使わずにfetch()できるようにしています。
クラス内部に追加した$_sthプロパティに保持させることで内部で管理するようにしています。
query()メソッドから$thisを返すようにすれば以下のようにメソッドチェーンで書くことだってできそうです。
と、これで改善はできました。
でももっと改善できないだろうか?
ということで次回につなげたいと思います。
ひとまず改善すべき点を整理します。
1.fetchモードで無駄なデータセットが発生しないよう、「カラム名」のみで返すよう指定する。
2.大規模システムにありがちな接続ホストやスキーマがいくつも分かれてる場合においても汎用的に使いやすくする工夫。
3.「操作」をするメソッドをもっとシンプルに使えるようオーバーライドする。
※オーバーライド = 継承メソッドの上書き実装
前回、「操作」は継承したままをそのまま使う形だったので、もっとシンプルになるよう工夫します。
具体的には接続時に返される「PDOオブジェクト」と、SQL実行時に返される「PDOStatementオブジェクト」とに分かれてるのが面倒なので統一化を試みます。
つまり$obj = new DB()で取得したオブジェクトですべて操作できるようにしようという試みです。[$obj->Method()形式]
ではでは書いていきましょ。
<?php
// 親クラス
class DB extends PDO
{
protected $_sth = null;
public function __construct($host='', $dbname='') {
list($host, $dbname, $user, $pass) = $this->_set_info($host, $dbname);
try {
parent::__construct("mysql:dbname={$dbname};host={$host}", $user, $pass);
parent::setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
parent::setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
} catch (PDOException $e) {
die($e);
}
}
protected function _set_info($host, $dbname) {
if (empty($host)) $host = 'localhost';
if (empty($dbname)) $dbname = 'mysql';
return array($host, $dbname, 'root', '');
}
public function query($sql) {
$this->_sth = parent::query($sql);
return $this->_sth;
}
/* 継承メソッドと同じなので不要
public function exec($sql) {
return parent::exec($sql);
}
*/
public function prepare($sql) {
$this->_sth = parent::prepare($sql);
return $this->_sth;
}
public function execute($param=array()) {
$this->_sth->execute($param);
return $this->rowCount();
}
public function rowCount() {
return $this->_sth->rowCount();
}
public function fetch() {
return $this->_sth->fetch();
}
public function fetchAll() {
return $this->_sth->fetchAll();
}
}
// 動作確認
$db = new DB();
$db->query("select 1");
$row = $db->fetch();
var_dump($row);
// 親クラス
class DB extends PDO
{
protected $_sth = null;
public function __construct($host='', $dbname='') {
list($host, $dbname, $user, $pass) = $this->_set_info($host, $dbname);
try {
parent::__construct("mysql:dbname={$dbname};host={$host}", $user, $pass);
parent::setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
parent::setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
} catch (PDOException $e) {
die($e);
}
}
protected function _set_info($host, $dbname) {
if (empty($host)) $host = 'localhost';
if (empty($dbname)) $dbname = 'mysql';
return array($host, $dbname, 'root', '');
}
public function query($sql) {
$this->_sth = parent::query($sql);
return $this->_sth;
}
/* 継承メソッドと同じなので不要
public function exec($sql) {
return parent::exec($sql);
}
*/
public function prepare($sql) {
$this->_sth = parent::prepare($sql);
return $this->_sth;
}
public function execute($param=array()) {
$this->_sth->execute($param);
return $this->rowCount();
}
public function rowCount() {
return $this->_sth->rowCount();
}
public function fetch() {
return $this->_sth->fetch();
}
public function fetchAll() {
return $this->_sth->fetchAll();
}
}
// 動作確認
$db = new DB();
$db->query("select 1");
$row = $db->fetch();
var_dump($row);
var_dumpの結果
array(1) {
[1]=>
string(1) "1"
}
[1]=>
string(1) "1"
}
はいっ!
狙い通り今度は1要素が返ってきました。改善点1が成功したということです。
コンストラクタ内に以下を追加することでfetchモード変更を行っています。
parent::setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
同じ場所に以下を追加してるのは、処理エラーが発生した場合にExceptionをスローさせるようにするためです。
これでエラー発生の可能性がある部分をtry,catchブロックで囲むことでハンドリングが楽になると思います。
parent::setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
では次の改善点2についてですが、大規模システム等にある多ホスト、多スキーマへの対応として_set_info()メソッドをワンクッション入れています。
これによって、このDBクラスを継承した子クラスではコンストラクタ部分をイジらずに_set_info()メソッドをオーバーライドすることで自分の環境に合った調整がしやすくなると思います。
例えばhost1のときはBLOGスキーマに接続して、host2のときはBBSスキーマに接続するといったサービスごとに違った接続先になる場合を考えます。
以下のようにオーバーライドできると思います。
protected function _set_info($host, $dbname) {
if ($host === 'host1') $dbname = 'BLOG';
else if ($host === 'host2') $dbname = 'BBS';
return array($host, $dbname, 'root', '');
}
if ($host === 'host1') $dbname = 'BLOG';
else if ($host === 'host2') $dbname = 'BBS';
return array($host, $dbname, 'root', '');
}
また、HTTPリクエスト先によって接続先を分けるなど自由自在です。
$_SERVER['REQUEST_URI']や$_SERVER['SCRIPT_NAME']などから$host,$dbnameへのセット値を判断するなどなど・・
当然$_SERVER['SERVER_NAME']が"www1.example.com"ならBLOGで、"www2.example.com"ならBBSなどサーバ名で分けることもできますね。
そして改善点3ですが、query()メソッドが返すPDOStatementオブジェクトを使わずにfetch()できるようにしています。
クラス内部に追加した$_sthプロパティに保持させることで内部で管理するようにしています。
query()メソッドから$thisを返すようにすれば以下のようにメソッドチェーンで書くことだってできそうです。
$row = $db->query("select 1")->fetch();
と、これで改善はできました。
でももっと改善できないだろうか?
ということで次回につなげたいと思います。