• [Zend Framework][MySQL] Zend_Dbでトランザクション(MySQLi)

    Zend_Db_Adapter_Mysqliを使ってDBのトランザクションを制御する方法を書きます。とはいえ、インターフェースはZend_Db_Adapter_Abstractで定義されているので他のDBでも処理の書き方には変わりはないと思います。 MySQLの代表的なストレージエンジンで、InnoDBはトランザクションをサーポートしていますが、MyISAMはしていません。MyISAMでトランザクションを開始して、ロールバックを行った場合、特にException等が発生することはなくトランザクションの処理が無視(行の挿入や更新、削除等に対する取り消しがされません)されるだけですので注意してください。 トランザクションを開始するには、Zend_Db_AbstractのbeginTransaction()を、DBに対する処理を確定するにはcommit()関数を使います。また、何らかの処理が発生して、DBに対する処理をキャンセルしたい場合にはrollBack()を行います。``` // Zend_Db_Tableを使っている場合は、 // $db = $tbl->getAdapter() でDBアダプターを取得しておきます // トランザクションの開始 $db->beginTransaction(); try { $db->query(‘insert …'); $db->query(‘update …'); $db->query(‘insert …'); // Zend\_Db\_Table を使っている場合は、 // $tbl->insert($values); // $tbl->update($set, $where); // $tbl->insert($values); // コミット $db->commit(); } catch (Exception $e) { // 例外が発生したらロールバック $db->rollBack(); // 他のエラー処理 } コミットとロールバックでDBに値がどのように変化するか調べます。 以下のようなテーブルの定義のときに、 mysql> show create table test\G *************************** 1. row *************************** Table: test Create Table: CREATE TABLE `test` ( `id` int(11) NOT NULL auto_increment, `title` varchar(50) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
  • [Zend Framework] Zend_Db_Tableで、WHERE句をORで結合して行を取得する

    Zned_Db_Table_Abstractを継承したクラスで、WHERE句を複数指定して行を取得する場合は以下のようにすると思います。$table = new Users(); $adapter = $table->getAdapter(); $where = array( $adapter->quoteInto('id = ?', $id'), $adapter->quoteInto('name = ?', $name) ); $rows = $table->fetchAll($where);この場合WHERE句はANDで結合されます(WHERE id = ‘…’ AND name = ‘…’ )。 今日ORで条件をつなげたいと思ったのですが、Zend_Db_Table_Abstractクラスにはそのようなものは用意されていませんでした。 普通に書くと以下のようになるかと思います。``` $where = $adapter->quoteInto(‘id = ?', $id’); $where .= ' OR ' . $ adapter->quoteInto(‘name = ?', $name’); $rows = $table->fetchAll($where); foreach($rows as $row) { // 各行に対する処理 } // もしくは $where = $adapter->quoteInto(‘id = ?', $id’); $rows = $table->fetchAll($where); foreach($rows as $row) { if($row->name == $name) { // 各行に対する処理 } } fetchAllを追いかけてみると、最終的にZend\_Db\_Selectクラスのwhere()を呼び出していて、そこでwhere句をANDで結合して生成していました。 Zend\_Db\_Selectのソースをさらに読み進めるとorWhere()という関数があって、動作は想像のとおりwhere句をORで結合するものでした。この関数をうまく使えば、AND条件と同じようにプログラムを書けそうです。 Zend\_Db\_Table\_AbstractクラスのfetchAll()は内部で\_fetch()を呼び出し、さらに\_fetch()の内部でZend\_Db\_Selectクラスのwhere()を呼び出しています。このwhere()をorWhere()によびかえればうまくいきそうです。 うまく追加するには、Zend\_Db\_Table\_Abstractの関数を直接修正したり新しく関数を追加するのではなく、これを継承したクラスに以下の関数を追加して、さらにこれを継承してテーブルクラスを使うようにするのがいいと思います。 public function fetchAllOr($where = null, $order = null, $count = null, $offset = null) { $data = array( ‘table’ => $this, ‘data’ => $this->_fetchOr($where, $order, $count, $offset), ‘rowClass’ => $this->_rowClass, ‘stored’ => true );
  • [Zend Framework][MySQL] MySQLでレプリケーションをしている場合のZend_Db_Table

    Zend FrameworkでMySQLのマスター/スレーブ形式のレプリケーションに対応させるには自力のようです。 Zend_Db_Tableでマスター/スレーブを使い分ける方法として、どういうのがいいのかと考えています。まず思い浮かぶところでは、Zend_Db_Table_Abstractを拡張したテーブルクラスでDBを振り分けて、そのクラスをさらに拡張して各テーブルを抽象化する(前エントリーのBarクラスのようなもの)というところでしょうか。``` require_once ‘Zend/Db/Table/Abstract.php’; class Zend_Db_Table_Wrapper extends Zend_Db_Table_Abstract { public function insert(array $data) { /* ここでDBの設定(master) */ return parent::insert($data); } public function update(array $data, $where) { /\* ここでDBの設定(master) \*/ return parent::update($data, $where); } public function delete($where) { /\* ここでDBの設定(master) \*/ return parent::delete($where); } public function find($key) { /\* ここでDBの設定(slave) \*/ return parent::find($key); } public function fetchAll($where = null, $order = null, $count = null, $offset = null) { /\* ここでDBの設定(slave) \*/ return parent::fetchAll($where, $order, $count, $offset); } public function fetchRow($where = null, $order = null) { /\* ここでDBの設定(slave) \*/ return parent::fetchRow($where, $order); } }
  • [Zend Framework] Zend_Db_Table

    Zend_Db_TableはRDBを抽象化するもので、Zend_Db _Table_Abstractを拡張して使用します。 MySQLで、fooというデータベースのbarというテーブルのクラスを作る場合は以下のようになります(ほんの一例です)。``` require_once ‘Zend/Db/Table/Abstract.php’; class Bar extends Zend_Db_Table_Abstract { // テーブル名 指定しない場合はクラス名となる protected $_name = ‘bar’; // スキーマ名(DB名) protected $\_schema = 'bar'; // 上記の記述は以下のようにまとめられる。 // 以下と$\_schemaが同時に定義されたときは、以下が優先される // protected $\_name = 'foo.bar'; // 主キーを設定。 protected $\_primary = 'bar\_id'; // 主キーがauto incrementの場合は、trueにする(falseは自然キー) protected $\_sequence = true; public function \_\_construct($config = array()) { // アダプタの設定 parent::\_\_construct($config); } } で、以下のように使用します // アダプタを指定 $db = Zend_Db::factory(‘MySQLi’, $options); $t_bar = new Bar(array(‘db'=>$db)); // 主キーの値を取得 $row = $t_bar->find($id);
  • [Zend Framework] Zend_Db 基本

    PHPからDBにアクセスする部分を請け負うアダプタ(Zend_Db_Adapterを拡張したもの)は、PDOドライバに対応しているようですね。 MySQL 5.0 、PHP5.1以降を使っていたので、./configure時に –with-mysqliオプションを付けてPHPをインストールするだけで、Zend_Db_Adapter_Mysqliのアダプタが使えました(他の方法は上記PDOのリンクをご覧ください)。$ ./configure --with-mysqli=/path/to/the/mysql\_config (以下略)基本的な使い方は以下の通りです。 アダプタの取得$params = array( 'host'=>'localhost', // DBサーバーのホスト名 'username'=>'user\_name', // DBに接続するユーザー名 'password'=>'passwd', // 上記ユーザーのパスワード 'dbname'=>'test' // DB名(スキーマ名) ); // アダプタの取得 $dbh = Zend\_Db::factory('MySQLi', $params);行の取得$where = $dbh->quoteInto('id = ?', $id); // エスケープ処理 $sql = "SELECT name, age " . "FROM users " . "WHERE $where " $rows = $dbh->fetchArray($sql);戻り値は以下のようになります``` array( 0 =>array(‘name'=>hoge, ‘age'=>1), 1 =>… ) * fetchAssoc 取得した最初の行の最初の列がキーになる。 * fetchRow 最初の行のみを取得 * fetchCol 最初の列のみ取得 * fetchPairs 最初の列が配列のキーに、2番目の行が値になる * fetchOne 最初の値のみ取得 があります。 **行の挿入**``` $table = 'users'; $values = array( 'name'=>'bar', 'age'=>20 ); $dbh->insert($table, $values); // auto incrementでprimary keyの場合、keyの値を取得 $id = $dbh->lastInsertId(); ```**行の更新**``` $table = 'users'; $set = array( 'name'=>'baz', 'age'=>25 ); $where = $dbh->quoteInto('id = ?