2011-08-15 71 views
2

我是Zend的新手,一直試圖遵循使用Data Mappers並擴展Zend_Db_Table_Abstract的Zend Quick Start Guide's example。我想我已經掌握了一般概念,但我現在想知道,我將如何去修改指南的示例代碼以允許多個表。基於快速入門指南的Zend中的多個表示例

下面是代碼的修改部分我現在感興趣的是:

protected $_dbTable; 

public function setDbTable($dbTable) 
{ 
    if (is_string($dbTable)) { 
     $dbTable = new $dbTable(); 
    } 
    if (!$dbTable instanceof Zend_Db_Table_Abstract) { 
     throw new Exception('Invalid table data gateway provided'); 
    } 
    $this->_dbTable = $dbTable; 
    return $this; 
} 

public function getDbTable() 
{ 
    if (null === $this->_dbTable) { 
     $this->setDbTable('Application_Model_DbTable_Guestbook'); 
    } 
    return $this->_dbTable; 
} 

我已經改成了這樣:

protected $_dbTables; 

public function setDbTable($dbTable, $tableName) 
{ 
    if (is_string($dbTable)) { 
     $dbTable = new $dbTable(); 
    } 
    if (!$dbTable instanceof Zend_Db_Table_Abstract) { 
     throw new Exception('Invalid table data gateway provided'); 
    } 
    $this->_dbTables[$tableName] = $dbTable; 
    return $this; 
} 

public function getDbTables() 
{ 
    if (null === $this->_dbTables) { 
     $this->setDbTable('Application_Model_DbTable_Courses', 'courses'); 
     $this->setDbTable('Application_Model_DbTable_CourseTimes', 'course_times'); 
    } 
    return $this->_dbTables; 
} 

這是一個正確的方式去實現多個表在數據映射器模式中,還是以不同的方式執行?感謝您的幫助!

+0

我現在意識到,我在這裏的邏輯是錯誤的輕微和MVC模式的無知造成的。我在這個問題上建議的解決方案並不好,因爲每個映射器應該只有一個表。如果需要多個同時插入,則應該從控制器或通過另一個模型調用它們。 – Dan

回答

1

假設您要從相關表中返回數據,您應該使用add queries with joins to one Zend_Db_Table或使用Zend_Db_Table_Relationships來獲取關聯的數據。使用連接的好處是,只有在使用關係時纔會執行一個查詢。缺點是連接的表不會返回Zend_Db_Table_Row對象(iirc),但是由於您要將它們映射到您的Domain對象,所以它不是什麼大問題。

在結構上,你可以像我在How to change Zend_Db_Table name within a Model to insert in multiple tables中所建議的那樣做。無論您是創建網關網關還是直接在DataMapper中直接聚合表網關,都取決於您。只要你認爲合適,就把它們組合起來。

+0

謝謝,我正在考慮INSERT而不是SELECT語句。是的,他們是兩個相關的表格。我不認爲我可以使用您爲此建議的任何一種方法? Zend_Db_Table_Relationships的文檔聲明:「不支持級聯INSERT,您必須在一個操作中向父表中插入一行,並在單獨的操作中將行插入到從屬表中。」 – Dan

+0

請檢查我的答案我告訴我你的想法。 –

+0

