2012-03-19 2 views
3

Я использую ZF в течение нескольких месяцев, и я действительно доволен этим, но я не совсем уверен в том, как работать с отношениями с моделями и в то же время избегать нескольких запросов к дБ. У многих людей есть эта проблема, и никто, кажется, не находит для нее хорошего решения. (и избегать использования стороннего ORM) Например, у меня есть список пользователей, и каждый пользователь принадлежит к группе. Я хочу, чтобы список пользователей отображал информацию о пользователе и имя группы (для таблиц: пользователей и групп. У пользователей есть внешний ключ для групп таблиц). У меня есть: 2 класса сопоставления для обработки этих таблиц, UserMapper и GroupMapper. 2 Модели Классы пользователей и группы классы 2 Источника данных, который расширяет Zend_Db_Table_AbstractZend Framework - отношения и оптимизация ORM

в пользовательском картографе я могу сделать findParentRow для того, чтобы получить информацию о группе каждого пользователя, но проблема у меня есть дополнительный запрос для каждой строки, это нехорошо, я думаю, когда с присоединением я могу сделать это только в одном. Конечно, теперь мы должны сопоставить этот результат с объектом. поэтому в моем абстрактном классе Mapper я пытаюсь загрузить загружаемые таблицы для каждой родительской строки с использованием псевдонимов столбцов (аналогично Yii .. я думаю), поэтому я получаю в одном запросе объект значения, подобный этому // Объект модели пользователя

$userMapper= new UserMapper(); 
$users= $userMapper->fetchAll(); //Array of user objects 
echo $user->id; 
echo $user->getGroup()->name // $user->getParentModel('group')->name // this info is already in the object so no extra query is required. 

Я думаю, вы поняли мою суть ... Есть ли у вас собственное решение, возможно, более академичное, чем мое, для того чтобы сделать это, не избегая нескольких запросов? // Zend db table выполняет дополнительные запросы, чтобы получить метаданные, которые хорошо и могут быть кэшированы. Моя проблема заключается в том, чтобы получить информацию о родительской строке ... например, в yii ... что-то вроде этого $ userModel-> with ('group') -> fetchAll(); Спасибо.

+1

Я борюсь с той же проблемой. Я думаю, что основная проблема заключается в том, что 'Zend_Db_Table' является реализацией TableDataGateway (TDG). Он достаточно гибкий, чтобы иметь дело со связанными таблицами, но без пользовательских запросов с использованием объединений вы получаете несколько запросов, как вы определили. Именно это несоответствие между TDG и моими реальными потребностями, которые часто подталкивают меня к сторонней ORM, вроде Doctrine. –

+0

Точно, но Доктрина слишком велика для проектов, с которыми я обычно работаю. , поэтому я стараюсь полагаться только на классы Zend, когда это возможно, и сохраняйте его простым. Я не знаю, какие планы для Zend_db и связанных классов в ZF2, но я надеюсь, что у нас могут быть некоторые улучшения ... ZF2 должен быть выпущен этим летом/осенью. Думаю – FranStrentz

+0

API ZF2 'Zend \ Db \ Table' будет вероятно, будет функционально таким же, как для ZF1 'Zend_Db_Table'; оба являются, в конце концов, реализациями TDG. К сожалению, вряд ли разумно ожидать, что ZF2 разрешит это фундаментальное напряжение TDG/mapper/join/use-case. –

ответ

2

Разработка вашего картографа для работы с Zend_Db_Select. Это должно обеспечить необходимую гибкость. Сгруппирована ли таблица групп, зависит от параметра, предоставленного методам сопоставления, в этом примере групповой объект является критическим параметром.

class Model_User { 
    //other fields id, username etc. 
    //... 

    /** 
    * @var Model_Group 
    */ 
    protected $_group; 

    public function getGroup() { 
     return $this->_group; 
    } 

    public function setGroup(Model_Group $group) { 
     $this->_group = $group; 
    } 

} 

class Model_Mapper_User { 

    /** 
    * User db select object, joins with group table if group model provided 
    * @param Model_Group $group 
    * @return Zend_Db_Select 
    */ 
    public function getQuery(Model_Group $group = NULL) { 
     $userTable = $this->getDbTable('user'); //mapper is provided with the user table 
     $userTableName = $userTable->info(Zend_Db_Table::NAME); //needed for aliasing 
     $adapter = $userTable->getAdapter(); 

     $select = $adapter->select()->from(array('u' => $userTableName)); 

     if (NULL !== $group) { 
      //group model provided, include group in query 
      $groupTable = $this->getDbTable('group'); 
      $groupTableName = $groupTable->info(Zend_Db_Table::NAME); 
      $select->joinLeft(array('g' => $groupTableName), 
           'g.group_id = u.user_group_id'); 
     } 

     return $select; 
    } 

