取得したDoctrine_Collectionのインデックスに特定のフィールドの値を使う

通常、Doctrineでレコードを取得すると、Doctrine_Collectionのインデックスには整数インデックスが使われます。
しかし、取得したレコード一覧を単にループで処理するのではなく、連想配列のように処理したい場合もあります。
このような場合、Doctrine_Collectionのインデックスに、取得するレコードの特定のフィールドを使うように設定できます。

この機能を「Key Mapping」と呼びます。

Key Mappingの基本1 INDEXBYキーワードによる指定

DQL内に直接指定できる「INDEXBY」というキーワードがあります。
これを使うのがもっとも簡単です。

<?php
$user_list = Doctrine_Query::create()->from('User u INDEXBY u.handle_name')
  ->execute();

この場合、インデックス化の指定は1つのクエリのみに適用されます。

Key Mappingの基本2 ATTR_COLL_KEY

Doctrine_Tableに設定できる属性で、ATTR_COLL_KEYがあります。

<?php
$user_list = Doctrine_Core::getTable('User')
  ->setAttribute(Doctrine_Core::ATTR_COLL_KEY, 'handle_name')
  ->execute();

このように、Doctrine_TableのsetAttribute()メソッドを使って、テーブルの属性として「ATTR_COLL_KEY」にインデックスに使用するフィールドの名前を設定します。


なお、ここで設定した属性値はDoctrine内にキャッシュ(テーブルオブジェクトがキャッシュ)されますので、同じテーブルのレコードを続けて取得する場合、インデックス化のフィールドが有効なままになります。

少し複雑な場合

リレーションも含めてJOINしてレコードを取得する場合はどうでしょうか。
この場合も、クエリの主となるテーブルに対してATTR_COLL_KEYを設定すれば、ハイドレーション時に同じようにインデックスとして使われます。

ユーザーと、その電話番号を一度に取得して、結果はハンドル名をキーとする連想配列になるようにしてみます。

<?php
Doctrine_Core::getTable('User')
  ->setAttribute(Doctrine_Core::ATTR_COLL_KEY, 'handle_name');
$dql = new Doctrine_Query::create()->from('User u')
  ->leftJoin('u.Phonenumber p')
  ->execute();

まとめ

Key Mappingを使うには、

  • 1つのクエリ内のみで有効な「INDEXBY」キーワードを使う
  • テーブル属性で「ATTR_COLL_KEY」を設定する

の2つの方法がある。
前者が簡単。全体の挙動を変えたいような場合は後者を使う。