add to hatena hatena.comment (1) add to del.icio.us (0) add to livedoor.clip (0) add to Yahoo!Bookmark (0) Total: 1

DoctrineのfindBy,findOneByにorder byを追加する方法

symfonyではDoctrineが実質的な標準ORMになりました。
まだまだ使いきれていないのですが、機能が豊富にあるため知っているのと知らないのではかなりの差がでてきます。

今回は知っていると得するDoctrineの小ネタです。

Doctrineの特徴としてfindByとfindOneByメソッドがあります。
これらはマジックメソッドで実装されており、カラム名をつけて呼び出すことでより通常のDQLの記述よりも短くすっきりと書くことができます。

たとえば、Categoryテーブルからnameが”食品”のデータを取得する場合で比較してみます。

PHP:
  1. <?php
  2. // DQLで書いた場合
  3. $dq = new Doctrine_Query();
  4. $category = $dq->from('Category')
  5.                ->where('name = ?', '食品')
  6.                ->execute();
  7.  
  8. // findByで書いた場合
  9. $category = Doctrine::getTable('Category')->findByName('食品');

タイプ数だけみてもほぼ半分です。
なので、複雑でないクエリであればfindBy系を普通に使っていました。

しかしこのfindBy系はフィールド名しか指定できません。
そのため、order byを指定するような変更が入った場合はDQLに書き直す必要があります。

さらに、findBy系の処理をアクションに書いていたりすると修正箇所がいっきに膨らんでしまうということも考えられます。

そういうときに、Doctrineのイベントリスナーを使えば既存のfindByを使っている箇所を変更せずにorder byを追加することができます。

Doctrineには色々なイベントが用意されているのですが、今回はDQLを生成するときのフックポイントであるpreDqlSelectメソッドを使います。その名のとおりSELECTのDQLを作成するときに呼び出されるフックポイントです。
そして、以下のようなコードを記述しておきます。

* /lib/model/doctrine/Category.class.php

PHP:
  1. <?php
  2. class Category extends BaseCategory
  3. {
  4.   public function preDqlSelect($event)
  5.   {
  6.     $params = $event->getParams();
  7.     $field_name = $params['alias'] . '.display_order';
  8.     $q = $event->getQuery();
  9.     $q->orderBy($field_name);
  10.   }

エイリアスはDoctrineによって作成されているのでgetParamsから取得したaliasというパラメータを参照しています。
また、呼び出された時点でのDQLは引数で渡されてきた$eventからgetQueryメソッドで取得します。
あとは、通常のDQLにorderByを追加するのと同じです。
このサンプルでは表示順(display_orderフィールド)でorder byしています。

ただし、このフックポイントがDoctrineで利用されるようにするためにはsymfonyの設定ファイルで以下のように設定しておく必要があります。

* /config/ProjectConfiguration.class.php

PHP:
  1. <?php
  2. class ProjectConfiguration extends sfProjectConfiguration
  3. {
  4.    public function configureDoctrine($manager)
  5.    {
  6.      $manager->setAttribute(Doctrine::ATTR_USE_DQL_CALLBACKS, true);
  7.    }
  8. }

こうすることでfindByを使いつつ、order byを指定することができるようになりました。
まだまだDoctrineを使いこなせていませんが、イベントリスナーとテンプレートは必須になりそうです。

参考ページ:
Doctrine Event Listeners
Doctrineで論理差駆除を意識せずに扱う

関連するその他の記事

Comments

Leave a Reply