Zend_Db_Table - работа со связями

RushHourRider

Новичок
Zend_Db_Table - работа со связями

Доброго времени суток.

Дано: MySQL 5.0 MyISAM, PHP5, ZendFramework 1.0, три таблицы (см. ниже)
Вопрос: как без извратов и потерь производительности получить список строк из таблицы Items?

Итак, таблоидцы:
Код:
CREATE TABLE Items (
  `ID` INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `TypeID` INT NOT NULL,
  `OwnerID` INT NOT NULL,
  `Identity` VARCHAR(255)
);

CREATE TABLE Types (
  `ID` INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `Name` VARCHAR(255)
);

CREATE TABLE Owners (
  `ID` INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `Name` VARCHAR(255)
);
Как видно, Items относится к Types и Owners как M:1. В ZF есть механизм для эмуляции связей. Напишем классцы-модели для всех таблиц:
PHP:
// foobar.php
include 'Zend/Loader.php';
spl_autoload_register(array('Zend_Loader', 'autoload'));

class Owners extends Zend_Db_Table_Abstract 
{
	protected $_name = 'Owners';
	protected $_primary = array('ID');
	protected $_dependentTables = array('Main');
}

class Types extends Zend_Db_Table_Abstract 
{
	protected $_name = 'Types';
	protected $_primary = array('ID');
	protected $_dependentTables = array('Items');
}

class Items extends Zend_Db_Table_Abstract  
{
	protected $_name = 'Items';
	protected $_primary = array('ID');
	protected $_referenceMap = array(
		'Type' => array(
			'columns' => 'TypeID',
			'refTableClass' => 'Types',
			'refColumns' => 'ID',
		),
		'Owner' => array(
			'columns' => 'OwnerID',
			'refTableClass' => 'Owners',
			'refColumns' => 'ID',
		),
	);
}
Благодабря этим конструкциям можем получить для каждой записи из Items связанные с ней записи из Owners и Type:
PHP:
$items = new Items();
$item = $items->find(123); // $id = 123;
$item->findParentOwners();
$item->findParentTypes();
Все это замечательно, но когда встает задача передать в шаблончег данные для заполнения html-таблички, в воздухе материализовывается госпожа жаба, которая утверждает, что для каждой строки из Items будет сделано еще два запроса к базе для получения связанных данных. Неоптимальненько. Сразу напрашивается новый метод в класс Items:
PHP:
function foo()
{
	$select = $this->_db->select()
		->from($this->_name)
		->joinLeft('Types', 'Types.ID = Items.TypeID', array('TypeName' => 'Name'))
		->joinLeft('Owners', 'Owners.ID = Items.OwnerID', array('OwnerName' => 'Name'));
	return $select->query()->fetchAll();
}
Но, черт подери, господа! В Zend_Db_Table_Abstract есть замечательный метод fetchAll(), который принимает в качестве параметров и условия для WHERE и для ORDER и т.п. Но его использовать не можем, потому как работает он только с одной таблицей, определенной в $this->_name. В то же время, хочется добавить этот функционал в foo(), но не охота делать ctrl+c ctrl+v из fetchAll(). Дилемма.

Итого. Охота иметь метод для SELECT * FROM Items с подключением связаных таблиц и в стиле Zend_Db_Table::fetch*()
Кто как эту задачу в ZF решает?
 

RushHourRider

Новичок
pachanga
Те же яйца, только в профиль. Приведенный пример имеет те же проблемы если тебе нужно получить список лекций, который будет включать в себя название курса.
 

pachanga

Новичок
Автор оригинала: RushHourRider
pachanga
Те же яйца, только в профиль. Приведенный пример имеет те же проблемы если тебе нужно получить список лекций, который будет включать в себя название курса.
Слушай, а ты прав, дико извиняюсь, читал видать невнимательно(мне подумалось другое). То что ты хочешь называется eager fetching, и насколько я знаю в Zend такого нет, да и у нас тоже(пока). Но вот в Rails ActiveRecord есть. Там это будет выглядеть примерно так:

Код:
Items.find(123, :include=>[:type, :owner])
Ну и кстати, твой вариант с инкапсуляцией left join логики в метод не так уж и плох на самом деле ;)
 

gray07

Новичок
В propel есть методы, что то типа

itemPeer::doSelectJoinTypes()
itemPeer::doSelectJoinOwners()
itemPeer::doSelectJoinAll()

которые выполняют только один запрос
 

RushHourRider

Новичок
pachanga
ROR рулит, как всегда =)
Мне мой вариант не нравится тем, что для сортировки, уточнения выборки с помощью WHERE и pagination придется _скопировать_ практически весь Zend_Db_Table_Abstract::_fetch(). А я очень ленивый =)
 

pachanga

Новичок
Автор оригинала: gray07
В propel есть методы, что то типа

itemPeer::doSelectJoinTypes()
itemPeer::doSelectJoinOwners()
itemPeer::doSelectJoinAll()

которые выполняют только один запрос
А если будет три и большей связей, Propel нагенерит все их комбинации?

itemPeer :: doSelectJoinFoo();
itemPeer :: doSelectJoinBar();
itemPeer :: doSelectJoinZoo();
itemPeer :: doSelectJoinFooAndBar();
itemPeer :: doSelectJoinFooAndZoo();
...
itemPeer :: doSelectJoinFooAndBarAndZoo();

Или только генерится по одному методу на каждую связь + doSelecJoinAll()?
 

gray07

Новичок
pachanga
только генерится по одному методу на каждую связь + doSelecJoinAll()

Плюс можно составлять запросы с выбором полей, джоинами... но тогда нету hydrate (нужно руками создавать объекты, можно работать только с resultSet)

Нравится мне тем, что есть content assist в редакторе :), в отличии от doctrine например

-~{}~ 14.09.07 20:35:

ах еще есть методы doSelectJoinAllExceptXXX :)
 

RushHourRider

Новичок
gray07
propel крут.
А как дела обстоят если в сгенерированный по схеме базы класс внесены дополнительные методы, а потом меняется схема и классы надо снова генерировать?
 

pachanga

Новичок
Автор оригинала: RushHourRider
gray07
propel крут.
А как дела обстоят если в сгенерированный по схеме базы класс внесены дополнительные методы, а потом меняется схема и классы надо снова генерировать?
В Propel перегенерятся базовые Peer классы, от которых унаследованы конечные. Т.е конечные классы пользователя не затронутся напрямую никак. Однако стадию генерации я лично недолюбливаю.
 
Сверху