Há muitas maneiras de abordar isso, esta é uma delas. Digamos que você tenha um esquema de banco de dados semelhante a este:
Agora você pode ter AlbumMapper e ArtistMapper responsáveis por buscar esses objetos do banco de dados para você:
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);
}
Eu coloquei que AlbumMapper requer ArtistMapper então com este mapeador Álbuns são sempre retornados com seus artistas. Agora, um exemplo de implementação pode ser assim, onde eu uso um pequeno truque de indexação para anexar artistas a álbuns:
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;
}
}
Neste caso, seu Álbum será nos moldes de:
class Album {
private $id;
private $title;
private $artists = array();
public function __construct($data) {
//initialize fields
}
public function addArtist(Artist $artist) {
$this->artists[] = $artist;
}
}
Ao final de tudo isso você deverá ter uma coleção de objetos Album inicializados com seus Artistas.