2014-12-03 3 views
1

datamapper 디자인 패턴을 이해하는 데 문제가 있습니다. 나는 두 가지 질문을한다. 하나는 앨범을 얻고 다른 하나는 아티스트를 얻는다. 앨범과 아티스트 (밴드 멤버) 목록을 만들고 싶습니다. 이 둘 사이에는 일대일 관계가 있습니다.SQL 쿼리에서 데이터 매퍼를 사용하는 방법

SQL 쿼리

public function getArtist() 
{ 
    $adapter = $this->getAdapter(); 

    $sql = new Sql($adapter); 
    $select = $sql->select(); 
    $select->from('genre'); 
    $select->where(array(
      'album_id' => $album,)); 
    $selectString = $sql->getSqlStringForSqlObject($select); 
    $resultSet = $adapter->query($selectString, $adapter::QUERY_MODE_EXECUTE); 
    return $resultSet; 
} 

public function getAlbum() 
{ 
    $adapter = $this->getAdapter(); 

    $sql = new Sql($adapter); 
    $select = $sql->select(); 
    $select->from('album'); 
    $selectString = $sql->getSqlStringForSqlObject($select); 
    $resultSet = $adapter->query($selectString, $adapter::QUERY_MODE_EXECUTE); 
    return $resultSet; 
} 

나는 각 앨범에 대해 별도의 쿼리를 실행 결국 아래로 비슷한 할. 나는 이것을하고 싶지 않다. 이런 식으로 설정하는 방법은 무엇입니까? http://akrabat.com/php/objects-in-the-model-layer-part-2/? 각 엔티티가 쿼리를 나타낼 수 있도록 설정할 수 있습니까? (각 테이블에 엔티티를 만드는 것과 대조적으로 쿼리에서 조인을 사용하고 싶습니다)?

<?php foreach ($albums->getAlbum() as $album) : ?> 
<tr> 
    <td><?php echo $this->escapeHtml($album->id);?></td> 
    <td><?php echo $this->escapeHtml($album->title);?></td> 
    <td><?php echo $this->escapeHtml($album->artist);?></td> 
    <td> 
      <?php foreach ($albums->getArtist($album->id) as $member) : ?> 

이 솔루션은

아래 ConcreteAlbumMapper.php

use Zend\Db\Adapter\AdapterInterface; 
use Album\Model\AlbumMapper; 

namespace Album\Model; 

class ConcreteAlbumMapper implements AlbumMapper { 


    public function __construct(AdapterInterface $dbAdapter) 
    { 
     $this->adapter =$dbAdapter; 
    } 

    public function fetchAlbums(ArtistMapper $artistMapper) { 
      $adapter = $this->getAdapter(); 
      $sql = new Sql($adapter); 
      $select = $sql->select(); 
      $select->from('album'); 
      $selectString = $sql->getSqlStringForSqlObject($select); 
      $albumRows = $adapter->query($selectString, $adapter::QUERY_MODE_EXECUTE); 

     $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; 
    } 

} 

ConcreteMemberMapper.php

<?php 

namespace Album\Model; 

use Zend\Db\Adapter\AdapterInterface; 
use Zend\Db\Sql\Sql; 
use Album\Model\Member; 

class ConcreteMemberMapper { 



    public function __construct(AdapterInterface $dbAdapter) 
    { 
     $this->adapter =$dbAdapter; 
    } 

    public function getAdapter() 
    { 
    if (!$this->adapter) { 
     $sm = $this->getServiceLocator(); 
     $this->adapter = $sm->get('Zend\Db\Adapter\Adapter'); 
    } 
    return $this->adapter; 
    } 

    public function fetchByAlbumIds($ids) { 
     $adapter = $this->getAdapter(); 

     $sql = new Sql($adapter); 
     $select = $sql->select(); 
     $select->from('members'); 
     $select->where(array(
       'album_id' => $ids)); 
     $selectString = $sql->getSqlStringForSqlObject($select); 
     $results = $adapter->query($selectString, $adapter::QUERY_MODE_EXECUTE); 
     return $results; 
     //return \Zend\Stdlib\ArrayUtils::iteratorToArray($results); 
    } 

