├── app ├── etc │ └── modules │ │ └── Bubble_Api.xml ├── code │ └── community │ │ └── Bubble │ │ └── Api │ │ ├── Helper │ │ ├── Data.php │ │ └── Catalog │ │ │ └── Product.php │ │ ├── Block │ │ └── Adminhtml │ │ │ └── System │ │ │ └── Config │ │ │ └── Fieldset │ │ │ └── Hint.php │ │ ├── etc │ │ ├── adminhtml.xml │ │ ├── wsdl.xml │ │ ├── config.xml │ │ ├── wsi.xml │ │ └── system.xml │ │ └── Model │ │ └── Catalog │ │ └── Product │ │ ├── Api.php │ │ └── Api │ │ └── V2.php ├── locale │ └── fr_FR │ │ └── Bubble_Api.csv └── design │ └── adminhtml │ └── base │ └── default │ └── template │ └── bubble │ └── api │ └── system │ └── config │ └── fieldset │ └── hint.phtml └── README.md /app/etc/modules/Bubble_Api.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | true 6 | community 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/code/community/Bubble/Api/Helper/Data.php: -------------------------------------------------------------------------------- 1 | getAttributeSetId($entityType, $attrSetName); 13 | } 14 | } -------------------------------------------------------------------------------- /app/code/community/Bubble/Api/Block/Adminhtml/System/Config/Fieldset/Hint.php: -------------------------------------------------------------------------------- 1 | toHtml(); 12 | } 13 | } -------------------------------------------------------------------------------- /app/locale/fr_FR/Bubble_Api.csv: -------------------------------------------------------------------------------- 1 | "General Settings","Général" 2 | "Categories Separator","Séparateur de catégories" 3 | "Full overview available","Aperçu complet disponible" 4 | "here","ici" 5 | "Bubble Api improves default features of Magento API such as association of simple products and configurable products.","Bubble Api permet d'ajouter des fonctionnalités à l'API Magento comme l'association de produits simples et configurables." 6 | "http://www.bubblecode.net/en/2012/04/20/magento-api-associate-simple-products-to-configurable-or-grouped-product/","http://www.bubblecode.net/2012/04/20/api-magento-associer-des-produits-simples-a-un-produit-configurable-ou-groupe/" -------------------------------------------------------------------------------- /app/code/community/Bubble/Api/etc/adminhtml.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Bubble Api 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | 3 | This extension improves default features of Magento API. 4 | 5 | It allows you to: 6 | 7 | * Associate simple products to configurable or grouped product; 8 | * Specify category names rather than the ids; 9 | * Specify the name of the attribute set rather than the id; 10 | * Specify options labels rather than the ids; 11 | * Specify the website code rather than the id. 12 | 13 | ## Installation 14 | 15 | ### Magento CE 1.6.x, 1.7.x 16 | 17 | Install with [modgit](https://github.com/jreinke/modgit): 18 | 19 | $ cd /path/to/magento 20 | $ modgit init 21 | $ modgit clone bubble-api https://github.com/jreinke/magento-improve-api.git 22 | 23 | or download package manually: 24 | 25 | * Download latest version [here](https://github.com/jreinke/magento-improve-api/archive/master.zip) 26 | * Unzip in Magento root folder 27 | * Clear cache 28 | 29 | ## How to associate simple products to configurable/grouped product 30 | 31 | Please refer to [this article](http://www.bubblecode.net/en/2012/04/20/magento-api-associate-simple-products-to-configurable-or-grouped-product/). -------------------------------------------------------------------------------- /app/code/community/Bubble/Api/etc/wsdl.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/design/adminhtml/base/default/template/bubble/api/system/config/fieldset/hint.phtml: -------------------------------------------------------------------------------- 1 | 6 |
7 |
8 | 9 | Fork me on GitHub 10 | 11 |

helper('bubble_api')->__('Bubble Api improves default features of Magento API such as association of simple products and configurable products.'); ?>

12 |

helper('bubble_api')->__('Full overview available'); ?> helper('bubble_api')->__('here'); ?>.

