これにアプローチする方法はたくさんありますが、これはそのうちの1つです。次のようなデータベーススキーマがあるとします。
これで、AlbumMapperとArtistMapperに、データベースからこれらのオブジェクトをフェッチする責任を持たせることができます。
interface AlbumMapper {
/**
* Fetches the albums from the db based on criteria
* @param type $albumCriteria
* @param ArtistMapper $artistMapper
*
* Note: the ArtistMapper here can be also made optional depends on your app
*/
public function fetchBySomeCriteria($albumCriteria, ArtistMapper $artistMapper);
}
interface ArtistMapper {
/**
* @param array $ids
* @return Artist[]
*/
public function fetchByAlbumIds(array $ids);
}
AlbumMapperにはArtistMapperが必要であるため、このマッパーを使用すると、アルバムは常にアーティストとともに返されます。これで、実装例は次のようになります。ここでは、インデックス作成のちょっとしたトリックを使用して、アーティストをアルバムに添付します。
class ConcreteAlbumMapper implements AlbumMapper {
public function fetchBySomeCriteria($albumCriteria, ArtistMapper $artistMapper) {
//sql for fetching rows from album table based on album criteria
$albums = array();
foreach ($albumRows as $albumRow) {
$albums[$albumRow['id']] = new Album($albumRow);
}
$artists = $artistMapper->fetchByAlbumIds(array_keys($albums));
//marrying album and artists
foreach ($artists as $artist) {
/**
* not crazy about the getAlbumId() part, would be better to say
* $artist->attachYourselfToAnAlbumFromThisIndexedCollection($albums);
* but going with this for simplicity
*/
$albums[$artist->getAlbumId()]->addArtist($artist);
}
return $albums;
}
}
この場合、アルバムは次のようになります。
class Album {
private $id;
private $title;
private $artists = array();
public function __construct($data) {
//initialize fields
}
public function addArtist(Artist $artist) {
$this->artists[] = $artist;
}
}
このすべての最後に、アーティストで初期化されたアルバムオブジェクトのコレクションが必要です。