    /** 
    * Returns an array of users (user group optional) 
    * @param Model_User $user 
    * @param Model_Group $group 
    * @return array 
    */ 
    public function fetchAll(Model_User $user, Model_Group $group = NULL) { 
     $select = $this->getQuery(); 
     $adapter = $select->getAdapter(); 
     $rows = $adapter->fetchAll($select); 

     $users = array(); 

     if (NULL === $group) { 
      foreach ($rows as $row) { 
       $users[] = $this->_populateUser($row, clone $user); 
      } 
     } else { 
      foreach ($rows as $row) { 
       $newUser = $this->_populateUser($row, clone $user); 
       $newGroup = $this->_populateGroup($row, clone $group); 

       //marrying user and group 
       $newUser->setGroup($newGroup); 

       $users[] = $newUser; 
      } 
     } 

     return $users; 
    } 

    /** 
    * Populating user object with data 
    */ 
    protected function _populateUser($row, Model_User $user) { 
     //setting fields like id, username etc 
     $user->setId($row['user_id']); 
     return $user; 
    } 

    /** 
    * Populating group object with data 
    */ 
    protected function _populateGroup($row, Model_Group $group) { 
     //setting fields like id, name etc 
     $group->setId($row['group_id']); 
     $group->setName($row['group_name']); 
     return $group; 
    } 

    /** 
    * This method also fits nicely 
    * @param int $id 
    * @param Model_User $user 
    * @param Model_Group $group 
    */ 
    public function fetchById($id, Model_User $user, Model_Group $group = NULL) { 
     $select = $this->getQuery($group)->where('user_id = ?', $id); 
     $adapter = $select->getAdapter(); 
     $row = $adapter->fetchRow($select); 

     $this->_populateUser($row, $user); 
     if (NULL !== $group) { 
      $this->_populateGroup($row, $group); 
      $user->setGroup($group); 
     } 

     return $user; 
    } 

} 

Сценарии использования

/** 
* This method needs users with their group names 
*/ 
public function indexAction() { 
    $userFactory = new Model_Factory_User(); 
    $groupFactory = new Model_Factory_Group(); 
    $userMapper = $userFactory->createMapper(); 
    $users = $userMapper->fetchAll($userFactory->createUser(), 
             $groupFactory->createGroup()); 
} 

/** 
* This method needs no user group 
*/ 
public function otherAction() { 
    $userFactory = new Model_Factory_User(); 
    $userMapper = $userFactory->createMapper(); 
    $users = $userMapper->fetchAll($userFactory->createUser()); 
} 

Приветствия

+0

Привет, спасибо за ответ. Я думаю, что я делаю то, что вы описываете. Мои сопоставления простираются от абстрактного класса, этот класс имеет общие методы, такие как fetch all, в абстрактном классе я передаю имя источника таблицы данных, который расширяет абстрактную абстрактную таблицу zend db, поэтому я могу иметь доступ к именам таблиц и отношениям, используя _referenceMap info. Имея это и имя класса модели Im, намеревающегося сопоставить, я могу сделать сопоставление в одном соединении с использованием aliasing. Он отлично работает для моих целей (обычная и простая CMS) ... – FranStrentz

+0

... но когда я писал, что я как бы спрашивал себя, может ли эта функциональность быть добавлена ​​в структуру. (сопоставление объектов со связанными таблицами). Но я думаю, что это тоже часть философии ZF. «Модели/картографы» - это ваша работа;) – FranStrentz

+0

Кто-то пишет абстрактный класс, который загружается в $ _referenceMap каждой модели, поэтому нам не нужно так много писать Зенд! – Mauro

2

Я написал решение по подклассов Zend_Db_Table_Rowset_Abstract и Zend_Db_Table_Row_Abstract. Я попытаюсь кратко изложить его вкратце, и если это интересно для кого-то, я могу расширить его.

Я создал абстрактную модель класса - My_Db_Table_Row - содержит массив (с ключом дочернего имени класса) для наборов строк.

Я создал абстрактный класс Rowset - My_Db_Table_Rowset - который извлекает данные из запроса на основе имен столбцов и создает ряды, хранящиеся в My_Db_Table_Row_children.

My_Db_Table_Rowset класс использует _dependantTables и _referenceMap из Zend_Db_Table_Abstract создавать дочерние экземпляры (из соединенных колонн) и добавить их в соответствующий массив в пределах _children их родительского экземпляра (созданные из столбцов «первичной таблицы»).

Доступ ребенка выполняется следующим образом: $car->getDrivers();

public function getDrivers() { 
    // allow for lazy loading 
    if (!isset($this->_children['My_Model_Driver'])) { 
     $this->_children['My_Model_Driver'] = My_Model_Driver::fetch........; 
    } 
    return $this->_children('My_Model_Driver'); 
} 

Изначально я закодирован это для 2-й уровней, родителя и ребенка, но я в процессе расширения этого обрабатывать больше уровней, например, прародитель-родитель-ребенок.