13 |
14 |
-------------------------------------------------------------------------------- /app/code/community/Bubble/Api/etc/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 1.0.0 6 | 7 | 8 | 9 | 10 | 11 | Bubble_Api_Helper 12 | 13 | 14 | 15 | 16 | 17 | Bubble_Api_Model_Catalog_Product_Api 18 | Bubble_Api_Model_Catalog_Product_Api_V2 19 | 20 | 21 | 22 | 23 | 24 | Bubble_Api_Block 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | Bubble_Api.csv 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/code/community/Bubble/Api/etc/wsi.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/code/community/Bubble/Api/etc/system.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 1000 7 | 8 | 9 | 10 | 11 | 12 | bubble 13 | 100 14 | 1 15 | 1 16 | 1 17 | 18 | 19 | bubble_api/adminhtml_system_config_fieldset_hint 20 | 0 21 | 1 22 | 1 23 | 1 24 | 25 | 26 | 27 | 10 28 | 1 29 | 0 30 | 0 31 | 32 | 33 | 34 | text 35 | 100 36 | 1 37 | 1 38 | 1 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/code/community/Bubble/Api/Model/Catalog/Product/Api.php: -------------------------------------------------------------------------------- 1 | getAttributeSetIdByName($set); 10 | } 11 | 12 | return parent::create($type, $set, $sku, $productData, $store); 13 | } 14 | 15 | protected function _prepareDataForSave($product, $productData) 16 | { 17 | /* @var $product Mage_Catalog_Model_Product */ 18 | 19 | if (isset($productData['categories'])) { 20 | $categoryIds = Mage::helper('bubble_api/catalog_product') 21 | ->getCategoryIdsByNames((array) $productData['categories']); 22 | if (!empty($categoryIds)) { 23 | $productData['categories'] = array_unique($categoryIds); 24 | } 25 | } 26 | 27 | if (isset($productData['website_ids'])) { 28 | $websiteIds = $productData['website_ids']; 29 | foreach ($websiteIds as $i => $websiteId) { 30 | if (!is_numeric($websiteId)) { 31 | $website = Mage::app()->getWebsite($websiteId); 32 | if ($website->getId()) { 33 | $websiteIds[$i] = $website->getId(); 34 | } 35 | } 36 | } 37 | $product->setWebsiteIds($websiteIds); 38 | unset($productData['website_ids']); 39 | } 40 | 41 | foreach ($productData as $code => $value) { 42 | $productData[$code] = Mage::helper('bubble_api/catalog_product') 43 | ->getOptionKeyByLabel($code, $value); 44 | } 45 | 46 | parent::_prepareDataForSave($product, $productData); 47 | 48 | if (isset($productData['associated_skus'])) { 49 | $simpleSkus = $productData['associated_skus']; 50 | $priceChanges = isset($productData['price_changes']) ? $productData['price_changes'] : array(); 51 | $configurableAttributes = isset($productData['configurable_attributes']) ? $productData['configurable_attributes'] : array(); 52 | Mage::helper('bubble_api/catalog_product')->associateProducts($product, $simpleSkus, $priceChanges, $configurableAttributes); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /app/code/community/Bubble/Api/Model/Catalog/Product/Api/V2.php: -------------------------------------------------------------------------------- 1 | getAttributeSetIdByName($set); 10 | } 11 | 12 | return parent::create($type, $set, $sku, $productData, $store); 13 | } 14 | 15 | protected function _prepareDataForSave($product, $productData) 16 | { 17 | /* @var $product Mage_Catalog_Model_Product */ 18 | 19 | if (property_exists($productData, 'categories')) { 20 | $categoryIds = Mage::helper('bubble_api/catalog_product') 21 | ->getCategoryIdsByNames((array) $productData->categories); 22 | if (!empty($categoryIds)) { 23 | $productData->categories = array_unique($categoryIds); 24 | } 25 | } 26 | 27 | if (property_exists($productData, 'additional_attributes')) { 28 | $singleDataExists = property_exists((object) $productData->additional_attributes, 'single_data'); 29 | $multiDataExists = property_exists((object) $productData->additional_attributes, 'multi_data'); 30 | if ($singleDataExists || $multiDataExists) { 31 | if ($singleDataExists) { 32 | foreach ($productData->additional_attributes->single_data as $_attribute) { 33 | $_attrCode = $_attribute->key; 34 | $productData->$_attrCode = Mage::helper('bubble_api/catalog_product') 35 | ->getOptionKeyByLabel($_attrCode, $_attribute->value); 36 | } 37 | } 38 | if ($multiDataExists) { 39 | foreach ($productData->additional_attributes->multi_data as $_attribute) { 40 | $_attrCode = $_attribute->key; 41 | $productData->$_attrCode = Mage::helper('bubble_api/catalog_product') 42 | ->getOptionKeyByLabel($_attrCode, $_attribute->value); 43 | } 44 | } 45 | } else { 46 | foreach ($productData->additional_attributes as $_attrCode => $_value) { 47 | $productData->$_attrCode = Mage::helper('bubble_api/catalog_product') 48 | ->getOptionKeyByLabel($_attrCode, $_value); 49 | } 50 | } 51 | unset($productData->additional_attributes); 52 | } 53 | 54 | if (property_exists($productData, 'website_ids')) { 55 | $websiteIds = (array) $productData->website_ids; 56 | foreach ($websiteIds as $i => $websiteId) { 57 | if (!is_numeric($websiteId)) { 58 | $website = Mage::app()->getWebsite($websiteId); 59 | if ($website->getId()) { 60 | $websiteIds[$i] = $website->getId(); 61 | } 62 | } 63 | } 64 | $product->setWebsiteIds($websiteIds); 65 | unset($productData->website_ids); 66 | } 67 | 68 | parent::_prepareDataForSave($product, $productData); 69 | 70 | if (property_exists($productData, 'associated_skus')) { 71 | $simpleSkus = (array) $productData->associated_skus; 72 | $priceChanges = array(); 73 | if (property_exists($productData, 'price_changes')) { 74 | if (key($productData->price_changes) === 0) { 75 | $priceChanges = $productData->price_changes[0]; 76 | } else { 77 | $priceChanges = $productData->price_changes; 78 | } 79 | } 80 | $configurableAttributes = array(); 81 | if (property_exists($productData, 'configurable_attributes')) { 82 | $configurableAttributes = $productData->configurable_attributes; 83 | } 84 | Mage::helper('bubble_api/catalog_product')->associateProducts($product, $simpleSkus, $priceChanges, $configurableAttributes); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /app/code/community/Bubble/Api/Helper/Catalog/Product.php: -------------------------------------------------------------------------------- 1 | getCollection() 17 | ->addFieldToFilter('sku', array('in' => (array) $simpleSkus)) 18 | ->addFieldToFilter('type_id', Mage_Catalog_Model_Product_Type::TYPE_SIMPLE) 19 | ->getAllIds(); 20 | 21 | $oldProductIds = Mage::getModel('catalog/product_type_configurable')->setProduct($product)->getUsedProductCollection() 22 | ->addAttributeToSelect('*') 23 | ->addFilterByRequiredOptions() 24 | ->getAllIds(); 25 | 26 | $usedProductIds = array_diff($newProductIds, $oldProductIds); 27 | 28 | if (!empty($newProductIds) && $product->isConfigurable()) { 29 | $this->_initConfigurableAttributesData($product, $newProductIds, $priceChanges, $configurableAttributes); 30 | } 31 | 32 | if (!empty($usedProductIds) && $product->isGrouped()) { 33 | $relations = array_fill_keys($usedProductIds, array('qty' => 0, 'position' => 0)); 34 | $product->setGroupedLinkData($relations); 35 | } 36 | } 37 | 38 | return $this; 39 | } 40 | 41 | /** 42 | * @param array $categoryNames 43 | * @return array 44 | */ 45 | public function getCategoryIdsByNames($categoryNames) 46 | { 47 | $categories = array(); 48 | $separator = $this->_getCatagoriesSeparator(); 49 | foreach ($categoryNames as $category) { 50 | if (is_string($category) && !is_numeric($category)) { 51 | $pieces = explode($separator, $category); 52 | $addCategories = array(); 53 | $parentIds = array(); 54 | foreach ($pieces as $level => $name) { 55 | $collection = Mage::getModel('catalog/category')->getCollection() 56 | ->setStoreId(0) 57 | ->addFieldToFilter('level', $level + 2) 58 | ->addAttributeToFilter('name', $name); 59 | if (!empty($parentIds)) { 60 | $collection->getSelect()->where('parent_id IN (?)', $parentIds); 61 | } 62 | $parentIds = array(); 63 | if ($collection->count()) { 64 | foreach ($collection as $category) { 65 | $addCategories[] = (int) $category->getId(); 66 | if ($level > 0) { 67 | $addCategories[] = (int) $category->getParentId(); 68 | } 69 | $parentIds[] = $category->getId(); 70 | } 71 | } 72 | } 73 | if (!empty($addCategories)) { 74 | $categories = array_merge($categories, $addCategories); 75 | } 76 | } 77 | } 78 | 79 | return !empty($categories) ? $categories : $categoryNames; 80 | } 81 | 82 | /** 83 | * @param string $attributeCode 84 | * @param string $label 85 | * @return mixed 86 | */ 87 | public function getOptionKeyByLabel($attributeCode, $label) 88 | { 89 | $attribute = Mage::getModel('catalog/product')->getResource() 90 | ->getAttribute($attributeCode); 91 | if ($attribute && $attribute->getId() && $attribute->usesSource()) { 92 | foreach ($attribute->getSource()->getAllOptions(true, true) as $option) { 93 | if ($label == $option['label']) { 94 | return $option['value']; 95 | } 96 | } 97 | } 98 | 99 | return $label; 100 | } 101 | 102 | protected function _getCatagoriesSeparator() 103 | { 104 | return Mage::getStoreConfig(self::CATEGORIES_SEPARATOR_PATH_XML); 105 | } 106 | 107 | /** 108 | * @param Mage_Catalog_Model_Product $mainProduct 109 | * @param array $simpleProductIds 110 | * @param array $priceChanges 111 | * @return Bubble_Api_Helper_Catalog_Product 112 | */ 113 | protected function _initConfigurableAttributesData(Mage_Catalog_Model_Product $mainProduct, $simpleProductIds, $priceChanges = array(), $configurableAttributes = array()) 114 | { 115 | if (!$mainProduct->isConfigurable() || empty($simpleProductIds)) { 116 | return $this; 117 | } 118 | 119 | $mainProduct->setConfigurableProductsData(array_flip($simpleProductIds)); 120 | $productType = $mainProduct->getTypeInstance(true); 121 | $productType->setProduct($mainProduct); 122 | $attributesData = $productType->getConfigurableAttributesAsArray(); 123 | 124 | if (empty($attributesData)) { 125 | // Auto generation if configurable product has no attribute 126 | $attributeIds = array(); 127 | foreach ($productType->getSetAttributes() as $attribute) { 128 | if ($productType->canUseAttribute($attribute)) { 129 | $attributeIds[] = $attribute->getAttributeId(); 130 | } 131 | } 132 | $productType->setUsedProductAttributeIds($attributeIds); 133 | $attributesData = $productType->getConfigurableAttributesAsArray(); 134 | } 135 | if (!empty($configurableAttributes)){ 136 | foreach ($attributesData as $idx => $val) { 137 | if (!in_array($val['attribute_id'], $configurableAttributes)) { 138 | unset($attributesData[$idx]); 139 | } 140 | } 141 | } 142 | 143 | $products = Mage::getModel('catalog/product')->getCollection() 144 | ->addIdFilter($simpleProductIds); 145 | 146 | if (count($products)) { 147 | foreach ($attributesData as &$attribute) { 148 | $attribute['label'] = $attribute['frontend_label']; 149 | $attributeCode = $attribute['attribute_code']; 150 | foreach ($products as $product) { 151 | $product->load($product->getId()); 152 | $optionId = $product->getData($attributeCode); 153 | $isPercent = 0; 154 | $priceChange = 0; 155 | if (!empty($priceChanges) && isset($priceChanges[$attributeCode])) { 156 | $optionText = $product->getResource() 157 | ->getAttribute($attribute['attribute_code']) 158 | ->getSource() 159 | ->getOptionText($optionId); 160 | if (isset($priceChanges[$attributeCode][$optionText])) { 161 | if (false !== strpos($priceChanges[$attributeCode][$optionText], '%')) { 162 | $isPercent = 1; 163 | } 164 | $priceChange = preg_replace('/[^0-9\.,-]/', '', $priceChanges[$attributeCode][$optionText]); 165 | $priceChange = (float) str_replace(',', '.', $priceChange); 166 | } 167 | } 168 | $attribute['values'][$optionId] = array( 169 | 'value_index' => $optionId, 170 | 'is_percent' => $isPercent, 171 | 'pricing_value' => $priceChange, 172 | ); 173 | } 174 | } 175 | $mainProduct->setConfigurableAttributesData($attributesData); 176 | } 177 | 178 | return $this; 179 | } 180 | } 181 | --------------------------------------------------------------------------------