Комментарии 44
Ну, в официальной документации это всё есть, на русском…
framework.zend.com/manual/1.11/ru/zend.db.table.relationships.html
framework.zend.com/manual/1.11/ru/zend.db.table.relationships.html
Вместо переопределения конструтора и вызова метода addReference можно записать все в протектед свойство:
protected $_referenceMap = array(
'Units' => array(
'columns' => 'unit_id',
'refTableClass' => 'Units'
'refColumns' => 'unit_id',
'onDelete' => self::CASCADE,
'onUpdate' => self::RESTRICT
) //, ну и для второго тоже
);
И да, экстендить свои классы надо от Zend_Db_Table_Abstract а не от Zend_Db_Table
И еще не забывайте определять protected $_primary;
Судя по коду, метаданные таблицы класс получает автоматически с помощью «DESCRIBE» (а это лишний запрос), по этому обратите внимание еще и на кеширование (рус, англ) этих данных… а также их можно "захардкодить" в класс и обойтись без кеша.
Использовать встроенные методы Zend_Db_Table_Abstract — плохая практика с точки зрения моделирования. Лучше добавлять в свой класс методы getAllProducts и т.д.
А вообще, еще лучше не пользоваться напрямую Zend_Db, а отделять модель и маппер.
А кэш к таким моделям лучше делать декоратором — тогда вручную вызывать кэшер не нужно будет.
А вообще, еще лучше не пользоваться напрямую Zend_Db, а отделять модель и маппер.
А кэш к таким моделям лучше делать декоратором — тогда вручную вызывать кэшер не нужно будет.
Данной статья не о том как правильно проектировать, а о том как создавать привязку данных из одной таблицы к данным другой таблицы.
Два последних моих суждения, возможно, и не относятся к теме статьи, но я настаиваю на том, что применение fetchAll — это плохо. И совершенно ни к чему учить новичков сходу такой не очень хорошей практике. Впрочем, статья — ваша, а мнение — мое. Решать вам и вашим читателям.
Вы щас совершенно не по теме человека отчитываете. Всеравно ведь в Вашем getAllProducts будет fetchAll. Человек лишь показал связь, как ей пользоваться, каждому решать самому. Можете добавить метод, можете еще одну абстракцию сверху навесить, а можете и один в один слизать код. Думать тоже надо, а не тупо копировать код один в один (Это я о Ваших словах про новичков).
Как показывает опыт, новички сначала слизывают один в один, а потом уже в процессе работы начинают думать и понимать — в этом и заключается понятие «профессиональный рост». Да, в простейшем случае внутри getAllProducts будет только вызов fetchAll, но если позже потребуется добавить доп. функционал, то разработчик без опыта не станет рефакторить вызовы fetchAll в getAllProducts, а начнет добавлять копипастом новый функционал прямо в контроллер (или где у него будет этот fetchAll вызываться). Так вот если новичок все равно будет слизывать, так пусть он уже слизывает нормальный код и правильные подходы.
И я автора не отчитывал. Просто высказал свое мнение.
И я автора не отчитывал. Просто высказал свое мнение.
Вот как раз, как показывает опыт, если новичку дать сразу готовенькое и правильно, он зажрется. А если дать нечто абстрактное и низкоуровневое, то при первом толчке с проблемой он, хоть и не будет переделывать на правильное решение, но уже хоть задумается что что-то тут не так. На ошибках учиться надежней, чем кушать из ложечки.
Я использую fetchAll для тех целей, чтобы получить Zend_Db_Table_Rowset потом из ровсета получаю отдельные Zend_Db_Table_Row и только эти row могут работать с каскадными удалениями и обновлениями, также они могут находить Parent и Dependent rows.
Если у вас есть вариант получения данных более подходящий для этих целей, предложите.
Если говорить о совсем нормальном варианте, то вот. Это, скажем так, мое видение DDD. Вдохновлялся у Мэтью:
weierophinney.net/matthew/archives/202-Model-Infrastructure.html
weierophinney.net/matthew/archives/201-Applying-ACLs-to-Models.html
weierophinney.net/matthew/archives/200-Using-Zend_Form-in-Your-Models.html
Если же говорить о конкретно вашем случае, с использованием только Table Gateway, то я бы делал так:
Пусть нам изначально ставят задачу вывести список юзеров. Нет ничего проще.
Казалось бы, ну что за идиотизм делать такую функцию, кому она нужна, если можно напрямую вызвать fetchAll???
Но тут начальство ставит нам задачу: выводить список юзеров + количество комментариев каждого юзера.
Все, что нам нужно, слегка модифицировать наш метод:
При таком решении никаких изменений в контроллере не потребуется.
Не нравятся join'ы??? Можно и без них, если правильно настроено кэширование:
weierophinney.net/matthew/archives/202-Model-Infrastructure.html
weierophinney.net/matthew/archives/201-Applying-ACLs-to-Models.html
weierophinney.net/matthew/archives/200-Using-Zend_Form-in-Your-Models.html
Если же говорить о конкретно вашем случае, с использованием только Table Gateway, то я бы делал так:
Пусть нам изначально ставят задачу вывести список юзеров. Нет ничего проще.
class Users extends Zend_Db_Table_Abstract
{
protected $_primary = 'id';
public function getAll()
{
return $this->fetchAll();
}
}
Казалось бы, ну что за идиотизм делать такую функцию, кому она нужна, если можно напрямую вызвать fetchAll???
Но тут начальство ставит нам задачу: выводить список юзеров + количество комментариев каждого юзера.
Все, что нам нужно, слегка модифицировать наш метод:
class User extends Zend_Db_Table_Row_Abstract
{
protected $_commentsCount = 0;
public function init()
{
parent::init();
if (array_key_exists('commentsCount', $this->_data)) {
$this->_commentsCount = (int)$this->_data['commentsCount'];
unset($this->_data['commentsCount']);
}
}
public function getCommentsCount()
{
return $this->_commentsCount;
}
}
class Users extends Zend_Db_Table_Abstract
{
protected $_primary = 'id';
protected $_rowClass = 'User';
public function getAll()
{
$select = $this->select()
->from(array('u' => 'Users'))
->joinLeft(array('c' => 'Comments'), 'c.user_id = u.id', array(
'commentsCount' => new Zend_Db_Expr('COUNT(c.*)')
))
->group('u.id');
return $this->fetchAll($select);
}
}
При таком решении никаких изменений в контроллере не потребуется.
Не нравятся join'ы??? Можно и без них, если правильно настроено кэширование:
class User extends Zend_Db_Table_Row_Abstract
{
protected $_commentsCount;
public function getCommentsCount()
{
if ($this->_commentsCount === null) {
/**
* @var Zend_Db_Table_Abstract $comments
*/
$comments = $this->getTable()->getReference('Comments');
$select = $comments->select()
->columns(array('commentsCount' => new Zend_Db_Expr('COUNT(*)')))
->where('user_id = ?', $this->id);
$row = $comments->fetchRow($select);
$this->_commentsCount = (int)$row->commentsCount;
}
return $this->_commentsCount;
}
}
class Users extends Zend_Db_Table_Abstract
{
protected $_primary = 'id';
protected $_rowClass = 'User';
public function getAll()
{
return $this->fetchAll();
}
}
А где же скрытые возможности? Пока в статье я вижу базовый функционал.
Да, действительно, пожалуй здесь бы подошло выражение «неочевидные-малоизвестные возможности»
Чем они неочевидны, если очевидно описаны в документации?
Что за странная манера у вас пересказывать ман?
Что за странная манера у вас пересказывать ман?
Чем чистый PDO не угодил?
Да и вообще, смотря на бенчмарки от ZF — плакать хочется.
Да и вообще, смотря на бенчмарки от ZF — плакать хочется.
Плакать больше хочется, когда смотришь решения этой проблемы на других технологиях. Сколько же нам еще в таком каменном языке жить %(
В zf предоставлено достойное и грамотно спроектированное решение связки таблиц. И вполне удобное в применении. И было бы интересно узнать, что вы подразумеваете под выражением «чистый PDO»?
Дайте, пожалуйста, линк и сорец где плакать от бенчмарка хочется. А то сколько смотрел, обычно бенчмарки выкладывают конкуренты, которые нахваливают только свой, типа у него по сравнению с другими +100500% производительность, безопасность, архитектура и бла бла бла. Если вы уделите время и откроете 5-10 офф сайтов разных фреймворков на разных языках вы увидите эту картину.
А разные блоггеры из тех что я видел не имеют должного уровня и опыта работы с фреймворком чтобы делать подобные бенчмарки. На каждую задачу свой инструмент, вот пусть кто-то сделает бенчмарки того же ZF под разные задачи, с использованием роутингов, моделей, автолоадом, кэшированием внутренним/сторонним и без каждого пункта и сравнит с такими же результатами коханы, уии или любым по вкусу. Вот тогда я скажу, да, действительно вот тут у ZF узкое место, но и будет видно почему. А так по опыту могу сказать, что люди просто не умеют его готовить. Не всегда нужно делать по примерам из документации, я бы даже сказал чаще всего это ресурсозатратно по причине неограниченной и ненужной расширяемости для среднестатистического проекта.
А разные блоггеры из тех что я видел не имеют должного уровня и опыта работы с фреймворком чтобы делать подобные бенчмарки. На каждую задачу свой инструмент, вот пусть кто-то сделает бенчмарки того же ZF под разные задачи, с использованием роутингов, моделей, автолоадом, кэшированием внутренним/сторонним и без каждого пункта и сравнит с такими же результатами коханы, уии или любым по вкусу. Вот тогда я скажу, да, действительно вот тут у ZF узкое место, но и будет видно почему. А так по опыту могу сказать, что люди просто не умеют его готовить. Не всегда нужно делать по примерам из документации, я бы даже сказал чаще всего это ресурсозатратно по причине неограниченной и ненужной расширяемости для среднестатистического проекта.
class Default_Models_Games_Table extends Zend_Db_Table_Abstract
{
protected $_name = 'games';
protected $_primary = array('game_id');
}
class Default_Models_Games
{
public function getGames()
{
$gamesModel = new Default_Models_Games_Table;
return $gamesModel->fetchAll();
}
}
это долго?..
возможно пример не самый подходящий, но это просто
{
protected $_name = 'games';
protected $_primary = array('game_id');
}
class Default_Models_Games
{
public function getGames()
{
$gamesModel = new Default_Models_Games_Table;
return $gamesModel->fetchAll();
}
}
это долго?..
возможно пример не самый подходящий, но это просто
По вашей ссылке, Zend_Db показал очень неплохой результат.
Первый минус, который я заметил в этом тесте, убрать все require_once, это дорогая операция для таких тестов.
У меня готова ещё одна статья про кэширование моделей, опубликовать её из-за минусовой кармы не имею возможности. Данная статья на данный момент имеет плюсовой рейтинг. Кому не безразлична судьба раздела Zend Framework на хабре и моя в частности, прошу плюсануть.
сааамый первый пример использования
>> $productsRowset = $productsTable->fetchRow();
видимо, должно быть
$productsRowset = $productsTable->fetchAll();
>> $productsRowset = $productsTable->fetchRow();
видимо, должно быть
$productsRowset = $productsTable->fetchAll();
Полезная статья, спасибо.
Совет: вместо
можно использовать
echo '' . print_r($row->toArray(), true) . '
' . PHP_EOL;
можно использовать
print Zend_Debug::dump($row);
Можно было бы рассказать, что в класе который расширяет Zend_Db_Table_Abstract можно переопределить поле primary $_rowClass = «Model_Custom_Row», в который можно какие — нибудь вкусности поместить.
class Model_Custom_Row extends Zend_Db_Table_Row_Abstract
{
//Переведем поля, которые можно перевести
public function getTranslate($lang, $field)
{
}
}
а далее
$result = $model->fetchRow($where);
$translatedDescription = $result->getTranslate(«en», «description»);
можно еще $_rowsetClass тоже переопределить, но это намного реже используется.
class Model_Custom_Row extends Zend_Db_Table_Row_Abstract
{
//Переведем поля, которые можно перевести
public function getTranslate($lang, $field)
{
}
}
а далее
$result = $model->fetchRow($where);
$translatedDescription = $result->getTranslate(«en», «description»);
можно еще $_rowsetClass тоже переопределить, но это намного реже используется.
Ад как он есть.
Зачем эта вкусность? чтобы вместо
$translatedDescription = $translator->translate($result->description);
писать
$translatedDescription = $result->getTranslate(«en», «description»);
?? да ещё и язык за собой таскать
Зачем эта вкусность? чтобы вместо
$translatedDescription = $translator->translate($result->description);
писать
$translatedDescription = $result->getTranslate(«en», «description»);
?? да ещё и язык за собой таскать
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Zend_Db_Table простота в использовании