PageRenderTime 92ms CodeModel.GetById 47ms RepoModel.GetById 2ms app.codeStats 0ms

/app/code/core/Mage/ImportExport/Model/Export/Entity/Product.php

https://bitbucket.org/claudiu_marginean/magento-hg-mirror
PHP | 794 lines | 561 code | 65 blank | 168 comment | 79 complexity | dacf53165b45b69700e16909763f68b1 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. * Export entity product model
  28. *
  29. * @category Mage
  30. * @package Mage_ImportExport
  31. * @author Magento Core Team <core@magentocommerce.com>
  32. */
  33. class Mage_ImportExport_Model_Export_Entity_Product extends Mage_ImportExport_Model_Export_Entity_Abstract
  34. {
  35. const CONFIG_KEY_PRODUCT_TYPES = 'global/importexport/export_product_types';
  36. /**
  37. * Value that means all entities (e.g. websites, groups etc.)
  38. */
  39. const VALUE_ALL = 'all';
  40. /**
  41. * Permanent column names.
  42. *
  43. * Names that begins with underscore is not an attribute. This name convention is for
  44. * to avoid interference with same attribute name.
  45. */
  46. const COL_STORE = '_store';
  47. const COL_ATTR_SET = '_attribute_set';
  48. const COL_TYPE = '_type';
  49. const COL_CATEGORY = '_category';
  50. const COL_SKU = 'sku';
  51. /**
  52. * Pairs of attribute set ID-to-name.
  53. *
  54. * @var array
  55. */
  56. protected $_attrSetIdToName = array();
  57. /**
  58. * Categories ID to text-path hash.
  59. *
  60. * @var array
  61. */
  62. protected $_categories = array();
  63. /**
  64. * Attributes with index (not label) value.
  65. *
  66. * @var array
  67. */
  68. protected $_indexValueAttributes = array(
  69. 'status',
  70. 'tax_class_id',
  71. 'visibility',
  72. 'enable_googlecheckout',
  73. 'gift_message_available',
  74. 'custom_design'
  75. );
  76. /**
  77. * Permanent entity columns.
  78. *
  79. * @var array
  80. */
  81. protected $_permanentAttributes = array(self::COL_SKU);
  82. /**
  83. * Array of supported product types as keys with appropriate model object as value.
  84. *
  85. * @var array
  86. */
  87. protected $_productTypeModels = array();
  88. /**
  89. * Array of pairs store ID to its code.
  90. *
  91. * @var array
  92. */
  93. protected $_storeIdToCode = array();
  94. /**
  95. * Website ID-to-code.
  96. *
  97. * @var array
  98. */
  99. protected $_websiteIdToCode = array();
  100. /**
  101. * Constructor.
  102. *
  103. * @return void
  104. */
  105. public function __construct()
  106. {
  107. parent::__construct();
  108. $this->_initTypeModels()
  109. ->_initAttrValues()
  110. ->_initStores()
  111. ->_initAttributeSets()
  112. ->_initWebsites()
  113. ->_initCategories();
  114. }
  115. /**
  116. * Initialize attribute sets code-to-id pairs.
  117. *
  118. * @return Mage_ImportExport_Model_Export_Entity_Product
  119. */
  120. protected function _initAttributeSets()
  121. {
  122. $productTypeId = Mage::getModel('catalog/product')->getResource()->getTypeId();
  123. foreach (Mage::getResourceModel('eav/entity_attribute_set_collection')
  124. ->setEntityTypeFilter($productTypeId) as $attributeSet) {
  125. $this->_attrSetIdToName[$attributeSet->getId()] = $attributeSet->getAttributeSetName();
  126. }
  127. return $this;
  128. }
  129. /**
  130. * Initialize categories ID to text-path hash.
  131. *
  132. * @return Mage_ImportExport_Model_Export_Entity_Product
  133. */
  134. protected function _initCategories()
  135. {
  136. $collection = Mage::getResourceModel('catalog/category_collection')->addNameToResult();
  137. /* @var $collection Mage_Catalog_Model_Resource_Eav_Mysql4_Category_Collection */
  138. foreach ($collection as $category) {
  139. $structure = preg_split('#/+#', $category->getPath());
  140. $pathSize = count($structure);
  141. if ($pathSize > 2) {
  142. $path = array();
  143. for ($i = 2; $i < $pathSize; $i++) {
  144. $path[] = $collection->getItemById($structure[$i])->getName();
  145. }
  146. $this->_categories[$category->getId()] = implode('/', $path);
  147. }
  148. }
  149. return $this;
  150. }
  151. /**
  152. * Initialize product type models.
  153. *
  154. * @throws Exception
  155. * @return Mage_ImportExport_Model_Export_Entity_Product
  156. */
  157. protected function _initTypeModels()
  158. {
  159. $config = Mage::getConfig()->getNode(self::CONFIG_KEY_PRODUCT_TYPES)->asCanonicalArray();
  160. foreach ($config as $type => $typeModel) {
  161. if (!($model = Mage::getModel($typeModel, array($this, $type)))) {
  162. Mage::throwException("Entity type model '{$typeModel}' is not found");
  163. }
  164. if (! $model instanceof Mage_ImportExport_Model_Export_Entity_Product_Type_Abstract) {
  165. Mage::throwException(
  166. Mage::helper('importexport')->__('Entity type model must be an instance of Mage_ImportExport_Model_Export_Entity_Product_Type_Abstract')
  167. );
  168. }
  169. if ($model->isSuitable()) {
  170. $this->_productTypeModels[$type] = $model;
  171. $this->_disabledAttrs = array_merge($this->_disabledAttrs, $model->getDisabledAttrs());
  172. $this->_indexValueAttributes = array_merge(
  173. $this->_indexValueAttributes, $model->getIndexValueAttributes()
  174. );
  175. }
  176. }
  177. if (!$this->_productTypeModels) {
  178. Mage::throwException(Mage::helper('importexport')->__('There are no product types available for export'));
  179. }
  180. $this->_disabledAttrs = array_unique($this->_disabledAttrs);
  181. return $this;
  182. }
  183. /**
  184. * Initialize website values.
  185. *
  186. * @return Mage_ImportExport_Model_Export_Entity_Product
  187. */
  188. protected function _initWebsites()
  189. {
  190. /** @var $website Mage_Core_Model_Website */
  191. foreach (Mage::app()->getWebsites() as $website) {
  192. $this->_websiteIdToCode[$website->getId()] = $website->getCode();
  193. }
  194. return $this;
  195. }
  196. /**
  197. * Prepare products tier prices
  198. *
  199. * @param array $productIds
  200. * @return array
  201. */
  202. protected function _prepareTierPrices(array $productIds)
  203. {
  204. $resource = Mage::getSingleton('core/resource');
  205. $select = $this->_connection->select()
  206. ->from($resource->getTableName('catalog/product_attribute_tier_price'))
  207. ->where('entity_id IN(?)', $productIds);
  208. $rowTierPrices = array();
  209. $stmt = $this->_connection->query($select);
  210. while ($tierRow = $stmt->fetch()) {
  211. $rowTierPrices[$tierRow['entity_id']][] = array(
  212. '_tier_price_customer_group' => $tierRow['all_groups']
  213. ? self::VALUE_ALL : $tierRow['customer_group_id'],
  214. '_tier_price_website' => 0 == $tierRow['website_id']
  215. ? self::VALUE_ALL
  216. : $this->_websiteIdToCode[$tierRow['website_id']],
  217. '_tier_price_qty' => $tierRow['qty'],
  218. '_tier_price_price' => $tierRow['value']
  219. );
  220. }
  221. return $rowTierPrices;
  222. }
  223. /**
  224. * Prepare catalog inventory
  225. *
  226. * @param array $productIds
  227. * @return array
  228. */
  229. protected function _prepareCatalogInventory(array $productIds)
  230. {
  231. $select = $this->_connection->select()
  232. ->from(Mage::getResourceModel('cataloginventory/stock_item')->getMainTable())
  233. ->where('product_id IN (?)', $productIds);
  234. $stmt = $this->_connection->query($select);
  235. $stockItemRows = array();
  236. while ($stockItemRow = $stmt->fetch()) {
  237. $productId = $stockItemRow['product_id'];
  238. unset(
  239. $stockItemRow['item_id'], $stockItemRow['product_id'], $stockItemRow['low_stock_date'],
  240. $stockItemRow['stock_id'], $stockItemRow['stock_status_changed_automatically']
  241. );
  242. $stockItemRows[$productId] = $stockItemRow;
  243. }
  244. return $stockItemRows;
  245. }
  246. /**
  247. * Prepare product links
  248. *
  249. * @param array $productIds
  250. * @return array
  251. */
  252. protected function _prepareLinks(array $productIds)
  253. {
  254. $resource = Mage::getSingleton('core/resource');
  255. $select = $this->_connection->select()
  256. ->from(
  257. array('cpl' => $resource->getTableName('catalog/product_link')),
  258. array(
  259. 'cpl.product_id', 'cpe.sku', 'cpl.link_type_id',
  260. 'position' => 'cplai.value', 'default_qty' => 'cplad.value'
  261. )
  262. )
  263. ->joinLeft(
  264. array('cpe' => $resource->getTableName('catalog/product')),
  265. '(cpe.entity_id = cpl.linked_product_id)',
  266. array()
  267. )
  268. ->joinLeft(
  269. array('cpla' => $resource->getTableName('catalog/product_link_attribute')),
  270. '(cpla.link_type_id = cpl.link_type_id AND cpla.product_link_attribute_code = "position")',
  271. array()
  272. )
  273. ->joinLeft(
  274. array('cplaq' => $resource->getTableName('catalog/product_link_attribute')),
  275. '(cplaq.link_type_id = cpl.link_type_id AND cplaq.product_link_attribute_code = "qty")',
  276. array()
  277. )
  278. ->joinLeft(
  279. array('cplai' => $resource->getTableName('catalog/product_link_attribute_int')),
  280. '(cplai.link_id = cpl.link_id AND cplai.product_link_attribute_id = cpla.product_link_attribute_id)',
  281. array()
  282. )
  283. ->joinLeft(
  284. array('cplad' => $resource->getTableName('catalog/product_link_attribute_decimal')),
  285. '(cplad.link_id = cpl.link_id AND cplad.product_link_attribute_id = cplaq.product_link_attribute_id)',
  286. array()
  287. )
  288. ->where('cpl.link_type_id IN (?)', array(
  289. Mage_Catalog_Model_Product_Link::LINK_TYPE_RELATED,
  290. Mage_Catalog_Model_Product_Link::LINK_TYPE_UPSELL,
  291. Mage_Catalog_Model_Product_Link::LINK_TYPE_CROSSSELL,
  292. Mage_Catalog_Model_Product_Link::LINK_TYPE_GROUPED
  293. ))
  294. ->where('cpl.product_id IN (?)', $productIds);
  295. $stmt = $this->_connection->query($select);
  296. $linksRows = array();
  297. while ($linksRow = $stmt->fetch()) {
  298. $linksRows[$linksRow['product_id']][$linksRow['link_type_id']][] = array(
  299. 'sku' => $linksRow['sku'],
  300. 'position' => $linksRow['position'],
  301. 'default_qty' => $linksRow['default_qty']
  302. );
  303. }
  304. return $linksRows;
  305. }
  306. /**
  307. * Prepare configurable product data
  308. *
  309. * @param array $productIds
  310. * @return array
  311. */
  312. protected function _prepareConfigurableProductData(array $productIds)
  313. {
  314. $resource = Mage::getSingleton('core/resource');
  315. $select = $this->_connection->select()
  316. ->from(
  317. array('cpsl' => $resource->getTableName('catalog/product_super_link')),
  318. array('cpsl.parent_id', 'cpe.sku')
  319. )
  320. ->joinLeft(
  321. array('cpe' => $resource->getTableName('catalog/product')),
  322. '(cpe.entity_id = cpsl.product_id)',
  323. array()
  324. )
  325. ->where('parent_id IN (?)', $productIds);
  326. $stmt = $this->_connection->query($select);
  327. $configurableData = array();
  328. while ($cfgLinkRow = $stmt->fetch()) {
  329. $configurableData[$cfgLinkRow['parent_id']][] = array('_super_products_sku' => $cfgLinkRow['sku']);
  330. }
  331. return $configurableData;
  332. }
  333. /**
  334. * Prepare configurable product price
  335. *
  336. * @param array $productIds
  337. * @return array
  338. */
  339. protected function _prepareConfigurableProductPrice(array $productIds)
  340. {
  341. $resource = Mage::getSingleton('core/resource');
  342. $select = $this->_connection->select()
  343. ->from(
  344. array('cpsa' => $resource->getTableName('catalog/product_super_attribute')),
  345. array(
  346. 'cpsa.product_id', 'ea.attribute_code', 'eaov.value', 'cpsap.pricing_value', 'cpsap.is_percent'
  347. )
  348. )
  349. ->joinLeft(
  350. array('cpsap' => $resource->getTableName('catalog/product_super_attribute_pricing')),
  351. '(cpsap.product_super_attribute_id = cpsa.product_super_attribute_id)',
  352. array()
  353. )
  354. ->joinLeft(
  355. array('ea' => $resource->getTableName('eav/attribute')),
  356. '(ea.attribute_id = cpsa.attribute_id)',
  357. array()
  358. )
  359. ->joinLeft(
  360. array('eaov' => $resource->getTableName('eav/attribute_option_value')),
  361. '(eaov.option_id = cpsap.value_index AND store_id = 0)',
  362. array()
  363. )
  364. ->where('cpsa.product_id IN (?)', $productIds);
  365. $configurablePrice = array();
  366. $stmt = $this->_connection->query($select);
  367. while ($priceRow = $stmt->fetch()) {
  368. $configurablePrice[$priceRow['product_id']][] = array(
  369. '_super_attribute_code' => $priceRow['attribute_code'],
  370. '_super_attribute_option' => $priceRow['value'],
  371. '_super_attribute_price_corr' => $priceRow['pricing_value'] . ($priceRow['is_percent'] ? '%' : '')
  372. );
  373. }
  374. return $configurablePrice;
  375. }
  376. /**
  377. * Export process.
  378. *
  379. * @return string
  380. */
  381. public function export()
  382. {
  383. /** @var $collection Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection */
  384. $validAttrCodes = $this->_getExportAttrCodes();
  385. $writer = $this->getWriter();
  386. $resource = Mage::getSingleton('core/resource');
  387. $dataRows = array();
  388. $rowCategories = array();
  389. $rowWebsites = array();
  390. $rowTierPrices = array();
  391. $stockItemRows = array();
  392. $linksRows = array();
  393. $gfAmountFields = array();
  394. $defaultStoreId = Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID;
  395. $collection = $this->_prepareEntityCollection(Mage::getResourceModel('catalog/product_collection'));
  396. // prepare multi-store values and system columns values
  397. foreach ($this->_storeIdToCode as $storeId => &$storeCode) { // go through all stores
  398. $collection->setStoreId($storeId)
  399. ->load();
  400. if ($defaultStoreId == $storeId) {
  401. $collection->addCategoryIds()->addWebsiteNamesToResult();
  402. // tier price data getting only once
  403. $rowTierPrices = $this->_prepareTierPrices($collection->getAllIds());
  404. }
  405. foreach ($collection as $itemId => $item) { // go through all products
  406. $rowIsEmpty = true; // row is empty by default
  407. foreach ($validAttrCodes as &$attrCode) { // go through all valid attribute codes
  408. $attrValue = $item->getData($attrCode);
  409. if (!empty($this->_attributeValues[$attrCode])) {
  410. if (isset($this->_attributeValues[$attrCode][$attrValue])) {
  411. $attrValue = $this->_attributeValues[$attrCode][$attrValue];
  412. } else {
  413. $attrValue = null;
  414. }
  415. }
  416. // do not save value same as default or not existent
  417. if ($storeId != $defaultStoreId
  418. && isset($dataRows[$itemId][$defaultStoreId][$attrCode])
  419. && $dataRows[$itemId][$defaultStoreId][$attrCode] == $attrValue
  420. ) {
  421. $attrValue = null;
  422. }
  423. if (is_scalar($attrValue)) {
  424. $dataRows[$itemId][$storeId][$attrCode] = $attrValue;
  425. $rowIsEmpty = false; // mark row as not empty
  426. }
  427. }
  428. if ($rowIsEmpty) { // remove empty rows
  429. unset($dataRows[$itemId][$storeId]);
  430. } else {
  431. $attrSetId = $item->getAttributeSetId();
  432. $dataRows[$itemId][$storeId][self::COL_STORE] = $storeCode;
  433. $dataRows[$itemId][$storeId][self::COL_ATTR_SET] = $this->_attrSetIdToName[$attrSetId];
  434. $dataRows[$itemId][$storeId][self::COL_TYPE] = $item->getTypeId();
  435. if ($defaultStoreId == $storeId) {
  436. $rowWebsites[$itemId] = $item->getWebsites();
  437. $rowCategories[$itemId] = $item->getCategoryIds();
  438. }
  439. }
  440. $item = null;
  441. }
  442. $collection->clear();
  443. }
  444. // remove root categories
  445. foreach ($rowCategories as $productId => &$categories) {
  446. $categories = array_intersect($categories, array_keys($this->_categories));
  447. }
  448. // prepare catalog inventory information
  449. $productIds = array_keys($dataRows);
  450. $stockItemRows = $this->_prepareCatalogInventory($productIds);
  451. // prepare links information
  452. $this->_prepareLinks($productIds);
  453. $linkIdColPrefix = array(
  454. Mage_Catalog_Model_Product_Link::LINK_TYPE_RELATED => '_links_related_',
  455. Mage_Catalog_Model_Product_Link::LINK_TYPE_UPSELL => '_links_upsell_',
  456. Mage_Catalog_Model_Product_Link::LINK_TYPE_CROSSSELL => '_links_crosssell_',
  457. Mage_Catalog_Model_Product_Link::LINK_TYPE_GROUPED => '_associated_'
  458. );
  459. // prepare configurable products data
  460. $configurableData = $this->_prepareConfigurableProductData($productIds);
  461. $configurablePrice = array();
  462. if ($configurableData) {
  463. $configurablePrice = $this->_prepareConfigurableProductPrice($productIds);
  464. foreach ($configurableData as $productId => &$rows) {
  465. if (isset($configurablePrice[$productId])) {
  466. $largest = max(count($rows), count($configurablePrice[$productId]));
  467. for ($i = 0; $i < $largest; $i++) {
  468. if (!isset($configurableData[$productId][$i])) {
  469. $configurableData[$productId][$i] = array();
  470. }
  471. if (isset($configurablePrice[$productId][$i])) {
  472. $configurableData[$productId][$i] = array_merge(
  473. $configurableData[$productId][$i],
  474. $configurablePrice[$productId][$i]
  475. );
  476. }
  477. }
  478. }
  479. }
  480. unset($configurablePrice);
  481. }
  482. // prepare custom options information
  483. $customOptionsData = array();
  484. $customOptionsDataPre = array();
  485. $customOptCols = array(
  486. '_custom_option_store', '_custom_option_type', '_custom_option_title', '_custom_option_is_required',
  487. '_custom_option_price', '_custom_option_sku', '_custom_option_max_characters',
  488. '_custom_option_sort_order', '_custom_option_row_title', '_custom_option_row_price',
  489. '_custom_option_row_sku', '_custom_option_row_sort'
  490. );
  491. foreach ($this->_storeIdToCode as $storeId => &$storeCode) {
  492. $options = Mage::getResourceModel('catalog/product_option_collection')
  493. ->reset()
  494. ->addTitleToResult($storeId)
  495. ->addPriceToResult($storeId)
  496. ->addProductToFilter($productIds)
  497. ->addValuesToResult($storeId);
  498. foreach ($options as $option) {
  499. $row = array();
  500. $productId = $option['product_id'];
  501. $optionId = $option['option_id'];
  502. $customOptions = isset($customOptionsDataPre[$productId][$optionId])
  503. ? $customOptionsDataPre[$productId][$optionId]
  504. : array();
  505. if ($defaultStoreId == $storeId) {
  506. $row['_custom_option_type'] = $option['type'];
  507. $row['_custom_option_title'] = $option['title'];
  508. $row['_custom_option_is_required'] = $option['is_require'];
  509. $row['_custom_option_price'] = $option['price'] . ($option['price_type'] == 'percent' ? '%' : '');
  510. $row['_custom_option_sku'] = $option['sku'];
  511. $row['_custom_option_max_characters'] = $option['max_characters'];
  512. $row['_custom_option_sort_order'] = $option['sort_order'];
  513. // remember default title for later comparisons
  514. $defaultTitles[$option['option_id']] = $option['title'];
  515. } elseif ($option['title'] != $customOptions[0]['_custom_option_title']) {
  516. $row['_custom_option_title'] = $option['title'];
  517. }
  518. if ($values = $option->getValues()) {
  519. $firstValue = array_shift($values);
  520. $priceType = $firstValue['price_type'] == 'percent' ? '%' : '';
  521. if ($defaultStoreId == $storeId) {
  522. $row['_custom_option_row_title'] = $firstValue['title'];
  523. $row['_custom_option_row_price'] = $firstValue['price'] . $priceType;
  524. $row['_custom_option_row_sku'] = $firstValue['sku'];
  525. $row['_custom_option_row_sort'] = $firstValue['sort_order'];
  526. $defaultValueTitles[$firstValue['option_type_id']] = $firstValue['title'];
  527. } elseif ($firstValue['title'] != $customOptions[0]['_custom_option_row_title']) {
  528. $row['_custom_option_row_title'] = $firstValue['title'];
  529. }
  530. }
  531. if ($row) {
  532. if ($defaultStoreId != $storeId) {
  533. $row['_custom_option_store'] = $this->_storeIdToCode[$storeId];
  534. }
  535. $customOptionsDataPre[$productId][$optionId][] = $row;
  536. }
  537. foreach ($values as $value) {
  538. $row = array();
  539. $valuePriceType = $value['price_type'] == 'percent' ? '%' : '';
  540. if ($defaultStoreId == $storeId) {
  541. $row['_custom_option_row_title'] = $value['title'];
  542. $row['_custom_option_row_price'] = $value['price'] . $valuePriceType;
  543. $row['_custom_option_row_sku'] = $value['sku'];
  544. $row['_custom_option_row_sort'] = $value['sort_order'];
  545. } elseif ($value['title'] != $customOptions[0]['_custom_option_row_title']) {
  546. $row['_custom_option_row_title'] = $value['title'];
  547. }
  548. if ($row) {
  549. if ($defaultStoreId != $storeId) {
  550. $row['_custom_option_store'] = $this->_storeIdToCode[$storeId];
  551. }
  552. $customOptionsDataPre[$option['product_id']][$option['option_id']][] = $row;
  553. }
  554. }
  555. $option = null;
  556. }
  557. $options = null;
  558. }
  559. foreach ($customOptionsDataPre as $productId => &$optionsData) {
  560. $customOptionsData[$productId] = array();
  561. foreach ($optionsData as $optionId => &$optionRows) {
  562. $customOptionsData[$productId] = array_merge($customOptionsData[$productId], $optionRows);
  563. }
  564. unset($optionRows, $optionsData);
  565. }
  566. unset($customOptionsDataPre);
  567. // create export file
  568. $headerCols = array_merge(
  569. array(
  570. self::COL_SKU, self::COL_STORE, self::COL_ATTR_SET,
  571. self::COL_TYPE, self::COL_CATEGORY, '_product_websites'
  572. ),
  573. $validAttrCodes,
  574. reset($stockItemRows) ? array_keys(end($stockItemRows)) : array(),
  575. $gfAmountFields,
  576. array(
  577. '_links_related_sku', '_links_related_position', '_links_crosssell_sku',
  578. '_links_crosssell_position', '_links_upsell_sku', '_links_upsell_position',
  579. '_associated_sku', '_associated_default_qty', '_associated_position'
  580. ),
  581. array('_tier_price_website', '_tier_price_customer_group', '_tier_price_qty', '_tier_price_price')
  582. );
  583. // have we merge custom options columns
  584. if ($customOptionsData) {
  585. $headerCols = array_merge($headerCols, $customOptCols);
  586. }
  587. // have we merge configurable products data
  588. if ($configurableData) {
  589. $headerCols = array_merge($headerCols, array(
  590. '_super_products_sku', '_super_attribute_code',
  591. '_super_attribute_option', '_super_attribute_price_corr'
  592. ));
  593. }
  594. $writer->setHeaderCols($headerCols);
  595. foreach ($dataRows as $productId => &$productData) {
  596. foreach ($productData as $storeId => &$dataRow) {
  597. if ($defaultStoreId != $storeId) {
  598. $dataRow[self::COL_SKU] = null;
  599. $dataRow[self::COL_ATTR_SET] = null;
  600. $dataRow[self::COL_TYPE] = null;
  601. } else {
  602. $dataRow[self::COL_STORE] = null;
  603. $dataRow += $stockItemRows[$productId];
  604. }
  605. if ($rowCategories[$productId]) {
  606. $dataRow[self::COL_CATEGORY] = $this->_categories[array_shift($rowCategories[$productId])];
  607. }
  608. if ($rowWebsites[$productId]) {
  609. $dataRow['_product_websites'] = $this->_websiteIdToCode[array_shift($rowWebsites[$productId])];
  610. }
  611. if (!empty($rowTierPrices[$productId])) {
  612. $dataRow = array_merge($dataRow, array_shift($rowTierPrices[$productId]));
  613. }
  614. foreach ($linkIdColPrefix as $linkId => &$colPrefix) {
  615. if (!empty($linksRows[$productId][$linkId])) {
  616. $linkData = array_shift($linksRows[$productId][$linkId]);
  617. $dataRow[$colPrefix . 'position'] = $linkData['position'];
  618. $dataRow[$colPrefix . 'sku'] = $linkData['sku'];
  619. if (null !== $linkData['default_qty']) {
  620. $dataRow[$colPrefix . 'default_qty'] = $linkData['default_qty'];
  621. }
  622. }
  623. }
  624. if (!empty($customOptionsData[$productId])) {
  625. $dataRow = array_merge($dataRow, array_shift($customOptionsData[$productId]));
  626. }
  627. if (!empty($configurableData[$productId])) {
  628. $dataRow = array_merge($dataRow, array_shift($configurableData[$productId]));
  629. }
  630. $writer->writeRow($dataRow);
  631. }
  632. // calculate largest links block
  633. $largestLinks = 0;
  634. if (isset($linksRows[$productId])) {
  635. foreach ($linksRows[$productId] as &$linkData) {
  636. $largestLinks = max($largestLinks, count($linkData));
  637. }
  638. }
  639. $additionalRowsCount = max(
  640. count($rowCategories[$productId]),
  641. count($rowWebsites[$productId]),
  642. $largestLinks
  643. );
  644. if (!empty($rowTierPrices[$productId])) {
  645. $additionalRowsCount = max($additionalRowsCount, count($rowTierPrices[$productId]));
  646. }
  647. if (!empty($customOptionsData[$productId])) {
  648. $additionalRowsCount = max($additionalRowsCount, count($customOptionsData[$productId]));
  649. }
  650. if (!empty($configurableData[$productId])) {
  651. $additionalRowsCount = max($additionalRowsCount, count($configurableData[$productId]));
  652. }
  653. if ($additionalRowsCount) {
  654. for ($i = 0; $i < $additionalRowsCount; $i++) {
  655. $dataRow = array();
  656. if ($rowCategories[$productId]) {
  657. $dataRow[self::COL_CATEGORY] = $this->_categories[array_shift($rowCategories[$productId])];
  658. }
  659. if ($rowWebsites[$productId]) {
  660. $dataRow['_product_websites'] = $this->_websiteIdToCode[array_shift($rowWebsites[$productId])];
  661. }
  662. if (!empty($rowTierPrices[$productId])) {
  663. $dataRow = array_merge($dataRow, array_shift($rowTierPrices[$productId]));
  664. }
  665. foreach ($linkIdColPrefix as $linkId => &$colPrefix) {
  666. if (!empty($linksRows[$productId][$linkId])) {
  667. $linkData = array_shift($linksRows[$productId][$linkId]);
  668. $dataRow[$colPrefix . 'position'] = $linkData['position'];
  669. $dataRow[$colPrefix . 'sku'] = $linkData['sku'];
  670. if (null !== $linkData['default_qty']) {
  671. $dataRow[$colPrefix . 'default_qty'] = $linkData['default_qty'];
  672. }
  673. }
  674. }
  675. if (!empty($customOptionsData[$productId])) {
  676. $dataRow = array_merge($dataRow, array_shift($customOptionsData[$productId]));
  677. }
  678. if (!empty($configurableData[$productId])) {
  679. $dataRow = array_merge($dataRow, array_shift($configurableData[$productId]));
  680. }
  681. $writer->writeRow($dataRow);
  682. }
  683. }
  684. }
  685. return $writer->getContents();
  686. }
  687. /**
  688. * Clean up already loaded attribute collection.
  689. *
  690. * @param Mage_Eav_Model_Mysql4_Entity_Attribute_Collection $collection
  691. * @return Mage_Eav_Model_Mysql4_Entity_Attribute_Collection
  692. */
  693. public function filterAttributeCollection(Mage_Eav_Model_Mysql4_Entity_Attribute_Collection $collection)
  694. {
  695. $validTypes = array_keys($this->_productTypeModels);
  696. foreach (parent::filterAttributeCollection($collection) as $attribute) {
  697. $attrApplyTo = $attribute->getApplyTo();
  698. $attrApplyTo = $attrApplyTo ? array_intersect($attrApplyTo, $validTypes) : $validTypes;
  699. if ($attrApplyTo) {
  700. foreach ($attrApplyTo as $productType) { // override attributes by its product type model
  701. if ($this->_productTypeModels[$productType]->overrideAttribute($attribute)) {
  702. break;
  703. }
  704. }
  705. } else { // remove attributes of not-supported product types
  706. $collection->removeItemByKey($attribute->getId());
  707. }
  708. }
  709. return $collection;
  710. }
  711. /**
  712. * Entity attributes collection getter.
  713. *
  714. * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Attribute_Collection
  715. */
  716. public function getAttributeCollection()
  717. {
  718. return Mage::getResourceModel('catalog/product_attribute_collection');
  719. }
  720. /**
  721. * EAV entity type code getter.
  722. *
  723. * @return string
  724. */
  725. public function getEntityTypeCode()
  726. {
  727. return 'catalog_product';
  728. }
  729. }