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.

:bulb:Did you enjoy this blog post?

If so, please consider checking out my side project Domain Clamp. It's a SaaS which monitors domains and SSL certificates and sends notifications before anything expires. If you work at an agency, then you're probably not the registrant for your client's domains or the SSL certificate owner. This means you won't get expiration notifications. You don't want a client's domain or SSL certificate to expire under your watch. Believe me, I've been there.

Domain Clamp solves this problem by letting you monitor the SSL certificate and registration for any domain you'd damn please. Free accounts are available so please head on over »