├── 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 |
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 |
--------------------------------------------------------------------------------