PdoInterceptorを用いたデータベースアクセスのExampleは、example/misc/pdo にあります。 各クラスファイル、設定ファイル、ディレクトリ構成は次になります。
example/misc/pdo/ +-- classes/ | +-- Paginate.php | +-- PdoInterceptor.php | +-- SqlFileReader.php | +-- StandardDto.php | +-- StandardPdo.php +-- example/ | +-- config/ | | +-- pdo.dicon | | +-- SqliteAPdo.php | | +-- SqliteBPdo.php | +-- db/ | +-- example.sql | +-- sqlite_a.db | +-- sqlite_b.db | +-- sqlite_c.db +-- tests/clasesディレクトリにあるクラスがPdoInterceptorを使用するために必要となります。
PDOのデータソース設定は、configディレクトリに次の3つのファイルで行っています。 それぞれ、dbディレクトリのSqliteデータベースファイルをデータソースとしています。
PdoInterceptorのアスペクト対象は、クラスとインターフェースです。クラスにアスペクトした場合は、Pointcutに 適合したメソッドが実行されます。PdoInterceptorは、メソッドの戻り値が文字列の場合は、SQLクエリとして扱います。 メソッドの戻り値がHash配列の場合は、SQL発行時のコンテキストとして扱います。 メソッドの戻り値が配列の場合は、1番目の値をSQLクエリ、2番目の値をSQL発行時のコンテキストとして扱います。 メソッドの戻り値が null の場合は、SQLファイルを探してSQLクエリを取得します。PdoIterceptorがインターフェースに アスペクトされている場合は、メソッドの呼び出しを行いません。メソッドの戻り値が null の場合と同様に、SQLファイルを探してSQLクエリを取得します。
SQLクエリが取得できた場合は、データベースに発行して結果を メソッドの戻り値として return します。 データベースへのSQLクエリの発行は、PDOのPrepared Statementが使用されます。 Prepared Statementにバインド されるvalueは、メソッド引数が使用されます。メソッドの戻り値が配列の場合は、2番目の値がバインドvalueとして扱われます。
PdoInterceptorは、自身が登録されているS2Containerに存在するPDOコンポーネントを使用してデータベースに接続します。 デフォルトでは、PdoInterceptorクラスとStandardPdoクラスは、ネームスペースpdoに登録されています。
PdoInterceptorには、O/Rマップ機能や自動SQL構築機能はありません。
EMPテーブルにアクセスするDaoクラスを作成します。Daoクラスには、EMPテーブルから全件を取得するfindAllメソッドを実装します。
メソッドの戻り値としてデータベースに発行するSQLクエリを返します。
s2component関数で、Daoクラスをコンポーネントとして登録します。s2aspect関数でDaoクラスにpdo.interceptorをAspectします。
- example/example010.php
<?php require_once('S2Container/S2Container.php'); define('ROOT_DIR', dirname(dirname(__FILE__))); use \seasar\container\S2ApplicationContext as s2app; s2app::import(ROOT_DIR . '/classes'); StandardPdo::$DSN = 'sqlite:' . ROOT_DIR . '/example/db/sqlite_a.db'; class Dao { public function findAll() { return 'select * from emp'; } } s2component('Dao'); s2aspect('pdo.interceptor', '/Dao$/'); $dao = s2app::get('Dao'); $rows = $dao->findAll();
EMPテーブルにアクセスするDaoクラスを作成します。Daoクラスには、EMPテーブルからIDで検索するfindByIdメソッドを実装します。 メソッドの戻り値を配列とし、1番目の値にSQLクエリ、2番目の値にSQLクエリにバインドする値を配列で指定します。
- example/example010.php
<?php require_once('S2Container/S2Container.php'); define('ROOT_DIR', dirname(dirname(__FILE__))); use \seasar\container\S2ApplicationContext as s2app; s2app::import(ROOT_DIR . '/classes'); StandardPdo::$DSN = 'sqlite:' . ROOT_DIR . '/example/db/sqlite_a.db'; class Dao { public function findById() { $sql = 'select * from emp where EMPNO = :id'; $context = array('id' => 7369); return array($sql, $context); } } s2component('Dao'); s2aspect('pdo.interceptor', '/Dao$/'); $dao = s2app::get('Dao'); $rows = $dao->findById();
EMPテーブルにアクセスするIDaoインターフェースを作成します。IDaoインターフェースには、EMPテーブルから全件を取得するfindAllメソッドを定義します。
s2component関数で、Daoクラスをコンポーネントとして登録します。s2aspect関数でDaoクラスにpdo.interceptorをAspectします。
- example/example020.php
<?php require_once('S2Container/S2Container.php'); define('ROOT_DIR', dirname(dirname(__FILE__))); use \seasar\container\S2ApplicationContext as s2app; s2app::import(ROOT_DIR . '/classes'); StandardPdo::$DSN = 'sqlite:' . ROOT_DIR . '/example/db/sqlite_a.db'; interface IDao { public function findAll(); } s2component('IDao'); s2aspect('pdo.interceptor', '/Dao$/'); $dao = s2app::get('IDao'); $rows = $dao->findAll();
findAllメソッドが呼ばれた際に発行するSQLクエリをSQLファイルに記述します。SQLファイルはインターフェースファイルと同じディレクトリに配置します。 SQLファイル名は、インターフェース名_メソッド名.sqlとなります。IDaoインターフェースのfindAllメソッドの場合は、IDao_findAll.sqlとなります。
- example/IDao_findAll.sql
select * from emp
EMPテーブルにアクセスするIDaoインターフェースを作成します。IDaoインターフェースには、EMPテーブルからIDで検索するfindByIdメソッドを定義します。 findByIdメソッドの引数で検索するIDを指定します。
- example/example020.php
<?php require_once('S2Container/S2Container.php'); define('ROOT_DIR', dirname(dirname(__FILE__))); use \seasar\container\S2ApplicationContext as s2app; s2app::import(ROOT_DIR . '/classes'); StandardPdo::$DSN = 'sqlite:' . ROOT_DIR . '/example/db/sqlite_a.db'; interface IDao { public function findById($id); } s2component('IDao'); s2aspect('pdo.interceptor', '/Dao$/'); $dao = s2app::get('IDao'); $rows = $dao->findAll();
findAllメソッドが呼ばれた際に発行するSQLクエリをSQLファイルに記述します。SQLファイルはインターフェースファイルと同じディレクトリに配置します。
- example/IDao_findById.sql
select * from emp where EMPNO = :id
PdoInterceptorは、デフォルトでは、StandardDtoクラスをPDO::FETCH_CLASSに指定します。 StandardDtoクラスは、__callメソッドを実装しており、カラムへのアクセッサメソッドを提供します。 カラム名が「ABC_XYZ」の場合、setAbcXyzメソッド、getAbcXyzメソッドでカラム値にアクセスすることができます。
PDO::FETCH_CLASSを指定する場合は、@DTOアノテーションで行います。 書式は次のようになります。
/** * @DTO(DTOクラス名) */
例として、EMPテーブルにアクセスするIDaoインターフェースを作成します。 IDaoインターフェースには、EMPテーブルからIDで検索するfindByIdメソッドを定義します。 PDO::FETCH_CLASSクラスには、BarDtoクラスを指定しています。
- example/example030.php
<?php require_once('S2Container/S2Container.php'); define('ROOT_DIR', dirname(dirname(__FILE__))); use \seasar\container\S2ApplicationContext as s2app; s2app::import(ROOT_DIR . '/classes'); StandardPdo::$DSN = 'sqlite:' . ROOT_DIR . '/example/db/sqlite_a.db'; class BarDto extends StandardDto{} interface IDao { /** * @DTO('BarDto') */ public function findById($id); } s2component('IDao'); s2aspect('pdo.interceptor', '/Dao$/'); $dao = s2app::get('IDao'); $rows = $dao->findById(7369);
Daoクラスの中で直接PDOを使用する場合は、DaoクラスにPDOコンポーネントをインジェクションします。 次の例では、CdDaoクラスにStandardPdoコンポーネントをセッターメソッドインジェクションしています。 sampleTransactionメソッドでは、インジェクションされたPDOコンポーネントを使用してトランザクションを開始しています。
- example/CdDao.php
<?php class CdDao { private $pdo = null; public function setPdo(Pdo $pdo) { $this->pdo = $pdo; } public function sampleTransaction() { try { \seasar\log\S2Logger::getInstance(__NAMESPACE__)->info('start transaction.', __METHOD__); $this->pdo->beginTransaction(); $this->insert(10, 'aaa', 'bbb'); $this->updateTitle(10, 'AAA'); $this->delete(10); \seasar\log\S2Logger::getInstance(__NAMESPACE__)->info('commit transaction.', __METHOD__); $this->pdo->commit(); } catch (Exception $e) { \seasar\log\S2Logger::getInstance(__NAMESPACE__)->info($e->getMessage(), __METHOD__); \seasar\log\S2Logger::getInstance(__NAMESPACE__)->info('rollback transaction.', __METHOD__); $this->pdo->rollBack(); } } public function insert($id, $title, $content) { return "insert into CD values(:id, :title, :content)"; } public function updateTitle($id, $title) { return "update CD set title = :title where id = :id"; } public function delete($id) { return "delete from CD where id = :id"; } }
実行ファイルは次になります。
- example/example040.php
<?php require_once('S2Container/S2Container.php'); define('ROOT_DIR', dirname(dirname(__FILE__))); use \seasar\container\S2ApplicationContext as s2app; s2app::import(ROOT_DIR . '/classes'); s2app::import(ROOT_DIR . '/example/CdDao.php'); StandardPdo::$DSN = 'sqlite:' . ROOT_DIR . '/example/db/sqlite_b.db'; s2aspect('pdo.interceptor', '/Dao$/', '/^(insert|update|delete)/'); $dao = s2app::get('CdDao'); $rows = $dao->sampleTransaction();
Paginateクラスは、データベースからのデータ取得の際にページングを行うユーティリティクラスです。 例として、EMPテーブルにアクセスするEmpDaoクラスを作成します。EmpDaoクラスには、次のようなbyPaginateメソッドを実装します。
- example/EmpDao.php
<?php class EmpDao { public function byPaginate(\Paginate $paginate) { if (!$paginate->hasTotal()) { list($row) = $this->findAllTotal($paginate); $paginate->setTotal($row->total); } return $this->findAll($paginate); } public function findAllTotal(\Paginate $paginate) { return 'select count(*) as total from EMP order by EMPNO'; } public function findAll(\Paginate $paginate) { $sql = 'select * from EMP order by EMPNO limit :limit offset :offset'; $context = array('limit' => $paginate->getLimit(), 'offset' => $paginate->getOffset()); return array($sql, $context); } }
byPaginateメソッドでは、まずfindAllTotalメソッドで全件数を取得し、paginateインスタンスに設定します。 (Paginateクラスでページング処理を実施するために全件数が必要なため) その後、findAllメソッドを実行し、データを取得しています。findAllメソッドでは、limit/offset値にpaginateインスタンスが持つ 情報を設定して、ページングを実施しています。
次の処理を行うexample050.phpを作成します。
- example/example050.php
<?php require_once('S2Container/S2Container.php'); define('ROOT_DIR', dirname(dirname(__FILE__))); use \seasar\container\S2ApplicationContext as s2app; s2app::import(ROOT_DIR . '/classes'); s2app::import(ROOT_DIR . '/example/EmpDao.php'); StandardPdo::$DSN = 'sqlite:' . ROOT_DIR . '/example/db/sqlite_a.db'; s2aspect('pdo.interceptor', '/Dao$/', '/^find/'); $dao = s2app::get('EmpDao'); $paginate = new Paginate; $paginate->setLimit(2); $rows = $dao->byPaginate($paginate); var_dump($rows); $paginate->next(); $rows = $dao->byPaginate($paginate); var_dump($rows);
Paginate::getTotalPage メソッド.
全件数を件数/ページ(limit)で割った全ページ数を返します。
/** * @return integer */ final public function getTotalPage();Paginate::getPage メソッド.
現在のページ番号を返します。
/** * @return integer */ final public function getPage();Paginate::setPage メソッド.
ページ番号を設定します。
/** * @param integer $page * @throw Exception */ final public function setPage($page);Paginate::getOffset メソッド.
現在のoffset位置を返します。
/** * @return integer */ final public function getOffset();Paginate::setLimit メソッド.
1ページあたりの件数を設定します。
/** * @prama integer $limit */ final public function setLimit($limit);Paginate::getTotal メソッド.
全件数を返します。
/** * @return integer * @throw Exception 全件数が未設定の場合にスローされます。 */ final public function getTotal();Paginate::setTotal メソッド.
全件数を設定します。
/** * @param integer $total */ final public function setTotal($total);Paginate::setWindow メソッド.
ページウィンドウに表示するページ数を設定します。
/** * @param integer $window */ final public function setWindow($window);Paginate::next メソッド.
1ページ進めます。
final public function next();Paginate::isNext メソッド.
次のページがあるかどうかを返します。
/** * @return boolean */ final public function isNext();Paginate::prev メソッド.
1ページ戻ります。
final public function prev();Paginate::isPrev メソッド.
前のページがあるかどうかを返します。
/** * @return boolean */ final public function isPrev();Paginate::pages メソッド.
window内に収まるページ番号を列挙します。
/** * @return array */ final public function pages() {
© Copyright The Seasar Foundation and the others 2005-2010, all rights reserved. |