product mapping * Depends on following product data: * - product must have children products attached * * @param array $parentProducts * @param $storeId * @return void */ public function attachConfigurableProductChildrenAttributeMapping(array $parentProducts, $storeId) { $listSwatchAttr = Mage::helper('configurableswatches/productlist')->getSwatchAttribute(); $parentProductIds = array(); /* @var $parentProduct Mage_Catalog_Model_Product */ foreach ($parentProducts as $parentProduct) { $parentProductIds[] = $parentProduct->getId(); } $configAttributes = Mage::getResourceModel('configurableswatches/catalog_product_attribute_super_collection') ->addParentProductsFilter($parentProductIds) ->attachEavAttributes() ->setStoreId($storeId) ; $optionLabels = array(); foreach ($configAttributes as $attribute) { $optionLabels += $attribute->getOptionLabels(); } foreach ($parentProducts as $parentProduct) { $mapping = array(); $listSwatchValues = array(); /* @var $attribute Mage_Catalog_Model_Product_Type_Configurable_Attribute */ foreach ($configAttributes as $attribute) { /* @var $childProduct Mage_Catalog_Model_Product */ if (!is_array($parentProduct->getChildrenProducts())) { continue; } foreach ($parentProduct->getChildrenProducts() as $childProduct) { // product has no value for attribute, we can't process it if (!$childProduct->hasData($attribute->getAttributeCode())) { continue; } $optionId = $childProduct->getData($attribute->getAttributeCode()); // if we don't have a default label, skip it if (!isset($optionLabels[$optionId][0])) { continue; } // normalize to all lower case before we start using them $optionLabels = array_map(function ($value) { return array_map('Mage_ConfigurableSwatches_Helper_Data::normalizeKey', $value); }, $optionLabels); // using default value as key unless store-specific label is present $optionLabel = $optionLabels[$optionId][0]; if (isset($optionLabels[$optionId][$storeId])) { $optionLabel = $optionLabels[$optionId][$storeId]; } // initialize arrays if not present if (!isset($mapping[$optionLabel])) { $mapping[$optionLabel] = array( 'product_ids' => array(), ); } $mapping[$optionLabel]['product_ids'][] = $childProduct->getId(); $mapping[$optionLabel]['label'] = $optionLabel; $mapping[$optionLabel]['default_label'] = $optionLabels[$optionId][0]; $mapping[$optionLabel]['labels'] = $optionLabels[$optionId]; if ($attribute->getAttributeId() == $listSwatchAttr->getAttributeId() && !in_array($mapping[$optionLabel]['label'], $listSwatchValues) ) { $listSwatchValues[$optionId] = $mapping[$optionLabel]['label']; } } // end looping child products } // end looping attributes foreach ($mapping as $key => $value) { $mapping[$key]['product_ids'] = array_unique($mapping[$key]['product_ids']); } $parentProduct->setChildAttributeLabelMapping($mapping) ->setListSwatchAttrValues($listSwatchValues); } // end looping parent products } /** * For given product, get configurable images fallback array * Depends on following data available on product: * - product must have child attribute label mapping attached * - product must have media gallery attached which attaches and differentiates local images and child images * - product must have child products attached * * @param Mage_Catalog_Model_Product $product * @param array $imageTypes - image types to select for child products * @return array */ public function getConfigurableImagesFallbackArray(Mage_Catalog_Model_Product $product, array $imageTypes, $keepFrame = false ) { if (!$product->hasConfigurableImagesFallbackArray()) { $mapping = $product->getChildAttributeLabelMapping(); $mediaGallery = $product->getMediaGallery(); if (!isset($mediaGallery['images'])) { return array(); //nothing to do here } // ensure we only attempt to process valid image types we know about $imageTypes = array_intersect(array('image', 'small_image'), $imageTypes); $imagesByLabel = array(); $imageHaystack = array_map(function ($value) { return Mage_ConfigurableSwatches_Helper_Data::normalizeKey($value['label']); }, $mediaGallery['images']); // load images from the configurable product for swapping foreach ($mapping as $map) { $imagePath = null; //search by store-specific label and then default label if nothing is found $imageKey = array_search($map['label'], $imageHaystack); if ($imageKey === false) { $imageKey = array_search($map['default_label'], $imageHaystack); } //assign proper image file if found if ($imageKey !== false) { $imagePath = $mediaGallery['images'][$imageKey]['file']; } $imagesByLabel[$map['label']] = array( 'configurable_product' => array( Mage_ConfigurableSwatches_Helper_Productimg::MEDIA_IMAGE_TYPE_SMALL => null, Mage_ConfigurableSwatches_Helper_Productimg::MEDIA_IMAGE_TYPE_BASE => null, ), 'products' => $map['product_ids'], ); if ($imagePath) { $imagesByLabel[$map['label']]['configurable_product'] [Mage_ConfigurableSwatches_Helper_Productimg::MEDIA_IMAGE_TYPE_SMALL] = $this->_resizeProductImage($product, 'small_image', $keepFrame, $imagePath); $imagesByLabel[$map['label']]['configurable_product'] [Mage_ConfigurableSwatches_Helper_Productimg::MEDIA_IMAGE_TYPE_BASE] = $this->_resizeProductImage($product, 'image', $keepFrame, $imagePath); } } $imagesByType = array( 'image' => array(), 'small_image' => array(), ); // iterate image types to build image array, normally one type is passed in at a time, but could be two foreach ($imageTypes as $imageType) { // load image from the configurable product's children for swapping /* @var $childProduct Mage_Catalog_Model_Product */ if ($product->hasChildrenProducts()) { foreach ($product->getChildrenProducts() as $childProduct) { if ($image = $this->_resizeProductImage($childProduct, $imageType, $keepFrame)) { $imagesByType[$imageType][$childProduct->getId()] = $image; } } } // load image from configurable product for swapping fallback if ($image = $this->_resizeProductImage($product, $imageType, $keepFrame, null, true)) { $imagesByType[$imageType][$product->getId()] = $image; } } $array = array( 'option_labels' => $imagesByLabel, Mage_ConfigurableSwatches_Helper_Productimg::MEDIA_IMAGE_TYPE_SMALL => $imagesByType['small_image'], Mage_ConfigurableSwatches_Helper_Productimg::MEDIA_IMAGE_TYPE_BASE => $imagesByType['image'], ); $product->setConfigurableImagesFallbackArray($array); } return $product->getConfigurableImagesFallbackArray(); } /** * Resize specified type of image on the product for use in the fallback and returns the image URL * or returns the image URL for the specified image path if present * * @param Mage_Catalog_Model_Product $product * @param string $type * @param bool $keepFrame * @param string $image * @param bool $placeholder * @return string|bool */ protected function _resizeProductImage($product, $type, $keepFrame, $image = null, $placeholder = false) { $hasTypeData = $product->hasData($type) && $product->getData($type) != 'no_selection'; if ($image == 'no_selection') { $image = null; } if ($hasTypeData || $placeholder || $image) { $helper = Mage::helper('catalog/image') ->init($product, $type, $image) ->keepFrame(($hasTypeData || $image) ? $keepFrame : false) // don't keep frame if placeholder ; $size = Mage::getStoreConfig(Mage_Catalog_Helper_Image::XML_NODE_PRODUCT_BASE_IMAGE_WIDTH); if ($type == 'small_image') { $size = Mage::getStoreConfig(Mage_Catalog_Helper_Image::XML_NODE_PRODUCT_SMALL_IMAGE_WIDTH); } if (is_numeric($size)) { $helper->constrainOnly(true)->resize($size); } return (string)$helper; } return false; } /** * Groups media gallery images by local images and child images * * @param Mage_Catalog_Model_Product $product * @return void */ public function groupMediaGalleryImages(Mage_Catalog_Model_Product $product) { $mediaGallery = $product->getMediaGallery(); if (empty($mediaGallery['images'])) { return; //nothing to do here } $newMediaGalleryImages = array(); $configurableImages = array(); foreach ($mediaGallery['images'] as $mediaGalleryImage) { if ($mediaGalleryImage['product_id'] == $product->getId()) { $newMediaGalleryImages[] = $mediaGalleryImage; } else { $configurableImages[] = $mediaGalleryImage; } } $mediaGallery['images'] = $newMediaGalleryImages; $mediaGallery['configurable_images'] = $configurableImages; $product->setMediaGallery($mediaGallery); //reset product media images based on new grouping } /** * For given product set, attach media_gallery attribute values. * * @param array $products * @param int $storeId * @return void */ public function attachGallerySetToCollection(array $products, $storeId) { $productIds = array(); /* @var $product Mage_Catalog_Model_Product */ foreach ($products as $product) { $productIds[] = $product->getId(); if (!is_array($product->getChildrenProducts())) { continue; } /* @var $childProduct Mage_Catalog_Model_Product */ foreach ($product->getChildrenProducts() as $childProduct) { $productIds[] = $childProduct->getId(); } } $attrCode = self::MEDIA_GALLERY_ATTRIBUTE_CODE; /* @var $resourceModel Mage_Catalog_Model_Resource_Product_Attribute_Backend_Media */ $resourceModel = Mage::getResourceModel('catalog/product_attribute_backend_media'); $images = $resourceModel->loadGallerySet($productIds, $storeId); $relationship = array(); foreach ($products as $product) { $relationship[$product->getId()] = $product->getId(); if (!is_array($product->getChildrenProducts())) { continue; } /* @var $childProduct Mage_Catalog_Model_Product */ foreach ($product->getChildrenProducts() as $childProduct) { $relationship[$childProduct->getId()] = $product->getId(); } } foreach ($images as $image) { $productId = $image['product_id']; $realProductId = $relationship[$productId]; $product = $products[$realProductId]; if (is_null($image['label'])) { $image['label'] = $image['label_default']; } if (is_null($image['position'])) { $image['position'] = $image['position_default']; } if (is_null($image['disabled'])) { $image['disabled'] = $image['disabled_default']; } $value = $product->getData($attrCode); if (!$value) { $value = array( 'images' => array(), 'value' => array() ); } $value['images'][] = $image; $product->setData($attrCode, $value); } } /** * Determines which product attributes should be selected * when children products are attached to parent products * * @return array */ protected function _getChildrenProductsAttributes() { return array( 'small_image', 'image', 'image_label', 'small_image_label', Mage::helper('configurableswatches/productlist')->getSwatchAttribute()->getAttributeCode(), ); } /** * Attaches children product to each product via * ->setChildrenProducts() * * @param array $products * @param int $storeId * @return void */ public function attachChildrenProducts(array $products, $storeId) { $productIds = array(); /* @var $product Mage_Catalog_Model_Product */ foreach ($products as $product) { $productIds[] = $product->getId(); } $collection = Mage::getResourceModel( 'configurableswatches/catalog_product_type_configurable_product_collection'); $collection->setFlag('product_children', true) ->addStoreFilter($storeId) ->addAttributeToSelect($this->_getChildrenProductsAttributes()); $collection->addProductSetFilter($productIds); $collection->load(); $mapping = array(); /* @var $childProduct Mage_Catalog_Model_Product */ foreach ($collection as $childProduct) { foreach ($childProduct->getParentIds() as $parentId) { if (!isset($mapping[$parentId])) { $mapping[$parentId] = array(); } $mapping[$parentId][] = $childProduct; } } foreach ($mapping as $parentId => $childrenProducts) { $products[$parentId]->setChildrenProducts($childrenProducts); } } }