DAO (Data Access Object)
データベースにアクセスし、結果を返すための窓口となるインターフェースです。
メソッドアノテーション
インターフェースの各メソッドは、問い合わせ/コマンドに応じて、以下のアノテーションのうちの一つを指定します
- @Select
- @Insert
- @Update
- @Delete
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| use Omelet\Annotation\Select;
use Omelet\Annotation\Insert;
use Omelet\Annotation\Update;
use Omelet\Annotation\Delete;
interface TodoDao {
/**
* @Select
*/
function selectAll();
/**
* @Insert
*/
function insert(array $values);
/**
* @Update
*/
function update(array $values);
/**
* @Delete
*/
function delete($id);
}
|
パラメータ
引数をもつメソッドを定義することで、SQLのパラメータとして提供することができます。
また、タイプヒンティングまたは @param ドキュメントコメントを示すことで、パラメータを適切に型変換します。
1
2
3
4
5
6
| /**
* @Select
*
* @param boolean unfinished
*/
function find($unfinished);
|
上の例は、bool型として引数を処理します
タイプヒンティングまたは @param ドキュメントコメントがない場合、引数を文字列として処理することに注意してください。
例えば、以下のメソッド定義において
1
2
3
4
5
| /**
* @Select
*
*/
function findByDateBetween($from, $to);
|
$from と $to は、 \Doctrine\DBAL\Types\Type::STRING として処理されます。
このタイプは、\PDO::PARAM_STR として、引数の値を変換せずそのままパラメータとしてバインドします。
もし、$from や $to が \DateTime 型の場合、SQL実行時にエラーとなるでしょう。
以下のように、タイプヒンティングかまたはドキュメントコメント(もしくは両方)を明示してください。
1
2
3
4
5
6
7
| /**
* @Select
*
* @param DateTime from
* @param DateTime to
*/
function findByDateBetween(\DateTime $from, \DateTime $to);
|
プリミティブ値を渡す
メソッドの引数名がそのままSQLのパラメータ名となります。
1
2
| select * from todo
where id = :id
|
というSQLの場合、
1
2
3
4
5
6
| /**
* @Select
*
* @param integer id
*/
function findById($id);
|
というメソッド定義となります。
プリミティブとして扱える型は、
- 文字列(string)
- 整数(integer / int)
- 論理型(boolean / bool)
- 不動小数点数(float / double)
- タイムゾーンなし日時(DateTime)
となります。
連想配列を渡す
{引数名}_{各要素のキー}をSQLのパラメータ名として使用します。
例えば、
1
2
3
4
5
6
| /**
* @Insert
*
* @param string[]
*/
function insert($params);
|
というメソッド定義があり、引数として渡される値が
1
| [ 'id' => '10', 'todo' => 'abc-xyz-12345', 'updated_at' => '2015-05-30' ]
|
の場合、
1
2
| insert into todo (id, todo, updateAt)
values (:params_id, :params_todo, :params_updated_at)
|
という、SQLを用意します。
引数として、配列を渡す場合、すべての要素はドキュメントコメントに従った同じ型をもつこととなります。
上の例のように、パラメータに文字列として日付を渡す場合、
例えばSQLiteでは日付のフォーマットが異なれば正しくフィルタできなくなる可能性があります。
従って、連想配列を使う場合は、すべての配列要素が、 string[] , integer[] , DateTime[] の場合に限定する方が良いでしょう。
複数の型が混在する場合、後述のエンティティに詰め込むことを推奨します。
ドメインを渡す
メソッドの引数名がそのままSQLのパラメータ名となります。
ドメインは、 \Omelet\Domain\CustomDomain の派生クラスを指します。
ドメイン定義時に指定された \Doctrine\DBAL\Types\Type の型定数を元に、保持するドメイン値を適切に整形してSQLのパラメータとして渡します。
1
2
3
4
5
6
| /**
* @Select
*
* @param PrimaryKey id
*/
function findById(PrimaryKey $id);
|
と、メソッドを定義する場合、SQLは
1
2
| select * from todo
where id = :id
|
と、なります。
エンティティを渡す
エンティティの公開プロパティ(public)をパラメータとして適用します。パラメータ名は、<メソッドの引数名>_<プロパティ名>となります。
例えば、
1
2
3
4
| class Todo {
public $id;
public $content;
}
|
というエンティティ定義がり、
1
2
3
4
5
6
| class TodoDao {
/**
* @Insert
*/
function insert(Todo $todo);
}
|
DAOが上のように定義されていた場合、
1
| insrt into todo (id, todo) values (:todo_id, :todo_content)
|
のようなパラメータ付きのSQLを定義します。
結果の取得(Select)
メソッドに @return ドキュメントコメントが明示されている場合、その型での結果セットの返却を行います。
1
2
3
4
5
6
7
| /**
* @Select
*
* @param integer id
* @return DateTime
*/
function getPublishedDateById($id);
|
上の例では、結果から DateTime 型のオブジェクトを生成して返します。
メソッドに @return ドキュメントコメントが明示されていない場合、行の配列として返します。
行は列名をキーとする連想配列で、その値の型はすべて文字列です。
プリミティブ値として取得
結果をプリミティブ値として返却します。
プリミティブとして扱える型は、引数と同様
- 文字列(string)
- 整数(integer / int)
- 論理型(boolean / bool)
- 不動小数点数(float / double)
- タイムゾーンなし日時(DateTime)
です。
SQLの実行結果が複数行となる場合、その先頭行、先頭列の値を返します。
これは適切にソートされていないと、受け取る結果が安定しないということを意味しています。
プリミティブ値の配列として取得
メソッドのドキュメントコメントがプリミティブな型の配列の場合、結果をプリミティブ値として返却します。
PhpDocumenterの仕様に従い、配列は型名の後ろに [ ] を付与します。
1
2
3
4
5
6
| /**
* @Select
*
* @return DateTime[]
*/
function listPublishedDateAll();
|
ドメインとして取得
結果をドメインとして返却します。
1
2
3
4
5
6
7
| /**
* @Select
*
* @param PrimaryKey id
* @return Hidden
*/
function hidden(PrimaryKey $id);
|
結果セットの列名に、ドメインのコンストラクタの引数名、もしくは別名が存在する場合、ドメインとしてインスタンス化して返します。
一致する列名が存在しない場合でも、ドメインをインスタンス化ことに注意してください。
また、ドメイン定義として、関数従属の要素を持ち、その名前、または別名が結果セットに存在する場合、関数従属の値付きでドメインをインスタンス化します。
プリミティブ値として取得の時と同様、SQLの実行結果が複数行となる場合、先頭行の内容からドメインの構築を試みます。
ドメインの配列として取得
結果をドメインの配列として返却します。
各行のドメインの構築は、ドメインとして取得と同様です。
1
2
3
4
5
6
| /**
* @Select
*
* @return PrimaryKey[]
*/
function listAllId();
|
エンティティとして取得
結果をエンティティとして返却します。
エンティティとして返すためには、DAOメソッドのドキュメントコメントの戻り値として明示します。
1
2
3
4
5
6
| /**
* @Select
*
* @return Todo
*/
function findById($id);
|
エンティティの公開プロパティ(public)に対して、結果セットの列の値がから割り当てられます。
このとき結果セットの列名は、エンティティのプロパティ名、
またはプロパティに付与した @Column アノテーションの alias のと同じ名前値にします。
例えば、
1
2
3
4
5
6
7
8
| class Todo {
/**
* @Column(alias="todo_id")
*/
public $id;
public $content;
}
|
というエンティティ定義であれば、
1
2
3
| select todo_id as id, content
from todo
where todo_id = :id
|
または、
1
2
3
| select todo_id, content
from todo
where todo_id = :id
|
というSQLとなります。
結果セットの列名が、エンティティのプロパティ名(またはエイリアス)に存在しない場合、エラーとはせず単に無視します。
エンティティのプロパティにドメインが存在する場合
エンティティは、プロパティとしてドメインを持つことができます。
結果セットにそのプロパティ名(またはエイリアス)が存在する場合、ドメインのコンストラクタに適用して作成します。
例えば、
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| class Todo {
/**
* @Column(alias="todo_id")
*/
public $id;
public $content;
/**
* @Column(alias="editor_id")
*
* @var Editor
*/
public $editor;
}
|
の場合、
1
2
3
| select todo_id, content, editor_id as editor
from todo
where todo_id = :id
|
のようにドメインと同じ名前があれば、ドメインとして作成されます。
エンティティのドメインが関数従属値を有する場合
加えてドメインは、関数従属な値を持たせることができます。
これはエンィティのフィールドにおいても有効です。
結果セットから、関数従属な値を割り当てるために、 @Column アノテーションの optFields にその列名を配列として指定します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| class Todo {
/**
* @Column(alias="todo_id")
*/
public $id;
public $content;
/**
* @Column(alias="editor_id", optFields={"editor_name"})
*
* @var Editor
*/
public $editor;
}
|
のようなエンティティ定義の場合、
1
2
3
4
| select t.todo_id, t.content, t.editor_id, m.editor_name
from todo t
join editor m on m.editor_id = t.editor_id
where todo_id = :id
|
のように、関数従属として指定した名前と同じ列名があれば、ドメインのコンストラクタに合わせて適用します。
エンティティの配列として取得
結果をエンティティの配列として返却します。
エンティティの配列として返すためにすることは、エンティティを返す時と同様、DAOメソッドのドキュメントコメントの戻り値として明示します。
1
2
3
4
5
6
| /**
* @Select
*
* @return Todo[]
*/
function listAll();
|
エンティティの各プロパティと、結果セットの列の関係は、エンティティを返す時と同様です。
結果の取得(Insert, Update, Delete)
現在の仕様では、作用されたレコード数を返します。
クラスアノテーション
DAOインターフェースには、専用のアノテーションとして、 @Dao が用意されています。
このアノテーションは以下のパラメータを持っています。
通常、SQLはインターフェースの完全クラス名(名前空間\インターフェース名)をパスと見立てたディレクトリの中に、メソッド名と同名のSQLファイルを用意します。
名前空間が長くなると、SQLファイルを探し出すのが大変になります。
route パラメータを指定することで、名前空間の分をショートカット、もしくは別ディレクトリにSQLを置くことができます。
例えば、
1
2
3
4
5
6
7
8
| namespace Foo\Bar\Baz;
/**
* @Dao(route="FBB")
*/
interface HogeDao {
}
|
という定義の場合、
Foo/Bar/Baz/HogeDao の代わりに * FBB* を探しに行くようになります。