В Zend Framework 1.6 появился компонент Zend_Paginator. Об его использовании подробно рассказано в Programmer’s Reference Guide. Компонент этот очень удобен в использовании, но пока ещё слишком сырой.
Сейчас я расскажу некоторые тонкости, которые уберегут вас от серьёзных проблем.
Как известно, есть три основных Paginator-адаптера:
- Array
- DbSelect
- Iterator
И тут кажется соответствующим концепции ZF’а использование следующего кода:
1 2 | $messages = new Messages; //класс Messages наследует Zend_Db_Table_Abstract $paginator = Zend_Paginator::factory($messages->fetchAll()); |
Красиво и удобно! Но на производительности в будущем будет сказываться ужасающим образом. Дело в том, что Paginator-адаптеры Array и Iterator принимают на входе все записи, выбирают из них нужные и формируют страницы. Следовательно, когда у нас в БД будет хранится порядка тысячи сообщений (не говоря уже о большем количестве), выборка их из базы будет серьёзно нагружать систему.
У адаптера DbSelect другой принцип работы, и в этом плане он лучше. На входе принимает он объект Zend_Db_Select, подставляет в него нужные лимиты выборки и достаёт только нужные записи, не нагружая систему выборкой бесполезных данных. Несколько подправим наш код:
1 2 | $messages = new Messages; //класс Messages наследует Zend_Db_Table_Abstract $paginator = Zend_Paginator::factory($messages->select()); |
Теперь мы передаём в Paginator объект Zend_Db_Table_Select, используется адаптер DbSelect, и наша проблема решена.
Следующая тонкость – использование paginator-адаптера DbSelect совместно с Zend_Db_Select, содержащим группировку (метод group()). Проблема в том, что DbSelect не обнуляет group перед выборкой количества записей, в результате чего “SELECT COUNT(*)” возвращает результат в неподобающем виде и весь Paginator “падает”.
Решается это добавлением строки “$rowCount->reset(Zend_Db_Select::GROUP)” в метод Zend_Paginator_Adapter_DbSelect::count() в блок, где идёт обнуление остальных параметров $rowCount. Разумеется, этот способ нарушает стандарты кодинга ZF – вносить правки в ZF нельзя. Но из-за одной строки писать свой paginator-адаптер совершенно не хотелось. Надеюсь, в финальной версии ZF1.6 проблему с группировкой решат.
Спасибо за внимание, всего доброго.
Igor says:
Т.е. пока активно пользуемся своими наработками, и ждем новых версий
20 августа 2008, 17:58Stud says:
По поводу: “использование paginator-адаптера DbSelect совместно с Zend_Db_Select,”
21 августа 2008, 12:25Чтож в багтрекере у ЗФ этого нет?
Сергей Митрошин says:
2 Stud:
21 августа 2008, 17:24Видимо, это не баг, а фича )
boberov says:
Я уже не знаю. Прочитал эту статью, но никак не могу понять: почему именно используется адаптер DbSelect???
1 сентября 2008, 20:38Сергей Митрошин says:
Потому, что все остальные адаптеры неоптимально расходуют ресурсы.
1 сентября 2008, 21:24-seruy- says:
Посмотрим что они смогут предлажить дальше.
2 сентября 2008, 13:29serjik76 says:
Честноговоря не ошоломлен новой версией все в очень мелких маштабах
30 сентября 2008, 20:15woto says:
Больше всего раздражает интеграция с dojo. Вместо правки багов и оптимизации фреймворка.
21 октября 2008, 22:41Сергей Митрошин says:
Не могу согласиться. Интеграция с dojo – довольно серьёзное нововведение, которое стало одним из переломных моментов в развитии фреймворка. Если использовать большую часть его мощности, то разработка приложений ускорится очень значительно.
22 октября 2008, 12:50А баги – они есть везде. В релизах, кстати, их относительно немного. А снэпшоты использовать не рекомендуется у любого софта.
under says:
В версии 1.7 они уже обнуляют GROUP BY.
Но есть с Paginator и другие проблемки, которые приходится решать по ходу. В частности, при комбинации в запросе GROUP BY, DISTINCT и агрегатных функций, чтобы Paginator сходит с ума и неправильно составляет запрос на определение количества записей.
Лечится это в каждой конкретной ситуации по своему, мне лично помогла установка собственного Zend_Paginator_Adapter_DbSelect::ROW_COUNT_COLUMN, приблизительно так:
$expression = new Zend_Db_Expr(‘COUNT(DISTINCT `my_field`) AS ‘ . $db->quoteIdentifier(Zend_Paginator_Adapter_DbSelect::ROW_COUNT_COLUMN));
25 декабря 2008, 16:17$rowCount = clone $select;
$rowCount->reset(Zend_Db_Select::COLUMNS)
->reset(Zend_Db_Select::GROUP)
->from(NULL, $expression);
$adapter->setRowCount($rowCount);