Skip to content

Instantly share code, notes, and snippets.

@Vinai
Last active April 30, 2020 06:39
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save Vinai/df2efe00c5f5ea12e45e to your computer and use it in GitHub Desktop.
Save Vinai/df2efe00c5f5ea12e45e to your computer and use it in GitHub Desktop.
Helper to preload all simple products and configurable attributes for a product collection containing configurable products.
<?php
class VinaiKopp_ProductList_Helper_Data extends Mage_Core_Helper_Abstract
{
/**
* Base product collection to work on
*
* @var Mage_Catalog_Model_Resource_Product_Collection
*/
protected $_productCollection;
/**
* Array of the ids of configurable products from $_productCollection
*
* @var array
*/
protected $_configurableProductIds;
/**
* All associated simple products from configurables in $_configurableProductIds
*
* @var Mage_Catalog_Model_Resource_Product_Type_Configurable_Product_Collection
*/
protected $_simpleProducts;
/**
* Array of associated simple product ids.
* The array index are configurable product ids, the array values are
* arrays of the associated simple product ids.
*
* @var array
*/
protected $_associatedSimpleProducts;
/**
* Array keys are the configurable product ids,
* the values are configurable attribute ids
*
* @var array
*/
protected $_configurableProductAttributes;
/**
* Array of all configurable attribute codes used in the current collection.
* No association of which product used which configurable attributes.
*
* @var array
*/
protected $_configurableAttributeCodes;
/**
* Set the product collection.
* If not specified defaults to the product collection of the layer model.
*
* @param Mage_Catalog_Model_Resource_Product_Collection $collection
* @return $this
*/
public function setProductCollection(Mage_Catalog_Model_Resource_Product_Collection $collection)
{
$this->_productCollection = $collection;
return $this;
}
/**
* Return the attribute values of the associated simple products
*
* @param Mage_Catalog_Model_Product $product Configurable product
* @param int|Mage_Core_Model_Store $store
* @return array
*/
public function getProductConfigurableAttributes(Mage_Catalog_Model_Product $product, $store = null)
{
if ($product->getTypeId() != Mage_Catalog_Model_Product_Type_Configurable::TYPE_CODE) {
return array();
}
$storeId = Mage::app()->getStore($store)->getId();
$usedProducts = $this->_getAssociatedSimpleProducts($product);
if (! $usedProducts) {
// This will happen if two configurable products share the same simple product
// The second configurable product will be skipped
return array();
}
$attributeIds = $this->_getProductConfigurableAttributeIds($product);
$allAttributeCodes = $this->_getConfigurableAttributeCodes();
$data = array();
foreach ($attributeIds as $attributeId) {
$code = $allAttributeCodes[$attributeId];
$sourceModel = Mage::getSingleton('eav/config')
->getAttribute('catalog_product', $code)
->setStoreId($storeId)
->getSource();
foreach ($usedProducts as $simpleProduct) {
// check if the attribute is set in case it isn't set to be used in product listing
if (isset($simpleProduct[$code])) {
$value = $simpleProduct[$code];
$data[$code][$value] = $sourceModel->getOptionText($value);
}
}
}
return $data;
}
/**
* Return array of configurable attribute ids of the given configurable product
*
* @param Mage_Catalog_Model_Product $product
* @return array
*/
protected function _getProductConfigurableAttributeIds(Mage_Catalog_Model_Product $product)
{
$attributes = $this->_getConfigurableProductAttributes();
$productId = $product->getId();
if (! isset($attributes[$productId])) {
Mage::throwException(
$this->__('Product %d is not part of the current product collection', $product->getId())
);
}
return explode(',', $attributes[$productId]);
}
/**
* Load all configurable attributes used in the current product collection
*
* @return array
*/
protected function _getConfigurableProductAttributes()
{
if (! $this->_configurableProductAttributes) {
$productIds = $this->_getConfigurableProductIds();
$attributes = $this->_getConfigurableAttributesForProductsFromResource($productIds);
$this->_configurableProductAttributes = $attributes;
}
return $this->_configurableProductAttributes;
}
/**
* This method actually would belong into a resource model, but for easier
* reference I dropped it into the helper here.
*
* @param array $productIds
* @return array
*/
protected function _getConfigurableAttributesForProductsFromResource(array $productIds)
{
/** @var Mage_Core_Model_Resource_Helper_Mysql4 $resourceHelper */
$resourceHelper = Mage::getResourceHelper('core');
$resource = Mage::getSingleton('core/resource');
$adapter = $resource->getConnection('catalog_read');
$select = $adapter->select()
->from($resource->getTableName('catalog/product_super_attribute'), array('product_id'))
->group('product_id')
->where('product_id IN(?)', $productIds);
$resourceHelper->addGroupConcatColumn($select, 'attribute_ids', 'attribute_id');
$attributes = $adapter->fetchPairs($select);
return $attributes;
}
/**
* Return array of all configurable attributes in the current collection.
* Array indexes are the attribute ids, array values the attribute code
*
* @return array
*/
protected function _getConfigurableAttributeCodes()
{
if (is_null($this->_configurableAttributeCodes)) {
// build list of all configurable attribute codes for the current collection
$this->_configurableAttributeCodes = array();
foreach ($this->_getConfigurableProductAttributes() as $attributes) {
$attributes = explode(',', $attributes);
foreach ($attributes as $attributeId) {
if ($attributeId && ! isset($this->_configurableAttributeCodes[$attributeId])) {
$attributeModel = Mage::getSingleton('eav/config')
->getAttribute('catalog_product', $attributeId);
$this->_configurableAttributeCodes[$attributeId] = $attributeModel->getAttributeCode();
}
}
}
}
return $this->_configurableAttributeCodes;
}
/**
* Return the current product collection
*
* @return Mage_Catalog_Model_Resource_Product_Collection
*/
protected function _getProductCollection()
{
if (! $this->_productCollection) {
$this->_productCollection = Mage::getSingleton('catalog/layer')->getProductCollection();
};
return $this->_productCollection;
}
/**
* Return array of ids of configurable products in the current product collection
*
* @return array
*/
protected function _getConfigurableProductIds()
{
if (is_null($this->_configurableProductIds)) {
$this->_configurableProductIds = array();
$products = $this->_getProductCollection();
foreach ($products as $product) {
if ($product->getTypeId() == Mage_Catalog_Model_Product_Type_Configurable::TYPE_CODE) {
$this->_configurableProductIds[] = $product->getId();
}
}
}
return $this->_configurableProductIds;
}
/**
* Return all associated simple products for the configurable products in
* the current product collection.
* Array key is the configurable product
*
* @return array
*/
protected function _getSimpleProducts()
{
if (is_null($this->_simpleProducts)) {
$parentIds = $this->_getConfigurableProductIds();
$collection = Mage::getResourceModel('catalog/product_type_configurable_product_collection')
->addAttributeToFilter('is_saleable', 1)
->addAttributeToSelect('parent_id');
$collection->getSelect()->where('link_table.parent_id IN(?)', $parentIds);
$collection->groupByAttribute('entity_id');
$attributeCodes = $this->_getConfigurableAttributeCodes();
$collection->addAttributeToSelect($attributeCodes);
foreach ($collection->getItems() as $row) {
$row = $row->getData();
$simpleId = $row['entity_id'];
$parentId = $row['parent_id'];
$this->_simpleProducts[$simpleId] = $row;
$this->_associatedSimpleProducts[$parentId][] = $simpleId;
}
}
return $this->_simpleProducts;
}
/**
* Return array of associated simple product data
*
* @param Mage_Catalog_Model_Product $product The configurable product
* @return array Data for each associated simple product as array
*/
protected function _getAssociatedSimpleProducts(Mage_Catalog_Model_Product $product) {
$simpleProducts = array();
$parentId = $product->getId();
$allSimpleProducts = $this->_getSimpleProducts();
// If two configurable products share the same simple product the
// second one will not have a matching record in $this->_associatedSimpleProducts
if (isset($this->_associatedSimpleProducts[$parentId])) {
foreach ($this->_associatedSimpleProducts[$parentId] as $simpleProduct) {
$simpleProducts[] = $allSimpleProducts[$simpleProduct];
}
}
return $simpleProducts;
}
/**
* The methods listed below belong into the resource model layer. For easier
* reference as an educational example I've listed them in this helper though.
*
* @see self::_getConfigurableAttributesForProductsFromResource()
* @see self::_getSimpleProducts()
* @return VinaiKopp_ProductList_Model_Resource_Configurable_Attributes
*/
protected function _getResourceModel()
{
return Mage::getResourceSingleton('vinaikopp_productlist/configurable_attributes');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment