また、ノリでやってしまったが。
(ノ・・)ン。。。。。。(((●コロコロッ
PHP5.1を使って数周週間ほど経つ。
毎日が良くも悪くも驚きの連続なのだが、まぁそれはいいとして、
PHP5.1といえばPECL::PDOなのだが、
これがなかなか良い感じだ。
動作速度が良く取りだたされるが、
一番の利点はPrepare Statementだと思う。
折角PHP5.1を使っているのだから、
メインフレームワークのEthnaでPDOを使ってみることにした。
まずは設定ファイルから
注意しなければならいのが、
DSNはPEAR::DB用のものを使うと言うこと。
Ethnaの仕様上こうするしかないので。
/path/to/project/etc/project-ini.php $config = array( 'dsn' = 'mysql://user:pass@localhost/db', );
次にファイルをダウンロードする。
Class/DBディレクトリ格納。
http://dozo.rgr.jp/Ethna_DB_PDO.tar.gz
次にファイルのrequireを設定
/path/to/Ethna/Ethna.php include_once(ETHNA_BASE . '/class/DB/Ethna_DB_PDO.php');
最後に各プロジェクトのControllerの$classプロパティを書き換えれば使用できる。
/path/to/project/app/procject_Controller.php var $class = array( 'db' => 'Ethna_DB_PDO', );
適当なactionやviewで、
$dbObj = & $this->backend->getDB(); $dbObj->setFetchMode( PDO::FETCH_OBJ ); $dbObj->query( " SELECT rand(); " ); print_r( $dbObj->fetchAll() );
で取得できます
$dbObj = & $this->backend->getDB(); $dbObj->begin(); // start transaction $table_name = "table1"; $queryArray = array( "key1" => "value1", "key2" => 3, ); $dbObj->insert( $table_name, $queryArray ); // insert $dbObj->commit();
トランザクション中でエラーが発生すると自動でrollbackが走ります。
ただし、rollbackが一度でも発生すると、
そのコネクション中の以降の更新はすべて無効になります。
$dbObj = & $this->backend->getDB(); $dbObj->begin(); // start transaction $table_name = "table1"; $queryArray = array( "key1" => "value1", "key2" => 3, ); $whereArray = array( "key3" => 3, ); $dbObj->update( $table_name, $queryArray ); $dbObj->commit();
Where句は=(イコール)にしか対応していません
$dbObj = & $this->backend->getDB(); $dbObj->begin(); // start transaction $table_name = "table1"; $whereArray = array( "key3" => 3, ); $dbObj->delete( $table_name, $whereArray ); $dbObj->commit();
Where句は=(イコール)にしか対応していません
基本的にエラーが出た場合、
例外が発生するようになっている。
isError, raiseErrorのたぐいは利用しない方向性だ。
catchするポイントは、
マウントポイントということになるだろうか。
errorというアクションがあったとすると
try{ Main_Controller::main('Main_Controller', 'index'); } catch( PDOException $errorObj ){ Main_Controller::main('Main_Controller', 'error', 'undef'); }
と言った具合にしておけば、
どの場所でどんなエラーが発生したときでも、
エラーメッセージだけthrowしておけば、
エラー処理をいちいち書かなくて済む。
汚らしくていやだと思う人は、
Controllerオブジェクトでmainメソッドをオーバーライドするのがいい。
function main($class_name, $action_name = "", $fallback_action_name = "") { $c =& new $class_name; try{ $c->trigger($action_name, $fallback_action_name); }catch( PDOException $errorObj ){ $c->trigger( 'error', $fallback_action_name); } }
と言った感じかな。(No check)
どうしてもisErrorが使いたいときは
ini.phpとかで
define('ETHNADBPDO_EXCEPTIONMODE', false );
としておくと
throwせずにreturnされる。
正直Ethna_DB_PEARとかでは機能不足過ぎてで使い物にならない。
Ethna_DB_PDOにはEthan_DB等にはない独自拡張がされている。
設定ファイルproject_ini.phpで設定できる。
PEAR::DBには実装されているquoteSmart。
値の型によってquote結果を変えるもの。
$quotedString = $dbObj->quoteSmart( 'hoge' );
PDOのStatementはデフォルトではPDO_Statementクラスが用いられる。
これを独自に拡張したものを利用することが出来る。
class PDO_HogeStatement extends PDO_Statement { }
という感じでStatementオブジェクトを拡張しておき、
設定ファイルproject_ini.phpに
"statement_class" => "PDO_HogeStatement",
を追加しておくと、
デフォルトでPDO_HogeStatement?が利用される。
MySQLの4.1以降ではコネクションを張ってからキャラクタセットの変更を行うのだが、
それをあらかじめ設定ファイルで設定することが出来る。
それがinitial_statement。
設定ファイルproject_ini.phpに
"initial_statement" => "SET NAMES 'ujis'",
としておけば、
接続した直後にキャラクタセットを変更するなどが可能になる。
PDOのデフォルトフェッチモードはPDO::FETCH_BOTH。
配列とハッシュがごちゃ混ぜになって出てくるわけだ。
使い勝手に問題があると思った場合は、
これを変更することが出来る。
設定ファイルproject_ini.phpに
"default_fetchmode" => PDO::FETCH_OBJ,
としておけば、
デフォルトでオブジェクトの配列が取得できる。
setFetchMode?というメソッドを利用することで、
設定を変更できる。
また、上記の用にFETCH_OBJに変更した場合、
stdClassの配列が返ってくる。
stdClassは空のクラスで実際なにも使えない。
これをあらかじめ用意したクラスに設定することが出来る。
設定ファイルproject_ini.phpに
"default_fetchclass" => "dbClass",
としておけば、
独自作成したdbClassというクラスが利用できる。
setFetchClass?というメソッドを利用することで、
設定を変更できる。
仕組み上こうならざるを得なかったのでご理解頂きたい。
というのもPDOのDSNはPEAR::DB等と互換性がない。
さらに、ユーザー名とパスワードはDSNに含まないので、
DSNとは別にそれを設定する項目を用意しなければいけない。
そうなると、特定のキーでデータベースを振り分けることが出来るEthnaの良さが無くなってしまうのだ。
これはどうすればいいのだろうか。
EthnaはPEAR::DBをベースに開発された。
PEARライブラリには必ずraiseErrorとisErrorが存在する。
raiseErrorでエラーオブジェクトを返し、
その一つ上のメソッド内でisErrorを受けて処理をする。
だが、PHP5で例外という概念ができた。
メソッド内でエラーが発生した場合、
エラーオブジェクトが投函される。
それを最も上のメソッド内で受けてエラー処理をする。
エラー処理が少なくて済むのでできれば例外を使いたいのだが、
Ethnaの仕組み上エラーオブジェクトを返さなければいけない。
エラーオブジェクトを返すとエラーの投函が出来ない。
この二つの相反する概念はどう棲み分けすればいいのか。
・・・やっぱりraiseError側に集約させるしかないのか。