GROUP-ing a product collection the right way with groupByAttribute

Published: March 12, 2017

Tags:

Recently, I was reworking the implementation of a featured products widget which showed up on the home page. In order to show a variety of products we decided to GROUP BY manufacturer. This way only one product would show up per brand. The initial implementation looked something like this…

$collection = Mage::getModel('catalog/product')->getCollection();

// Do some other logic

$collection->getSelect()->group('e.manufacturer_value')

This was working fine in dev (and production). However, when I merged some new code into the develop branch and deployed it to staging I started getting exceptions.

This GROUP BY implementation assumes that flat tables are available (e.manufacturer_value). However the code I merged in created an attribute through an install script making flat temporarily unavailable.

Running a reindex would, of course, “solve” the issue, however I wanted to rework my implementation so that it could work with both flat and EAV.

After some digging I found this StackOverflow thead and saw groupByAttribute mentioned. Digging through the code I found that it will work with both flat and EAV implementations (detecting whether it is GROUPing from the flat table, or a JOIN-ed EAV table.) groupByAttribute is a thing in both Magento 1 and Magento 2. It looks like this…

/**
 * Groups results by specified attribute
 *
 * @param string|array $attribute
 */
public function groupByAttribute($attribute)
{
    if (is_array($attribute)) {
        foreach ($attribute as $attributeItem) {
            $this->groupByAttribute($attributeItem);
        }
    } else {
        if (isset($this->_joinFields[$attribute])) {
            $this->getSelect()->group($this->_getAttributeFieldName($attribute));
            return $this;
        }

        if (isset($this->_staticFields[$attribute])) {
            $this->getSelect()->group(sprintf('e.%s', $attribute));
            return $this;
        }

        if (isset($this->_joinAttributes[$attribute])) {
            $attrInstance = $this->_joinAttributes[$attribute]['attribute'];
            $entityField = $this->_getAttributeTableAlias($attribute) . '.' . $attrInstance->getAttributeCode();
        } else {
            $attrInstance = $this->getEntity()->getAttribute($attribute);
            $entityField = 'e.' . $attribute;
        }

        if ($attrInstance->getBackend()->isStatic()) {
            $this->getSelect()->group($entityField);
        } else {
            $this->_addAttributeJoin($attribute);
            $this->getSelect()->group($this->_getAttributeTableAlias($attribute).'.value');
        }
    }

    return $this;
}

From now on, I’ll be using groupByAttribute instead of group whenever working with any collection that descends from Mage_Eav_Model_Entity_Collection_Abstract.

Conclusion

If you have any questions or comments, feel free to drop a note below, or, as always, you can reach me on Twitter as well.

Hi, I'm Max!

I'm a software developer who mainly works in PHP, but also dabbles in Ruby and Go. Technical topics that interest me are monitoring, security and performance.

During the day I solve challenging technical problems at Something Digital where I mainly work with the Magento platform. I also blog about tech, work on open source and hunt for bugs.

I built a tool called Domain Clamp which monitors and alerts about expiring domains and SSL certificates.

If you'd like to get in touch with me the best way is on Twitter.