@戈登在你接受的答案[這裏](http://stackoverflow.com/questions/2436461/how-to-change-zend-db-table-name-within-a-model-to-insert-in-multiple你建議創建兩個Zend_Db_Table_Abstract實例,每個實例一個。這實際上是我從中得到我最初的想法的地方。這是因爲沒有使用數據映射器,或者您不再推薦這種方法嗎? – Dan

1

我不知道這是否是最好的做法,但是,這是我的抽象映射:

<?php 
abstract class Zf_Model_DbTable_Mapper 
{ 
    protected $_db; 
    protected $_dbTable = null; 

    protected $_systemLogger = null; 
    protected $_userLogger = null; 

    public function __construct() 
    { 
     $this->_systemLogger = Zend_Registry::get('systemLogger'); 
     $this->_userLogger = Zend_Registry::get('userLogger'); 

     // Set the adapter 
     if(null !== $this->_dbTable) 
     { 
      $tableName = $this->_dbTable; 
      $this->_db = $this->$tableName->getAdapter(); 
     } 
    } 

    public function __get($value) 
    { 
     if(isset($this->$value)) 
     { 
      return $this->$value; 
     } 

     $dbTable = 'Model_DbTable_' . $value; 
     $mapper = 'Model_' . $value; 

     if(class_exists($dbTable)) 
     { 
      return new $dbTable; 
     } 
     elseif(class_exists($mapper)) 
     { 
      return new $mapper; 
     } 
     else 
     { 
      throw new Exception("The property, DbTable or Mapper \"$value\" doesn't exists");  
     } 
    } 

    public function __set($key,$value) 
    { 
     $this->$key = $value; 
    } 

    public function getById($id) 
    { 
     $resource = $this->getDefaultResource(); 

     $id = (int)$id; 
     $row = $resource->fetchRow('id =' . $id); 

     if (!$row) { 
      throw new Exception("Count not find row $id"); 
     } 

     return $row;  
    } 

    public function getAll() 
    { 
     $resource = $this->getDefaultResource(); 

     return $resource->fetchAll()->toArray();  
    } 

    public function save(Zf_Model $Model) 
    { 
     $dbTable = $this->getDefaultResource(); 
     $data = $Model->toArray(); 

     if(false === $data) return false; 

     if(false === $Model->isNew()) 
     { 
       if(1 == $dbTable->update($data, 'id =' . (int)$Model->getId())) 
       { 
        return $Model; 
       } 
     } 
     else 
     {  
      $id = $dbTable->insert($data); 

      if($id) 
      { 
       $Model->setId($id); 
       return $Model; 
      } 
     } 

     return false; 
    } 

    public function remove($id) 
    { 
     return $this->getDefaultResource()->delete('id =' . (int) $id); 
    } 

    protected function getDefaultResource() 
    { 
     if(empty($this->_dbTable)) 
     { 
      throw new Exception('The $_dbTable property was not set.'); 
     } 

     $classname = 'Model_DbTable_' . $this->_dbTable; 
     if(!class_exists($classname)) 
     { 
      throw new Exception("The Model_DbTable_\"$classname\" class was not found."); 
     } 


     return new $classname; 
    } 

    protected function getDefaultModel() 
    { 
     return current($this->_models); 
    } 

    protected function getResources() 
    { 
     return $this->_resources; 
    } 
} 

這是一個對我實施的映射器:

<?php 
class Model_TwitterPostsMapper extends Zf_Model_DbTable_Mapper 
{ 
    /* 
    * Data Source 
    * @var string Zend_Db_Table name 
    */ 
    protected $_dbTable = 'TwitterPosts'; 

    public function recordExists($Item) 
    { 
     $row = $this->TwitterPosts->fetchRow($this->TwitterPosts->select()->where('status_id =?', $Item->getSource()->getStatusId())); 
     if($row) 
     { 
      return $row->id; 
     } 

     return false; 
    } 

    public function getLastUpdate($options) 
    { 
     $select = $this->TwitterPosts->select() 
         ->setIntegrityCheck(false) 
         ->from(array('t' => 'twt_tweets'), 't.created_at') 
         ->join(array('u' => 'twt_users'), 't.user_id = u.id', '') 
         ->order('t.created_at DESC'); 

     if($options['user_id']) 
     { 
      $select->where("t.user_id = ?", $options['user_id']); 
     } 

     if($options['terms']) 
     { 
      if(is_array($options['terms'])) 
      { 
       $condition = ''; 
       foreach($options['terms'] as $i => $term) 
       { 
        $condition .= ($i > 0) ? ' OR ' : ''; 
        $condition .= $this->getAdapter()->quoteInto('content LIKE ?',"%$term%"); 
       } 

       if($condition) 
       { 
        $select->where($condition); 
       } 
      } 
     } 

     return $this->TwitterPosts->fetchRow($select)->created_at; 
    } 

    public function getSinceId($term = null) 
    { 
     $select = $this->TwitterPosts->select()->setIntegrityCheck(false) 
         ->from('twt_tweets_content', 'status_id') 
         ->where('MATCH(content) AGAINST(? IN BOOLEAN MODE)', "$term") 
         ->order('status_id ASC') 
         ->limit(1); 
     //echo $select; exit; 

     $tweet = $this->TwitterPosts->fetchRow($select); 

     if(null !== $tweet) return $tweet->status_id; 

     return 0; 
    } 

    public function getAllByStatusId($statuses_id) 
    { 
     $select = $this->TwitterPosts->select() 
         ->setIntegrityCheck(false) 
         ->from(array('t' => 'twt_tweets'), array('t.id', 't.user_id', 't.status_id','t.user_id')) 
         ->join(array('u' => 'twt_users'), 't.user_id = u.id', array('u.screen_name', 'u.profile_image')) 
         ->where('status_id IN(?)', $statuses_id); 

     $rows = $this->TwitterPosts->fetchAll($select); 

     $Posts = array(); 
     foreach($rows as $row) 
     { 
      // Here we populate the models only with the specific method return data 
      $data = $row->toArray(); 

      $Post = new Model_TwitterPost($data['id']); 
      $Post->populate($data); 

      $User = new Model_TwitterUser($data['user_id']); 
      $User->populate($data); 

      $Post->setUser($User); 
      $Posts[] = $Post; 
     } 

     return $Posts; 
    } 

    public function getAllSince($since_id) 
    { 
     $select = $this->TwitterPosts->select() 
         ->setIntegrityCheck(false) 
         ->from(array('t' => 'twt_tweets'), array('t.status_id','t.user_id')) 
         ->join(array('u' => 'twt_users'), 't.user_id = u.id', array('u.screen_name', 'u.profile_image')) 
         ->where('status_id > ?', $since_id) 
         ->order('t.datetime DESC'); 

     $rows = $this->TwitterPosts->fetchAll($select); 

     $Posts = array(); 
     foreach($rows as $row) 
     { 
      // Here we populate the models only with the specific method return data 
      // TODO: This is not a truly lazy instatiation, since there's no way to get the not setted properties 
      $data = $row->toArray(); 
      $Post = new Model_TwitterPost($data); 
      $User = new Model_TwitterUser($data); 
      $Post->setUser($User); 
      $Posts[] = $Post; 
     } 

     return $Posts; 
    } 

    public function getTotalRatedItems($options) 
    { 
     $options = $this->prepareOptions($options); 

     $select = $this->TwitterPosts->select() 
         ->setIntegrityCheck(false) 
         ->from(array('t' => 'twt_tweets'), array('COUNT(DISTINCT t.id) AS total','r.rate')) 
         ->join(array('u' => 'twt_users'), 't.user_id = u.id', '') 
         ->join(array('r' => 'twt_tweets_rate'), 't.id = r.tweet_id', array('r.rate')) 
         ->group('r.rate') 
         ->order('t.datetime DESC'); 

     $select = $this->prepareSelect($select, $options); 

     $rates = $this->TwitterPosts->fetchAll($select)->toArray(); 

     $itemsRated = array('Green' => 0, 'Yellow' => 0, 'Orange' => 0, 'Red' => 0, 'Gray' => 0); 
     foreach ($rates as $rate) 
     { 
      $itemsRated[$rate['rate']] = $rate['total']; 
     } 

     return $itemsRated; 
    } 

    public function getUsersActivity($options) 
    { 
     $options = $this->prepareOptions($options); 

     $select = $this->TwitterPosts->select() 
         ->setIntegrityCheck(false) 
         ->from(array('t' => 'twt_tweets'), array('COUNT(DISTINCT t.id) AS total','DATE(t.datetime) AS datetime')) 
         ->join(array('u' => 'twt_users'), 't.user_id = u.id', '') 
         ->joinLeft(array('r' => 'twt_tweets_rate'), 't.id = r.tweet_id', '') 
         ->group('t.user_id') 
         ->order('t.datetime DESC'); 

     $select = $this->prepareSelect($select, $options); 

     $activity = $this->TwitterPosts->fetchAll($select)->toArray(); 

     return $activity; 
    } 

    public static function prepareOptions($options) 
    { 
     if(!is_array($options)) 
     { 
      $options = array(); 
     } 

     date_default_timezone_set('America/Sao_Paulo'); 

     if(Zend_Date::isDate($options['start_date'])) 
     { 
      $date = new Zend_Date($options['start_date']); 
      $date->setTime('00:00:00'); 
      $date->setTimezone('UTC'); 

      $options['start_date'] = $date->toString('yyyy-MM-dd HH:mm:ss'); 
     } 

     if(Zend_Date::isDate($options['end_date'])) 
     { 
      $date = new Zend_Date($options['end_date']); 
      $date->setTime('23:59:59'); 
      $date->setTimezone('UTC'); 

      $options['end_date'] = $date->toString('yyyy-MM-dd HH:mm:ss'); 
     } 

     date_default_timezone_set('UTC'); 

     $options['mainTerms'] = array(); 
     if(!empty($options['terms']) && !is_array($options['terms'])) 
     { 
      $options['mainTerms'] = explode(' ', $options['terms']);  
     } 

     if(!is_array($options['terms'])) 
     { 
      $options['terms'] = array(); 
     } 

     if($options['group_id'] || $options['client_id']) 
     { 
      $TwitterSearches = new Model_DbTable_TwitterSearches(); 

      $options['terms'] = array_merge($TwitterSearches->getList($options),$options['terms']); 

      if(empty($options['terms'])) 
      { 
       $options['terms'] = array(); 
      } 
     } 

     return $options; 
    } 

    public static function prepareSelect($select, $options) 
    { 
     if($options['start_date']) 
     { 
      $select->where('t.datetime >= ?', $options['start_date']); 
     } 

     if($options['end_date']) 
     { 
      $select->where('t.datetime <= ?', $options['end_date']); 
     } 

     foreach($options['mainTerms'] as $mainTerm) 
     { 
      $select->where('t.content LIKE ?', "%$mainTerm%"); 
     } 

     if($options['user_id']) 
     { 
      $select->where("t.user_id = ?", $options['user_id']); 
     } 

     if($options['terms']) 
     { 
      $select->where('MATCH (t.content) AGASINT(?)', $options['terms']); 
     } 

     if($options['rate']) 
     { 
      if($options['rate'] == 'NotRated') 
      { 
       $select->where('r.rate IS NULL'); 
      } 
      else 
      { 
       $select->where('r.rate = ?', $options['rate']); 
      } 
     } 

     if($options['last_update']) 
     { 
      $select->where('t.created_at > ?', $options['last_update']); 
     } 

     if($options['max_datetime']) 
     { 
      $select->where('t.created_at < ?', $options['max_datetime']); 
     } 

     return $select; 
    } 
} 

模型:

<?php 
class Model_TwitterPost extends Zf_Model 
{ 
    private $_name = 'twitter'; 

    protected $_properties = array(
     'id', 
     'status_id', 
     'user_id', 
     'content' 
    ); 

    protected $_User = null; 

    public function setUser(Zf_Model $User) 
    { 
     $this->_User = $User; 
    } 

    public function getUser() 
    { 
     return $this->_User; 
    } 

    public function getPermalink() 
    { 
     return 'http://twitter.com/' . $this->screen_name . '/' . $this->status_id; 
    } 

    public function hasTerm($term) 
    { 
     if(preg_match("/\b$term\b/i", $this->getContent())) 
     { 
      return true; 
     } 
     return false; 
    } 

    public function getEntityName() 
    { 
     return $this->_name; 
    } 

    public function getUserProfileLink() 
    { 
     return $this->getUser()->getProfileLink() . '/status/' . $this->getStatusId(); 
    } 
} 

抽象模型(一般對象):

<?php 
abstract class Zf_Model 
{ 
    protected $_properties = array(); 
    protected $_modified = array(); 
    protected $_data = array(); 

    protected $_new = true; 
    protected $_loaded = false; 

    public function __construct($id=false) 
    { 
     $id = (int)$id; 
     if(!empty($id)) 
     { 
      $this->_data['id'] = (int)$id; 
      $this->setNew(false); 
     } 
    } 

    public function populate($data) 
    { 
     if(is_array($data) && count($data)) 
     { 
      foreach($data as $k => $v) 
      { 
       if(in_array($k,$this->_properties)) 
       { 
        $this->_data[$k] = $v; 
       } 
      } 
     } 

     $this->setLoaded(true); 
    } 

    public function setNew($new=true) 
    { 
     $this->_new = (bool)$new; 
    } 

    public function isNew() 
    { 
     return $this->_new; 
    } 

    public function setLoaded($loaded = true) 
    { 
     $this->_loaded = (bool)$loaded; 
    } 

    public function isLoaded() 
    { 
     return $this->_loaded; 
    } 

    public function __call($methodName, $args) { 

     if(method_exists($this, $methodName)) 
     { 
      return $this->$methodName($args); 
     } 

     $property = $methodName; 
     if (preg_match('~^(set|get)(.*)$~', $methodName, $matches)) 
     { 
      $filter = new Zend_Filter_Word_CamelCaseToUnderscore(); 
      $property = strtolower($filter->filter($matches[2])); 

      if(in_array($property, $this->_properties)) 
      { 
       if('set' == $matches[1]) 
       { 
        $this->_data[$property] = $args[0]; 

        if(true === $this->isLoaded()) 
        { 
         $this->_modified[$property] = true; 
        } 

        return $this; 
       } 
       elseif('get' == $matches[1]) 
       { 
        if(array_key_exists($property, $this->_data)) 
        { 
         return $this->_data[$property]; 
        } 

        throw new Exception("The property $property or $methodName() method was not setted for " . get_class($this)); 
       } 
      } 
     } 

     throw new Exception("The property '$property' doesn't exists."); 
    } 

    public function __get($key) 
    { 
     if(isset($this->_data[$key])) 
     { 
      return $this->_data[$key]; 
     } 
     return $this->$key; 
    } 

    public function __set($key,$value) 
    { 
     if(array_key_exists($key,$this->_properties)) 
     { 
      $this->_data[$key] = $value; 
      return; 
     } 

     $this->$key = $value; 
    } 

    public function getId() 
    { 
     return (!$this->_data['id']) ? null : $this->_data['id']; 
    } 

    public function toArray() 
    { 
     // If it's a new object 
     if(true === $this->isNew()) 
     { 
      return $this->_data; 
     } 

     // Else, if it's existing object 
     $data = array(); 
     foreach($this->_modified as $k=>$v) 
     { 
      if($v) 
      { 
       $data[$k] = $this->_data[$k]; 
      } 
     } 

     if(count($data)) 
     { 
      return $data; 
     } 

     return false; 
    } 

    public function reload() 
    { 
     $this->_modified = array(); 
    } 
}