В 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 проблему с группировкой решат.

Спасибо за внимание, всего доброго.

10 Comments

  1. Igor says:

    Т.е. пока активно пользуемся своими наработками, и ждем новых версий :-)

  2. Stud says:

    По поводу: “использование paginator-адаптера DbSelect совместно с Zend_Db_Select,”
    Чтож в багтрекере у ЗФ этого нет?

  3. Сергей Митрошин says:

    2 Stud:
    Видимо, это не баг, а фича )

  4. boberov says:

    Я уже не знаю. Прочитал эту статью, но никак не могу понять: почему именно используется адаптер DbSelect???

  5. Сергей Митрошин says:

    Потому, что все остальные адаптеры неоптимально расходуют ресурсы.

  6. -seruy- says:

    Посмотрим что они смогут предлажить дальше.

  7. serjik76 says:

    Честноговоря не ошоломлен новой версией все в очень мелких маштабах

  8. woto says:

    Больше всего раздражает интеграция с dojo. Вместо правки багов и оптимизации фреймворка.

  9. Сергей Митрошин says:

    Не могу согласиться. Интеграция с dojo – довольно серьёзное нововведение, которое стало одним из переломных моментов в развитии фреймворка. Если использовать большую часть его мощности, то разработка приложений ускорится очень значительно.
    А баги – они есть везде. В релизах, кстати, их относительно немного. А снэпшоты использовать не рекомендуется у любого софта.

  10. 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));
    $rowCount = clone $select;
    $rowCount->reset(Zend_Db_Select::COLUMNS)
    ->reset(Zend_Db_Select::GROUP)
    ->from(NULL, $expression);
    $adapter->setRowCount($rowCount);

Leave a Reply