Php 如何在sql查询中使用数据映射器

Php 如何在sql查询中使用数据映射器,php,mysql,zend-framework,zend-framework2,datamapper,Php,Mysql,Zend Framework,Zend Framework2,Datamapper,我很难理解datamapper设计模式。我有两个问题(一个是获取相册,另一个是获取艺术家)。我想制作一张专辑和艺术家(乐队成员)的名单。两者之间存在一对多的关系 SQL查询 public function getArtist() { $adapter = $this->getAdapter(); $sql = new Sql($adapter); $select = $sql->select(); $select->from('genre');

我很难理解datamapper设计模式。我有两个问题(一个是获取相册,另一个是获取艺术家)。我想制作一张专辑和艺术家(乐队成员)的名单。两者之间存在一对多的关系

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;  
}  
当我执行类似于下面的操作时,我最终会对每个相册运行一个单独的查询。我不想这样做。我该怎么设置这样的东西呢?我是否可以设置为每个实体表示一个查询(我希望在查询中使用联接,而不是为每个表创建实体)

ConcreteMemberMapper.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;
    }

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

}
<?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;
    }
 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,
     ));
 }
非常感谢

马特

有很多方法可以做到这一点,这就是其中之一。假设您有一个类似于以下内容的db模式:

现在,您可以让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,因此使用此mapper,相册总是与艺术家一起返回。现在,一个示例实现可以是这样的,我使用一个小索引技巧将艺术家附加到相册:

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

}

在所有这些结束时,您应该有一个由艺术家初始化的相册对象集合。

您必须从根本上更改代码,并执行
join
查询,以便在一次查询中获取相册+艺术家信息。相册上的我的foreach不会重复吗?我希望每张专辑都能回音一次,如果有艺术家在专辑下面回音的话。这就是为什么我有多个要求(我不想重复信息)。每个相册有多个艺术家(乐队成员)。SQL是否进入相册映射器界面?还是映射器?我的控制器会改变吗?我现在会创建一个新的Album实例而不是searchQuery吗$相册=新相册()//$艺术家=新的搜索查询($dbAdapter);不知道你的控制器里有什么。这里的想法是使用WHERE album_id IN($AlbumID)根据专辑id获取艺术家。这样,您将有2个db查询,而不是像结果中有相册那样多的查询。您对相册的db查询将进入ConcreteAlbumMapper,类似地,关于艺术家的db查询将存在于ConcreteArtistMapper(一些ArtistMapper实现)中。明白了。我会继续修补的。我在掌握实现这个逻辑所需的代码方面有点困难,如果你有任何你推荐我读过的源代码(我已经到处搜索了,但找不到具体的例子),我对上面的设置有两个问题。在ConcreateArtistMapper上,我得到一个错误(文本编辑器告诉我它不是抽象的)。控制器也不正确(传递的参数不正确)
class Album {

    private $id;
    private $title;
    private $artists = array();

    public function __construct($data) {
        //initialize fields
    }

    public function addArtist(Artist $artist) {
        $this->artists[] = $artist;
    }

}