dousi4257 2012-12-03 20:09
浏览 46
已采纳

Zend框架模型,Active Record模式替代

I'm writing some code that allows users to read reports on a site, using AJAX calls to dynamically load only what is requested, instead of the entire 15+MB report.

I'm writing a Model to access all the report data from the database, and I don't want to use the Active Record pattern. I'm following the idea of "A Model HAS a table, instead of IS-A table", since this model will be accessing 5 different tables, and there are some complex MySQL JOIN's between these tables.

What is a good design pattern to follow in Zend Framework for this, examples?


UPDATED on 2012-12-05 @ 12:14PM EST

I'm currently working for a Market Research Report company. Without using actual function names, or revealing any meaningful details of the code, here are the basics:

code diagram

readreportAction() does:

  • get the report meta data
  • get the report "table of contents"

readsectionAction() does:

  • get the report text, only a part of it
  • get the embedded tabular data
  • get the figures / images
  • get the footnotes
  • format the report text

reportpdfAction() does the exact same thing as readreportAction() and readsectionAction(), except all at one time. I'm trying to conceptualize a way to NOT copy + paste this code / programming logic. A data mapper seems to solve this.

  • 写回答

3条回答 默认 最新

  • dongtan1845 2012-12-06 09:08
    关注

    First it looks like you need to make a little bit more of a conceptual leap. With the data mapper pattern it helps to think in terms of objects instead of database tables. I found these two articles helpful when I needed to make the leap.

    http://phpmaster.com/building-a-domain-model/
    http://phpmaster.com/integrating-the-data-mappers/

    That being said ZF 1 has some very useful tools for building a data mapper/domain model.

    The convention in ZF 1 is for each table you are working with to be accessible through the Zend_Db_Table api. The simplest way I've found is to just use the DbTable resource for each table. You could also use the Zend_Db::factory or new Zend_Db_Table('tableName') or any other method that appeals to you.

    This example is based on a mp3 song track.

    //in effect this is the database adapter for database table 'track', This is $tableGateway used later.
    <?php
    class Application_Model_DbTable_Track extends Zend_Db_Table_Abstract
    {
        //name of database table, required to be set if name of class does not match name of table
        protected $_name = 'track';
        //optional, column name of primary key
        protected $_primary = 'id';
    
    }
    

    there are several ways to attach a table to the Db adapter and the Zend_Db_Table api, I just find this method simple to implement and it makes setting up a mapper simple as well.

    The mapper class is the bridge between the data source and your object (domain entity). The mapper interacts with the api for Zend_Db_Table in this example.

    A really important point to understand: when using classes that extend Zend_Db_Table_Abstract you have all the basic functionality of the Zend_Db component at your disposal. (find(),fetchall(), fetchRow(), select() ...)

    <?php
    class Music_Model_Mapper_Track extends Model_Mapper_Abstract
    {
    
        //the mapper to access the songs artist object
        protected $artistMapper;
        //the mapper to access to songs album object
        protected $albumMapper;
    
        /**
         * accepts instance of Zend_Db_Table_Abstract
         *
         * @param Zend_Db_Table_Abstract $tableGateway
         */
        public function __construct(Zend_Db_Table_Abstract $tableGateway = null)
        {
            //at this point I tend to hardcode $tablegateway but I don't have to
            $tableGateway = new Application_Model_DbTable_Track();
            parent::__construct($tableGateway);
            //parent sets the $tablegateway variable and provides an abstract requirement
            //for createEntity(), which is the point of this class
        }
        /**
         * Creates concrete object of Music_Model_Track
         *
         * @param object $row
         * @return Music_Model_Track
         */
        public function createEntity($row)
        {
            $data = array(
                'id'           => $row->id,
                'filename'     => $row->filename,
                'format'       => $row->format,
                'genre'        => $row->genre,
                'hash'         => $row->hash,
                'path'         => $row->path,
                'playtime'     => $row->playtime,
                'title'        => $row->title,
                'track_number' => $row->track_number,
                'album'        => $row->album_id,//foriegn key
                'artist'       => $row->artist_id//foriegn key
            );
            //instantiate new entity object
            return new Music_Model_Track($data);
        }
        /**
         * findById() is proxy for find() method and returns
         * an entity object.
         *
         * @param type $id
         * @return object Model_Entity_Abstract
         */
        public function findById($id)
        {
            //instantiate the Zend_Db_Select object
            $select = $this->getGateway()->select();
            $select->where('id = ?', $id);
            //retrieve one database table row
            $row = $this->getGateway()->fetchRow($select);
            //create one entity object Music_Model_Track
            $entity = $this->createEntity($row);
            //return one entity object Music_Model_Track
            return $entity;
        }
    
      //truncated
    }
    

    All that has gone before is for the express purpose of building the following object:

    <?php
    class Music_Model_Track extends Model_Entity_Abstract
    {
        /**
         * $id, __set, __get and toArray() are implemented in the parent
         */
        protected $album;
        protected $artist;
        protected $filename;
        protected $format;
        protected $genre;
        protected $hash;
        protected $path;
        protected $playtime;
        protected $title;
        protected $track_number;
        //artist and album mappers
        protected $albumMapper  = null;
        protected $artistMapper = null;
    
    
       //these are the important accessors/mutators because they convert a foreign key
       //in the database table to an entity object.
        public function getAlbum()
        {
            //if the album object is already set, use it.
            if(!is_null($this->album) && $this->album instanceof Music_Model_Album) {
                return $this->album;
            } else {
                //else we make a new album object
                if(!$this->albumMapper) {
                    $this->albumMapper = new Music_Model_Mapper_Album();
                }
                //This is the album object we get from the id in our reference array.
                return $this->albumMapper->findById($this->getReferenceId('album'));
            }
        }
        //same as above only with the artist object.
        public function getArtist()
        {
            if(!is_null($this->artist) && $this->artist instanceof Music_Model_Artist) {
                return $this->artist;
            } else {
                if(!$this->artistMapper) {
                    $this->artistMapper = new Music_Model_Mapper_Artist();
                }
                return $this->artistMapper->findById($this->getReferenceId('artist'));
            }
        }
        //the setters record the foriegn keys recorded in the table row to an array,
        //this allows the album and artist objects to be loaded only when needed.
        public function setAlbum($album)
        {
            $this->setReferenceId('album', $album);
            return $this;
        }
    
        public function setArtist($artist)
        {
            $this->setReferenceId('artist', $artist);
            return $this;
        }
    
      //standard setter and getters truncated...
    }
    
    so when using the track object you would get album or artist info like:
    
    //this would be used in a controller most likely.
    $mapper = new Music_Model_Mapper_Track();
    $track = $mapper->findById('1');
    //all of the information contained in the album or artist object is
    //available to the track object.
    //echo album title, year or artist. This album object also contains the artist object
    //so the artist object would be available in two ways.
    echo $track->album->title; //or
    echo $track->artist->name;
    echo $track->album->artist->name;
    echo $track->getAlbum()->getArtist()->getName();
    

    So what you really need to decide is how you want to structure your application. What I see as obvious may not be an option you wish to implement. A lot of the answers to your questions depend on exactly how these resources are to be used.

    I hope this helps you at least a little bit.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

悬赏问题

  • ¥15 数学建模招标中位数问题
  • ¥15 phython路径名过长报错 不知道什么问题
  • ¥15 深度学习中模型转换该怎么实现
  • ¥15 HLs设计手写数字识别程序编译通不过
  • ¥15 Stata外部命令安装问题求帮助!
  • ¥15 从键盘随机输入A-H中的一串字符串,用七段数码管方法进行绘制。提交代码及运行截图。
  • ¥15 TYPCE母转母,插入认方向
  • ¥15 如何用python向钉钉机器人发送可以放大的图片?
  • ¥15 matlab(相关搜索:紧聚焦)
  • ¥15 基于51单片机的厨房煤气泄露检测报警系统设计