Pagination Tricks in Magento – Part 2

Disclaimer: the code/ideas in this post are for interests sake only, and are likely unsuitable for inclusion in production of a Magento website.

Last month’s blog post about Magento/Zend Framework pagination raised the question; what happens if you use fractions instead of integers when you set the pagination in a Magento collection? This month we’re going to find out.

The key function here is Zend_Db_Select::limitPage(), which gets called via Mage_Eav_Model_Entity_Collection_Abstract::load() -> Mage_Eav_Model_Entity_Collection_Abstract::_loadEntities()

    /**
     * Sets the limit and count by page number.
     *
     * @param int $page Limit results to this page number.
     * @param int $rowCount Use this many rows per page.
     * @return Zend_Db_Select This Zend_Db_Select object.
     */
    public function limitPage($page, $rowCount)
    {
        $page     = ($page > 0)     ? $page     : 1;
        $rowCount = ($rowCount > 0) ? $rowCount : 1;
        $this->_parts[self::LIMIT_COUNT]  = (int) $rowCount;
        $this->_parts[self::LIMIT_OFFSET] = (int) $rowCount * ($page - 1);
        return $this;
    }

This is pretty straightforward; it tells our collection that we want as many results as will fit in one page ($rowCount) and that we should offset by the number of elements in the preceeding pages ($rowCount * ($page – 1)).

The problem that came up last month was that sometimes we wanted an offset that wasn’t divisible by the page size. We can game this function to get such results by passing fractions through to the limitPage function; for example to load N items with an offset of O we can think of it as loading the fractional page number 1+O/N (see diagram below for an example). In our situation last week we wanted 10 items with an offset of 18 so we’d use code such as:

$collection = Mage::getModel('catalog/product')->getCollection();
$collection->setPageSize(10);
$collection->setCurPage(2.8);
$collection->load();

There are other ways to set this up; we need to satisfy $rowCount = N + a, $page = 1 + (O + b)/(N + a), for some a, b with 0 <= a, b < 1.

If you were actually implementing something like this you would probably look to override Mage_Catalog_Block_Product_List_Toolbar::setCollection(). This function gets called in Mage_Catalog_Block_Product_List::_beforeToHtml() shortly before the collection gets loaded, so it’s the opportunity to sort out the pagination values we want to pass to the collection and display to the user.

Hope this was interesting, let me know if anyone is crazy enough to try it out.