    public function getAlbumId() 
    { 
     return $this->albumId; 
    } 

} 

Album.php를 제공하여

<?php 
namespace Album\Model; 

use Zend\InputFilter\InputFilter; 
use Zend\InputFilter\InputFilterAwareInterface; 
use Zend\InputFilter\InputFilterInterface; 

class Album //implements InputFilterAwareInterface 
{ 

    public $id; 
    public $title; 
    public $artist; 
    public $members = array(); 

    public function __construct($data) { 
     $this->id  = (!empty($data['id'])) ? $data['id'] : null; 
     $this->artist = (!empty($data['artist'])) ? $data['artist'] : null; 
     $this->title = (!empty($data['title'])) ? $data['title'] : null; 
     $this->members = (!empty($data['members'])) ? $data['members'] : null; 
    } 

    public function addMember(Member $member) { 
     $this->members[] = $member; 
    } 

AlbumController.php

public function indexAction() 
{ 
    $dbAdapter = $this->getServiceLocator()->get('Zend\Db\Adapter\Adapter'); 
    //$album = new Album(); 
    $albumMapper = new ConcreteAlbumMapper($dbAdapter); 
    $memberMapper = new ConcreteMemberMapper($dbAdapter); 
    $bar=$albumMapper->fetchAlbums($memberMapper); 
    //$artistMapper = new 
    //var_dump($bar); 
    return new ViewModel(array(
     'albums' => $bar, 
    )); 
} 

많은 감사
매트

+1

코드를 근본적으로 변경하고 앨범 + 아티스트 정보를 단일 쿼리로 가져 오기 위해 'join'쿼리를 수행해야합니다. –

+0

그럼 내 foreach 앨범에 중복되지 않을까요? 나는 각 앨범을 한 번 울리며, 앨범 밑에있는 앨범을 되풀이하는 아티스트가 있다면 그 앨범을 되풀이하고 싶다. 그래서 여러 번 quires (나는 정보를 복제하고 싶지 않다)가있다. 앨범마다 아티스트 (밴드 멤버)가 여러 명 있습니다. – Matt

답변

2

이이 그들 중 하나를 접근하는 방법에는 여러 가지가 있습니다. 의 당신이 유사한 DB 스키마가 있다고 가정 해 봅시다 :

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); 
} 

나는 것을 넣어했습니다

enter image description here

지금 당신은 당신을 위해 DB에서 해당 개체를 가져 오는에 대한 책임 AlbumMapper 및 ArtistMapper을 가질 수 있습니다 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; 
    } 

} 

내가 앨범 아티스트를 연결하는 작은 인덱싱 트릭을 사용하는 경우 이제 구현 예는 다음과 같이 할 수있다 이 모든 것들 중에서 자신의 아티스트와 함께 초기화 된 앨범 개체 모음이 있어야합니다.

+0

SQL이 AlbumMapper 인터페이스에 있습니까? 또는 ConcreteAlbumMapper? – Matt

+0

컨트롤러가 변경됩니까? 내 searchQuery 대신 지금 Album의 새 인스턴스를 만들 수 있습니까? $ album = 새 앨범(); // $ artist = 새 SearchQuery ($ dbAdapter); – Matt

+0

컨트롤러에 무엇이 있는지 모릅니다. 여기에있는 아이디어는 WHERE album_id IN ($ albumIds)을 사용하여 앨범 ID를 기반으로 아티스트를 가져 오는 것입니다. 그렇게하면 결과에 앨범이있는만큼 많은 쿼리가 아니라 2 db의 쿼리를 갖게됩니다. 앨범에 대한 db 쿼리는 ConcreteAlbumMapper에 들어가고, 아티스트에 관한 db 쿼리는 ConcreteArtistMapper (일부 ArtistMapper 구현)에 살게됩니다. – Weltschmerz