それ程こだわりも無いんだけど、長いこと放置されてきたような気もするし、ADOdb使おうかなと思ったのでやってみた。
結果的にいうと「自分にしては結構大変だわ」でした。
以下、オーバーライドして使うHoge_DB_ADOdbクラスのソースですよ。
まあ、こんな感じ?
今のところちゃんと動いていたEthna_DB_PEARクラスを超参考にしてあるので、自分では使わないメソッドてんこ盛り。
そこはテストもしてないので、ようわかりません。
ポイントとしては、
・getNextId()はADOdbではサポートしてないので、実質放棄
・getInsertId()にpgsqlを対応させるため、引数を変更
・getMetaData()のsqliteはADOdbではサポートしてないので放棄
sqliteに関しては、ちょっと絶望的かもなあ。
あと、Ethna_AppObjectクラスもオーバーライドするのでした。
結果的にいうと「自分にしては結構大変だわ」でした。
以下、オーバーライドして使うHoge_DB_ADOdbクラスのソースですよ。
require_once 'Ethna/class/DB/Ethna_DB_ADOdb.php';
// PEAR:DBに合わせる為(これがないとnotice出します)
define('DB_FETCHMODE_DEFAULT', null);
define('DB_FETCHMODE_ASSOC', null);
/**
* Hoge_DB_ADOdb
*
* EthnaのフレームワークでADOdbオブジェクトを扱うための抽象クラス
*
* @package Ethna
* @author longkey1
* @access public
*/
class Hoge_DB_ADOdb extends Ethna_DB_ADOdb
{
/**#@+
* @access private
*/
/** @var object Ethna_Logger ログオブジェクト */
var $logger;
/** @var object Ethna_AppSQL SQLオブジェクト */
var $sql;
/** @var string DBタイプ(mysql, pgsql...) */
var $type;
/** @var array DSN (DB::parseDSN()の返り値) */
var $dsninfo;
/**#@-*/
/**
* コンストラクタ
*
* @access public
* @param object Ethna_Controller &$controller コントローラオブジェクト
* @param string $dsn DSN
* @param bool $persistent 持続接続設定
*/
function Hoge_DB_ADOdb(&$controller, $dsn, $persistent)
{
parent::Ethna_DB_ADOdb($controller, $dsn, $persistent);
$this->db = null;
$this->logger =& $controller->getLogger();
$this->sql =& $controller->getSQL();
$this->dsninfo = $this->parseDSN($dsn);
$this->dsninfo['new_link'] = true;
$this->type = $this->dsninfo['phptype'];
}
// {{{ getType
/**
* DBタイプを返す
*
* @access public
* @return string DBタイプ
*/
function getType()
{
return $this->type;
}
// }}}
// {{{ getMetaData
/**
* テーブル定義情報を取得する
*
* @access public
* @param string $table テーブル名
* @return mixed array: PEAR::DBに準じたメタデータ Ethna_Error::エラー
*/
function &getMetaData($table)
{
$def =& $this->db->MetaColumns($table);
$deforg = $def;// originalとして退避
if (is_array($def) === false) {
return $def;
}
foreach (array_keys($def) as $k) {
// object型で返してくるので
$def[$k] = array_map('strtolower', (array)$def[$k]);
// type
$type_map = array(
'int' => array(
'int', 'integer', '^int\(?[0-9]\+', '^serial', '[a-z]+int$',
),
'boolean' => array(
'bit', 'bool', 'boolean',
),
'datetime' => array(
'timestamp', 'datetime',
),
);
foreach ($type_map as $convert_to => $regex) {
foreach ($regex as $r) {
if (preg_match('/'.$r.'/', $def[$k]['type'])) {
$def[$k]['type'] = $convert_to;
break 2;
}
}
}
// len
if($def[$k]['max_length'] > 0){
$def[$k]['len'] = $def[$k]['max_length'];
}
// flags
if($def[$k]['primary_key'] == true){
$def[$k]['flags'][] = 'primary_key';
}
if($def[$k]['not_null'] == true){
$def[$k]['flags'][] = 'not_null';
}
switch ($this->type) {
case 'mysql':
case 'pgsql':
// カウンタあるいは自動インクリメントフィールドであれば sequence
if ($this->db->MetaType($deforg[$k], null, true) == 'R') {
$def[$k]['flags'][] = 'sequence';
break;
}
break;
// ADOdbの対応次第かなあ、今のところちと厳しい
// case 'sqlite':
// break;
}
}
return $def;
}
// }}}
// {{{ qstr
/**
* 文字列をクオートする
*
* @access public
* @param string $s クオートする文字列
* @return string クオートされた文字列
*/
function qstr($s, $magic_quotes=false)
{
return $this->db->qstr($s,$magic_quotes);
}
// }}}
// {{{ Ethna_AppObject連携のための実装
// {{{ getNextId
/**
* 直後のINSERTに使うIDを取得する
* (pgsqlのみ対応)
* ※adodbでは特に対応していないのでnullを返す、代わりにgetInsertIDを対応させた
*
* @access public
* @return mixed int
*/
function getNextId($table_name, $field_name)
{
return null;
}
// }}}
// {{{ getInsertId
/**
* 直前のINSERTによるIDを取得する
* (mysql, pgsql, sqlite対応)
* ※pgsql用に引数を追加
* @access public
* @return mixed int:直近のINSERTにより生成されたID null:未サポート
*/
function getInsertId($table_name = null, $field_name = null)
{
if ($this->isValid() == false) {
return null;
} else if ($this->db->Insert_ID($table_name, $field_name) == false) {
return null;
}
return $this->db->Insert_ID($table_name, $field_name);
}
// }}}
// {{{ fetchRow
/**
* DB_Result::fetchRow()の結果を整形して返す
*
* @access public
* @return int 更新行数
*/
function &fetchRow(&$res, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum = null)
{
$row =& $res->FetchRow();
if (is_array($row) === false) {
return $row;
}
if ($this->type === 'sqlite') {
// "table"."column" -> column
foreach ($row as $k => $v) {
unset($row[$k]);
if (($f = strstr($k, '.')) !== false) {
$k = substr($f, 1);
}
if ($k{0} === '"' && $k{strlen($k)-1} === '"') {
$k = substr($k, 1, -1);
}
$row[$k] = $v;
}
}
return $row;
}
// }}}
// {{{ affectedRows
/**
* 直近のクエリによる更新行数を取得する
*
* @access public
* @return int 更新行数
*/
function affectedRows()
{
return $this->db->Affected_Rows();
}
// }}}
// {{{ quoteIdentifier
/**
* dbのtypeに応じて識別子をquoteする
* (配列の場合は各要素をquote)
*
* @access protected
* @param mixed $identifier array or string
*/
function quoteIdentifier($identifier)
{
if (is_array($identifier)) {
foreach (array_keys($identifier) as $key) {
$identifier[$key] = $this->quoteIdentifier($identifier[$key]);
}
return $identifier;
}
$ret = $this->db->nameQuote . $identifier . $this->db->nameQuote;
return $ret;
}
// }}}
// {{{ sqlquery
/**
* SQL文指定クエリを発行する
*
* @access public
* @param string $sqlid SQL-ID(+引数)
* @return mixed DB_Result:結果オブジェクト Ethna_Error:エラー
*/
function &sqlquery($sqlid)
{
$args = func_get_args();
array_shift($args);
$query = $this->sql->get($sqlid, $args);
return $this->_query($query);
}
// }}}
// {{{ sql
/**
* SQL文を取得する
*
* @access public
* @param string $sqlid SQL-ID
* @return string SQL文
*/
function sql($sqlid)
{
$args = func_get_args();
array_shift($args);
$query = $this->sql->get($sqlid, $args);
return $query;
}
// }}}
// }}}
}まあ、こんな感じ?
今のところちゃんと動いていたEthna_DB_PEARクラスを超参考にしてあるので、自分では使わないメソッドてんこ盛り。
そこはテストもしてないので、ようわかりません。
ポイントとしては、
・getNextId()はADOdbではサポートしてないので、実質放棄
・getInsertId()にpgsqlを対応させるため、引数を変更
・getMetaData()のsqliteはADOdbではサポートしてないので放棄
sqliteに関しては、ちょっと絶望的かもなあ。
あと、Ethna_AppObjectクラスもオーバーライドするのでした。
// {{{ Hoge_AppObject /** * アプリケーションオブジェクトのベースクラス * * @package Hoge * @author longkey1 * @access public */ class Hoge_AppObject extends Ethna_AppObject { // {{{ add /** * オブジェクトを追加する * * @access public * @return mixed 0:正常終了 Ethna_Error:エラー */ function add() { // next idの取得: (pgsqlの場合のみ) // 取得できた場合はこのidを使う // ※ADOdb対応でnext id取得できなくなったのでいらないかもだけど、 // とりあえず残しておく foreach (to_array($this->id_def) as $id_def) { if (isset($this->prop_def[$id_def]['seq']) && $this->prop_def[$id_def]['seq']) { // NOTE: このapp object以外からinsertがないことが前提 $next_id = $this->my_db_rw->getNextId( $this->prop_def[$id_def]['table'], $id_def); if ($next_id !== null && $next_id >= 0) { $this->prop[$id_def] = $next_id; } break; } } $sql = $this->_getSQL_Add(); for ($i = 0; $i < 4; $i++) { $r =& $this->my_db_rw->query($sql); if (Ethna::isError($r)) { if ($r->getCode() == E_DB_DUPENT) { // 重複エラーキーの判別 $duplicate_key_list = $this->_getDuplicateKeyList(); if (Ethna::isError($duplicate_key_list)) { return $duplicate_key_list; } if (is_array($duplicate_key_list) && count($duplicate_key_list) > 0) { foreach ($duplicate_key_list as $k) { return Ethna::raiseNotice('Duplicate Key Error [%s]', E_APP_DUPENT, $k); } } } else { return $r; } } else { break; } } if ($i == 4) { // cannot be reached return Ethna::raiseError('Cannot detect Duplicate key Error', E_GENERAL); } // last insert idの取得: (mysql, postgres, sqlite) // primary key の 'seq' フラグがある(最初の)プロパティに入れる // ※ pgsql用にテーブルとカラム名をセットするように変更 foreach (to_array($this->id_def) as $id_def) { if ( (isset($this->prop_def[$id_def]['seq'])) && ($this->prop_def[$id_def]['seq']) ) { $insert_id = $this->my_db_rw->getInsertId( $this->prop_def[$id_def]['table'], $id_def ); if ($insert_id !== null && $insert_id >= 0) { $this->prop[$id_def] = $insert_id; } break; } } // IDの設定 if (is_array($this->id_def)) { $this->id = array(); foreach ($this->id_def as $k) { $this->id[] = $this->prop[$k]; } } else if (isset($this->prop[$this->id_def])) { $this->id = $this->prop[$this->id_def]; } else { trigger_error("primary key is missing", E_USER_ERROR); } // バックアップ/キャッシュ更新 $this->prop_backup = $this->prop; $this->_clearPropCache(); return 0; } // }}} } // }}}</pre>
これで最低限動くかなって感じだ。
next idが絡むところは、全部削除しても良いかも。
無理してsql直書きでってやり方もあるだろうけど、それはADOdbとは完全に違うものになってしまうしなあ。
どうなんだろうね。
とりあえず、ネット上にこういう情報がほぼ皆無だったので、晒しておきます。
※ 2008/10/08 追記
以下を最初の方に追記しました。
<efine('DB_FETCHMODE_DEFAULT', null); define('DB_FETCHMODE_ASSOC', null);</pre>
実働には支障が無い(使ってない)のですが、Noticeだしてsimpletest使う時とかにやたらexceptionsを出すので。
コメント