PageRenderTime 46ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/app/code/core/Mage/ImportExport/Model/Import/Entity/Product/Type/Configurable.php

https://bitbucket.org/andrewjleavitt/magestudy
PHP | 487 lines | 294 code | 32 blank | 161 comment | 51 complexity | 99d07cb120b3ce8be8a73a80a2a3af9f MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1, GPL-2.0, WTFPL
  1. <?php
  2. /**
  3. * Magento
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@magentocommerce.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade Magento to newer
  18. * versions in the future. If you wish to customize Magento for your
  19. * needs please refer to http://www.magentocommerce.com for more information.
  20. *
  21. * @category Mage
  22. * @package Mage_ImportExport
  23. * @copyright Copyright (c) 2010 Magento Inc. (http://www.magentocommerce.com)
  24. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  25. */
  26. /**
  27. * Import entity configurable product type model
  28. *
  29. * @category Mage
  30. * @package Mage_ImportExport
  31. * @author Magento Core Team <core@magentocommerce.com>
  32. */
  33. class Mage_ImportExport_Model_Import_Entity_Product_Type_Configurable
  34. extends Mage_ImportExport_Model_Import_Entity_Product_Type_Abstract
  35. {
  36. /**
  37. * Error codes.
  38. */
  39. const ERROR_ATTRIBUTE_CODE_IS_NOT_SUPER = 'attrCodeIsNotSuper';
  40. const ERROR_INVALID_PRICE_CORRECTION = 'invalidPriceCorr';
  41. const ERROR_INVALID_OPTION_VALUE = 'invalidOptionValue';
  42. const ERROR_INVALID_WEBSITE = 'invalidSuperAttrWebsite';
  43. /**
  44. * Validation failure message template definitions
  45. *
  46. * @var array
  47. */
  48. protected $_messageTemplates = array(
  49. self::ERROR_ATTRIBUTE_CODE_IS_NOT_SUPER => 'Attribute with this code is not super',
  50. self::ERROR_INVALID_PRICE_CORRECTION => 'Super attribute price correction value is invalid',
  51. self::ERROR_INVALID_OPTION_VALUE => 'Invalid option value',
  52. self::ERROR_INVALID_WEBSITE => 'Invalid website code for super attribute'
  53. );
  54. /**
  55. * Column names that holds values with particular meaning.
  56. *
  57. * @var array
  58. */
  59. protected $_particularAttributes = array(
  60. '_super_products_sku', '_super_attribute_code', '_super_attribute_option',
  61. '_super_attribute_price_corr', '_super_attribute_price_website'
  62. );
  63. /**
  64. * Reference array of existing product-attribute to product super attribute ID.
  65. *
  66. * product_1 (underscore) attribute_id_1 => product_super_attr_id_1,
  67. * product_1 (underscore) attribute_id_2 => product_super_attr_id_2,
  68. * ...,
  69. * product_n (underscore) attribute_id_n => product_super_attr_id_n
  70. *
  71. * @var array
  72. */
  73. protected $_productSuperAttrs = array();
  74. /**
  75. * Array of SKU to array of super attribute values for all products.
  76. *
  77. * array (
  78. * attr_set_name_1 => array(
  79. * product_id_1 => array(
  80. * super_attribute_code_1 => attr_value_1,
  81. * ...
  82. * super_attribute_code_n => attr_value_n
  83. * ),
  84. * ...
  85. * ),
  86. * ...
  87. * )
  88. *
  89. * @var array
  90. */
  91. protected $_skuSuperAttributeValues = array();
  92. /**
  93. * Array of SKU to array of super attributes data for validation new associated products.
  94. *
  95. * array (
  96. * product_id_1 => array(
  97. * super_attribute_id_1 => array(
  98. * value_index_1 => TRUE,
  99. * ...
  100. * value_index_n => TRUE
  101. * ),
  102. * ...
  103. * ),
  104. * ...
  105. * )
  106. *
  107. * @var array
  108. */
  109. protected $_skuSuperData = array();
  110. /**
  111. * Super attributes codes in a form of code => TRUE array pairs.
  112. *
  113. * @var array
  114. */
  115. protected $_superAttributes = array();
  116. /**
  117. * All super attributes values combinations for each attribute set.
  118. *
  119. * @var array
  120. */
  121. protected $_superAttrValuesCombs = null;
  122. /**
  123. * Add attribute parameters to appropriate attribute set.
  124. *
  125. * @param string $attrParams Name of attribute set.
  126. * @param array $attrParams Refined attribute parameters.
  127. * @return Mage_ImportExport_Model_Import_Entity_Product_Type_Abstract
  128. */
  129. protected function _addAttributeParams($attrSetName, array $attrParams)
  130. {
  131. // save super attributes for simplier and quicker search in future
  132. if ('select' == $attrParams['type'] && 1 == $attrParams['is_global'] && $attrParams['for_configurable']) {
  133. $this->_superAttributes[$attrParams['code']] = $attrParams;
  134. }
  135. return parent::_addAttributeParams($attrSetName, $attrParams);
  136. }
  137. /**
  138. * Get super attribute ID (if it is not possible - return NULL).
  139. *
  140. * @param int $productId
  141. * @param int $attributeId
  142. * @return array|null
  143. */
  144. protected function _getSuperAttributeId($productId, $attributeId)
  145. {
  146. if (isset($this->_productSuperAttrs["{$productId}_{$attributeId}"])) {
  147. return $this->_productSuperAttrs["{$productId}_{$attributeId}"];
  148. } else {
  149. return null;
  150. }
  151. }
  152. /**
  153. * Have we check attribute for is_required? Used as last chance to disable this type of check.
  154. *
  155. * @param string $attrCode
  156. * @return bool
  157. */
  158. protected function _isAttributeRequiredCheckNeeded($attrCode)
  159. {
  160. return !$this->_isAttributeSuper($attrCode); // do not check super attributes
  161. }
  162. /**
  163. * Is attribute is super-attribute?
  164. *
  165. * @param string $attrCode
  166. * @return boolean
  167. */
  168. protected function _isAttributeSuper($attrCode)
  169. {
  170. return isset($this->_superAttributes[$attrCode]);
  171. }
  172. /**
  173. * Validate particular attributes columns.
  174. *
  175. * @param array $rowData
  176. * @param int $rowNum
  177. * @return bool
  178. */
  179. protected function _isParticularAttributesValid(array $rowData, $rowNum)
  180. {
  181. if (!empty($rowData['_super_attribute_code'])) {
  182. $superAttrCode = $rowData['_super_attribute_code'];
  183. if (!$this->_isAttributeSuper($superAttrCode)) { // check attribute superity
  184. $this->_entityModel->addRowError(self::ERROR_ATTRIBUTE_CODE_IS_NOT_SUPER, $rowNum);
  185. return false;
  186. } elseif (isset($rowData['_super_attribute_option']) && strlen($rowData['_super_attribute_option'])) {
  187. $optionKey = strtolower($rowData['_super_attribute_option']);
  188. if (!isset($this->_superAttributes[$superAttrCode]['options'][$optionKey])) {
  189. $this->_entityModel->addRowError(self::ERROR_INVALID_OPTION_VALUE, $rowNum);
  190. return false;
  191. }
  192. // check price value
  193. if (!empty($rowData['_super_attribute_price_corr'])
  194. && !$this->_isPriceCorr($rowData['_super_attribute_price_corr'])
  195. ) {
  196. $this->_entityModel->addRowError(self::ERROR_INVALID_PRICE_CORRECTION, $rowNum);
  197. return false;
  198. }
  199. }
  200. }
  201. return true;
  202. }
  203. /**
  204. * Array of SKU to array of super attribute values for all products.
  205. *
  206. * @return Mage_ImportExport_Model_Import_Entity_Product_Type_Configurable
  207. */
  208. protected function _loadSkuSuperAttributeValues()
  209. {
  210. if ($this->_superAttributes) {
  211. $attrSetIdToName = $this->_entityModel->getAttrSetIdToName();
  212. $allowProductTypes = array();
  213. foreach (Mage::getConfig()
  214. ->getNode('global/catalog/product/type/configurable/allow_product_types')->children() as $type) {
  215. $allowProductTypes[] = $type->getName();
  216. }
  217. foreach (Mage::getResourceModel('catalog/product_collection')
  218. ->addFieldToFilter('type_id', $allowProductTypes)
  219. ->addAttributeToSelect(array_keys($this->_superAttributes)) as $product) {
  220. $attrSetName = $attrSetIdToName[$product->getAttributeSetId()];
  221. $data = array_intersect_key(
  222. $product->getData(),
  223. $this->_superAttributes
  224. );
  225. foreach ($data as $attrCode => $value) {
  226. $attrId = $this->_superAttributes[$attrCode]['id'];
  227. $this->_skuSuperAttributeValues[$attrSetName][$product->getId()][$attrId] = $value;
  228. }
  229. }
  230. }
  231. return $this;
  232. }
  233. /**
  234. * Array of SKU to array of super attribute values for all products.
  235. *
  236. * @return Mage_ImportExport_Model_Import_Entity_Product_Type_Configurable
  237. */
  238. protected function _loadSkuSuperData()
  239. {
  240. if (!$this->_skuSuperData) {
  241. $connection = $this->_entityModel->getConnection();
  242. $mainTable = Mage::getSingleton('core/resource')->getTableName('catalog/product_super_attribute');
  243. $priceTable = Mage::getSingleton('core/resource')->getTableName('catalog/product_super_attribute_pricing');
  244. $select = $connection->select()
  245. ->from(array('m' => $mainTable), array('product_id', 'attribute_id', 'product_super_attribute_id'))
  246. ->joinLeft(
  247. array('p' => $priceTable),
  248. $connection->quoteIdentifier('p.product_super_attribute_id') . ' = '
  249. . $connection->quoteIdentifier('m.product_super_attribute_id'),
  250. array('value_index')
  251. );
  252. foreach ($connection->fetchAll($select) as $row) {
  253. $attrId = $row['attribute_id'];
  254. $productId = $row['product_id'];
  255. if ($row['value_index']) {
  256. $this->_skuSuperData[$productId][$attrId][$row['value_index']] = true;
  257. }
  258. $this->_productSuperAttrs["{$productId}_{$attrId}"] = $row['product_super_attribute_id'];
  259. }
  260. }
  261. return $this;
  262. }
  263. /**
  264. * Validate and prepare data about super attributes and associated products.
  265. *
  266. * @param array $superData
  267. * @param array $superAttributes
  268. * @return Mage_ImportExport_Model_Import_Entity_Product_Type_Configurable
  269. */
  270. protected function _processSuperData(array $superData, array &$superAttributes)
  271. {
  272. if ($superData) {
  273. $usedCombs = array();
  274. // is associated products applicable?
  275. foreach (array_keys($superData['assoc_ids']) as $assocId) {
  276. if (!isset($this->_skuSuperAttributeValues[$superData['attr_set_code']][$assocId])) {
  277. continue;
  278. }
  279. if ($superData['used_attributes']) {
  280. $skuSuperValues = $this->_skuSuperAttributeValues[$superData['attr_set_code']][$assocId];
  281. $usedCombParts = array();
  282. foreach ($superData['used_attributes'] as $usedAttrId => $usedValues) {
  283. if (empty($skuSuperValues[$usedAttrId]) || !isset($usedValues[$skuSuperValues[$usedAttrId]])) {
  284. continue; // invalid value or value does not exists for associated product
  285. }
  286. $usedCombParts[] = $skuSuperValues[$usedAttrId];
  287. $superData['used_attributes'][$usedAttrId][$skuSuperValues[$usedAttrId]] = true;
  288. }
  289. $comb = implode('|', $usedCombParts);
  290. if (isset($usedCombs[$comb])) {
  291. continue; // super attributes values combination was already used
  292. }
  293. $usedCombs[$comb] = true;
  294. }
  295. $superAttributes['super_link'][] = array(
  296. 'product_id' => $assocId, 'parent_id' => $superData['product_id']
  297. );
  298. $superAttributes['relation'][] = array(
  299. 'parent_id' => $superData['product_id'], 'child_id' => $assocId
  300. );
  301. }
  302. // clean up unused values pricing
  303. foreach ($superData['used_attributes'] as $usedAttrId => $usedValues) {
  304. foreach ($usedValues as $optionId => $isUsed) {
  305. if (!$isUsed
  306. && isset($superAttributes['pricing'][$superData['product_id']][$usedAttrId])
  307. ) {
  308. foreach ($superAttributes['pricing'][$superData['product_id']][$usedAttrId] as $k => $params) {
  309. if ($optionId == $params['value_index']) {
  310. unset($superAttributes['pricing'][$superData['product_id']][$usedAttrId][$k]);
  311. }
  312. }
  313. }
  314. }
  315. }
  316. }
  317. return $this;
  318. }
  319. /**
  320. * Save product type specific data.
  321. *
  322. * @throws Exception
  323. * @return Mage_ImportExport_Model_Import_Entity_Product_Type_Abstract
  324. */
  325. public function saveData()
  326. {
  327. $connection = $this->_entityModel->getConnection();
  328. $mainTable = Mage::getSingleton('core/resource')->getTableName('catalog/product_super_attribute');
  329. $labelTable = Mage::getSingleton('core/resource')->getTableName('catalog/product_super_attribute_label');
  330. $priceTable = Mage::getSingleton('core/resource')->getTableName('catalog/product_super_attribute_pricing');
  331. $linkTable = Mage::getSingleton('core/resource')->getTableName('catalog/product_super_link');
  332. $relationTable = Mage::getSingleton('core/resource')->getTableName('catalog/product_relation');
  333. $newSku = $this->_entityModel->getNewSku();
  334. $oldSku = $this->_entityModel->getOldSku();
  335. $productSuperData = array();
  336. $productData = null;
  337. $nextAttrId = $this->_entityModel->getNextAutoincrement($mainTable);
  338. if ($this->_entityModel->getBehavior() == Mage_ImportExport_Model_Import::BEHAVIOR_APPEND) {
  339. $this->_loadSkuSuperData();
  340. }
  341. $this->_loadSkuSuperAttributeValues();
  342. while ($bunch = $this->_entityModel->getNextBunch()) {
  343. $superAttributes = array(
  344. 'attributes' => array(),
  345. 'labels' => array(),
  346. 'pricing' => array(),
  347. 'super_link' => array(),
  348. 'relation' => array()
  349. );
  350. foreach ($bunch as $rowNum => $rowData) {
  351. if (!$this->_entityModel->isRowAllowedToImport($rowData, $rowNum)) {
  352. continue;
  353. }
  354. // remember SCOPE_DEFAULT row data
  355. $scope = $this->_entityModel->getRowScope($rowData);
  356. if (Mage_ImportExport_Model_Import_Entity_Product::SCOPE_DEFAULT == $scope) {
  357. $productData = $newSku[$rowData[Mage_ImportExport_Model_Import_Entity_Product::COL_SKU]];
  358. if ($this->_type != $productData['type_id']) {
  359. $productData = null;
  360. continue;
  361. }
  362. $productId = $productData['entity_id'];
  363. $this->_processSuperData($productSuperData, $superAttributes);
  364. $productSuperData = array(
  365. 'product_id' => $productId,
  366. 'attr_set_code' => $productData['attr_set_code'],
  367. 'used_attributes' => empty($this->_skuSuperData[$productId])
  368. ? array() : $this->_skuSuperData[$productId],
  369. 'assoc_ids' => array()
  370. );
  371. } elseif (null === $productData) {
  372. continue;
  373. }
  374. if (!empty($rowData['_super_products_sku'])) {
  375. if (isset($newSku[$rowData['_super_products_sku']])) {
  376. $productSuperData['assoc_ids'][$newSku[$rowData['_super_products_sku']]['entity_id']] = true;
  377. } elseif (isset($oldSku[$rowData['_super_products_sku']])) {
  378. $productSuperData['assoc_ids'][$oldSku[$rowData['_super_products_sku']]['entity_id']] = true;
  379. }
  380. }
  381. if (empty($rowData['_super_attribute_code'])) {
  382. continue;
  383. }
  384. $attrParams = $this->_superAttributes[$rowData['_super_attribute_code']];
  385. if ($this->_getSuperAttributeId($productId, $attrParams['id'])) {
  386. $productSuperAttrId = $this->_getSuperAttributeId($productId, $attrParams['id']);
  387. } elseif (!isset($superAttributes['attributes'][$productId][$attrParams['id']])) {
  388. $productSuperAttrId = $nextAttrId++;
  389. $superAttributes['attributes'][$productId][$attrParams['id']] = array(
  390. 'product_super_attribute_id' => $productSuperAttrId, 'position' => 0
  391. );
  392. $superAttributes['labels'][] = array(
  393. 'product_super_attribute_id' => $productSuperAttrId,
  394. 'store_id' => 0,
  395. 'use_default' => 1,
  396. 'value' => $attrParams['frontend_label']
  397. );
  398. }
  399. if (isset($rowData['_super_attribute_option']) && strlen($rowData['_super_attribute_option'])) {
  400. $optionId = $attrParams['options'][strtolower($rowData['_super_attribute_option'])];
  401. if (!isset($productSuperData['used_attributes'][$attrParams['id']][$optionId])) {
  402. $productSuperData['used_attributes'][$attrParams['id']][$optionId] = false;
  403. }
  404. if (!empty($rowData['_super_attribute_price_corr'])) {
  405. $superAttributes['pricing'][] = array(
  406. 'product_super_attribute_id' => $productSuperAttrId,
  407. 'value_index' => $optionId,
  408. 'is_percent' => '%' == substr($rowData['_super_attribute_price_corr'], -1),
  409. 'pricing_value' => (float) rtrim($rowData['_super_attribute_price_corr'], '%'),
  410. 'website_id' => 0
  411. );
  412. }
  413. }
  414. }
  415. // save last product super data
  416. $this->_processSuperData($productSuperData, $superAttributes);
  417. // remove old data if needed
  418. if ($this->_entityModel->getBehavior() != Mage_ImportExport_Model_Import::BEHAVIOR_APPEND
  419. && $superAttributes['attributes']) {
  420. $quoted = $connection->quoteInto('IN (?)', array_keys($superAttributes['attributes']));
  421. $connection->delete($mainTable, "product_id {$quoted}");
  422. $connection->delete($linkTable, "parent_id {$quoted}");
  423. $connection->delete($relationTable, "parent_id {$quoted}");
  424. }
  425. $mainData = array();
  426. foreach ($superAttributes['attributes'] as $productId => $attributesData) {
  427. foreach ($attributesData as $attrId => $row) {
  428. $row['product_id'] = $productId;
  429. $row['attribute_id'] = $attrId;
  430. $mainData[] = $row;
  431. }
  432. }
  433. if ($mainData) {
  434. $connection->insertOnDuplicate($mainTable, $mainData);
  435. }
  436. if ($superAttributes['labels']) {
  437. $connection->insertOnDuplicate($labelTable, $superAttributes['labels']);
  438. }
  439. if ($superAttributes['pricing']) {
  440. $connection->insertOnDuplicate(
  441. $priceTable,
  442. $superAttributes['pricing'],
  443. array('is_percent', 'pricing_value')
  444. );
  445. }
  446. if ($superAttributes['super_link']) {
  447. $connection->insertOnDuplicate($linkTable, $superAttributes['super_link']);
  448. }
  449. if ($superAttributes['relation']) {
  450. $connection->insertOnDuplicate($relationTable, $superAttributes['relation']);
  451. }
  452. }
  453. return $this;
  454. }
  455. }