データベースアプリケーションを構築する上で多対多テーブルの扱いは面倒です。
例えば、ブックッマークをタグで分類するようなシステムの場合、BookmarkとTagは多対多(Many-to-Many)の関係にあります。RDBでは例えば下のように中間テーブルTaggedを作って以下のようにします。
Bookmark ----<> Tagged <>---- Tag
このような構造になるため、本来TagとBookmarkの関係間にTaggedが入る事により一段手間が増えます。Symfonyのソースツリーを見ていたらこの煩雑な処理をやってくれるsfPropelManyToManyというライブラリがありましたので紹介します。
sfPropelManyToMany
上の例のような多対多関係の時、特定のタグに紐づいているBookmarkを全て持ってきたい時は通常以下のようにします。
<?php // $tagにはTagのオブジェクトが入っている $bookmark = array(); foreach($tag->getTaggeds() as $tagged){ $bookmarks[] = $tagged->getBookmark(); }
実行されるSQLは以下のようになります。
[51.44 ms] SELECT tagged.TAG_ID, tagged.BOOKMARK_ID, tagged.ID FROM tagged WHERE tagged.TAG_ID=1 [65.91 ms] SELECT bookmark.TITLE, bookmark.URL, bookmark.MEMO, bookmark.DELETION, bookmark.CREATED_AT, bookmark.UPDATED_AT, bookmark.ID FROM bookmark WHERE bookmark.ID=1 [1.28 ms] SELECT bookmark.TITLE, bookmark.URL, bookmark.MEMO, bookmark.DELETION, bookmark.CREATED_AT, bookmark.UPDATED_AT, bookmark.ID FROM bookmark WHERE bookmark.ID=2 [1.24 ms] SELECT bookmark.TITLE, bookmark.URL, bookmark.MEMO, bookmark.DELETION, bookmark.CREATED_AT, bookmark.UPDATED_AT, bookmark.ID FROM bookmark WHERE bookmark.ID=3
sfPropelManyToManyを使うと上記のコードが以下のように簡単になります。
[code lang="php"] < ?php // $tagにはTagのオブジェクトが入っている $bookmarks = sfPropelManyToMany::getRelatedObjects($tag,"Tagged"); [/code]
実行されるSQLは結合を利用して一回になります。
[code lang="sql"] [35.69 ms] SELECT tagged.TAG_ID, tagged.BOOKMARK_ID, tagged.ID, bookmark.TITLE, bookmark.URL, bookmark.MEMO, bookmark.DELETION, bookmark.CREATED_AT, bookmark.UPDATED_AT, bookmark.ID FROM tagged, bookmark WHERE tagged.TAG_ID=1 AND tagged.BOOKMARK_ID=bookmark.ID ORDER BY bookmark.CREATED_AT DESC [/code]
sfPropelManyToMany::getRelatedObjects()の第3引数には、Criteriaのオブジェクトを与える事ができます。
[code lang="php"] < ?php // $tagにはTagのオブジェクトが入っている $crit = new Criteria; $crit->add(BookmarkPeer::DELETION,false); $crit->addDescendingOrderByColumn(BookmarkPeer::CREATED_AT); $bookmarks = sfPropelManyToMany::getRelatedObjects($tag,"Tagged",$crit); [/code]
sfPropelManyToManyはとても手軽で便利なので重宝しますが、多対多テーブルの扱いは実行時の負荷が大きくなりがちなので性能が要求される用途には注意してください。

