PageRenderTime 39ms CodeModel.GetById 7ms RepoModel.GetById 0ms app.codeStats 1ms

/app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product.php

https://bitbucket.org/claudiu_marginean/magento-hg-mirror
PHP | 567 lines | 342 code | 51 blank | 174 comment | 26 complexity | 5cde9dab0c70b8aaac31e89ba960ece8 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_Catalog
  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. * Product entity resource model
  28. *
  29. * @category Mage
  30. * @package Mage_Catalog
  31. * @author Magento Core Team <core@magentocommerce.com>
  32. */
  33. class Mage_Catalog_Model_Resource_Eav_Mysql4_Product extends Mage_Catalog_Model_Resource_Eav_Mysql4_Abstract
  34. {
  35. protected $_productWebsiteTable;
  36. protected $_productCategoryTable;
  37. /**
  38. * Initialize resource
  39. */
  40. public function __construct()
  41. {
  42. parent::__construct();
  43. $resource = Mage::getSingleton('core/resource');
  44. $this->setType('catalog_product')
  45. ->setConnection('catalog_read', 'catalog_write');
  46. $this->_productWebsiteTable = $resource->getTableName('catalog/product_website');
  47. $this->_productCategoryTable= $resource->getTableName('catalog/category_product');
  48. }
  49. /**
  50. * Default product attributes
  51. *
  52. * @return array
  53. */
  54. protected function _getDefaultAttributes()
  55. {
  56. return array('entity_id', 'entity_type_id', 'attribute_set_id', 'type_id', 'created_at', 'updated_at');
  57. }
  58. /**
  59. * Retrieve product website identifiers
  60. *
  61. * @param Mage_Catalog_Model_Product|int $product
  62. * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product
  63. */
  64. public function getWebsiteIds($product)
  65. {
  66. if ($product instanceof Mage_Catalog_Model_Product) {
  67. $productId = $product->getId();
  68. } else {
  69. $productId = $product;
  70. }
  71. $select = $this->_getWriteAdapter()->select()
  72. ->from($this->_productWebsiteTable, 'website_id')
  73. ->where('product_id = ?', $productId);
  74. return $this->_getWriteAdapter()->fetchCol($select);
  75. }
  76. /**
  77. * Retrieve product website identifiers by product identifiers
  78. *
  79. * @param array $productIds
  80. * @return array
  81. */
  82. public function getWebsiteIdsByProductIds($productIds)
  83. {
  84. $select = $this->_getWriteAdapter()->select()
  85. ->from($this->_productWebsiteTable, array('product_id', 'website_ids' =>'GROUP_CONCAT(website_id)'))
  86. ->where('product_id IN (?)', $productIds)
  87. ->group('product_id');
  88. return $this->_getWriteAdapter()->fetchAll($select);
  89. }
  90. /**
  91. * Retrieve product category identifiers
  92. *
  93. * @param Mage_Catalog_Model_Product $product
  94. * @return array
  95. */
  96. public function getCategoryIds($product)
  97. {
  98. $select = $this->_getReadAdapter()->select()
  99. ->from($this->_productCategoryTable, 'category_id')
  100. ->where('product_id=?', $product->getId());
  101. return $this->_getReadAdapter()->fetchCol($select);
  102. }
  103. /**
  104. * Get product identifier by sku
  105. *
  106. * @param string $sku
  107. * @return int|false
  108. */
  109. public function getIdBySku($sku)
  110. {
  111. return $this->_getReadAdapter()->fetchOne('select entity_id from '.$this->getEntityTable().' where sku=?',$sku);
  112. }
  113. /**
  114. * Process product data before save
  115. *
  116. * @param Varien_Object $object
  117. * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product
  118. */
  119. protected function _beforeSave(Varien_Object $object)
  120. {
  121. /**
  122. * Try detect product id by sku if id is not declared
  123. */
  124. if (!$object->getId() && $object->getSku()) {
  125. $object->setId($this->getIdBySku($object->getSku()));
  126. }
  127. /**
  128. * Check if declared category ids in object data.
  129. */
  130. if ($object->hasCategoryIds()) {
  131. $categoryIds = Mage::getResourceSingleton('catalog/category')->verifyIds(
  132. $object->getCategoryIds()
  133. );
  134. $object->setCategoryIds($categoryIds);
  135. }
  136. return parent::_beforeSave($object);
  137. }
  138. /**
  139. * Save data related with product
  140. *
  141. * @param Varien_Object $product
  142. * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product
  143. */
  144. protected function _afterSave(Varien_Object $product)
  145. {
  146. $this->_saveWebsiteIds($product)
  147. ->_saveCategories($product)
  148. //->refreshIndex($product)
  149. ;
  150. parent::_afterSave($product);
  151. return $this;
  152. }
  153. /**
  154. * Save product website relations
  155. *
  156. * @param Mage_Catalog_Model_Product $product
  157. * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product
  158. */
  159. protected function _saveWebsiteIds($product)
  160. {
  161. $websiteIds = $product->getWebsiteIds();
  162. $oldWebsiteIds = array();
  163. $product->setIsChangedWebsites(false);
  164. $select = $this->_getWriteAdapter()->select()
  165. ->from($this->_productWebsiteTable)
  166. ->where('product_id=?', $product->getId());
  167. $query = $this->_getWriteAdapter()->query($select);
  168. while ($row = $query->fetch()) {
  169. $oldWebsiteIds[] = $row['website_id'];
  170. }
  171. $insert = array_diff($websiteIds, $oldWebsiteIds);
  172. $delete = array_diff($oldWebsiteIds, $websiteIds);
  173. if (!empty($insert)) {
  174. foreach ($insert as $websiteId) {
  175. $this->_getWriteAdapter()->insert($this->_productWebsiteTable, array(
  176. 'product_id' => $product->getId(),
  177. 'website_id' => $websiteId
  178. ));
  179. }
  180. }
  181. if (!empty($delete)) {
  182. foreach ($delete as $websiteId) {
  183. $this->_getWriteAdapter()->delete($this->_productWebsiteTable, array(
  184. $this->_getWriteAdapter()->quoteInto('product_id=?', $product->getId()),
  185. $this->_getWriteAdapter()->quoteInto('website_id=?', $websiteId)
  186. ));
  187. }
  188. }
  189. if (!empty($insert) || !empty($delete)) {
  190. $product->setIsChangedWebsites(true);
  191. }
  192. return $this;
  193. }
  194. /**
  195. * Save product category relations
  196. *
  197. * @param Mage_Catalog_Model_Product $product
  198. * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product
  199. */
  200. protected function _saveCategories(Varien_Object $object)
  201. {
  202. /**
  203. * If category ids data is not declared we haven't do manipulations
  204. */
  205. if (!$object->hasCategoryIds()) {
  206. return $this;
  207. }
  208. $categoryIds = $object->getCategoryIds();
  209. $oldCategoryIds = $this->getCategoryIds($object);
  210. $object->setIsChangedCategories(false);
  211. $insert = array_diff($categoryIds, $oldCategoryIds);
  212. $delete = array_diff($oldCategoryIds, $categoryIds);
  213. $write = $this->_getWriteAdapter();
  214. if (!empty($insert)) {
  215. $data = array();
  216. foreach ($insert as $categoryId) {
  217. if (empty($categoryId)) {
  218. continue;
  219. }
  220. $data[] = array(
  221. 'category_id' => (int)$categoryId,
  222. 'product_id' => $object->getId(),
  223. 'position' => 1
  224. );
  225. }
  226. if ($data) {
  227. $write->insertMultiple($this->_productCategoryTable, $data);
  228. }
  229. }
  230. if (!empty($delete)) {
  231. $where = join(' AND ', array(
  232. $write->quoteInto('product_id=?', $object->getId()),
  233. $write->quoteInto('category_id IN(?)', $delete)
  234. ));
  235. $write->delete($this->_productCategoryTable, $where);
  236. }
  237. if (!empty($insert) || !empty($delete)) {
  238. $object->setAffectedCategoryIds(array_merge($insert, $delete));
  239. $object->setIsChangedCategories(true);
  240. }
  241. return $this;
  242. }
  243. /**
  244. * Refresh Product Enabled Index
  245. *
  246. * @param Mage_Catalog_Model_Product $product
  247. * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product
  248. */
  249. public function refreshIndex($product)
  250. {
  251. /**
  252. * Ids of all categories where product is assigned (not related with store)
  253. */
  254. $categoryIds = $product->getCategoryIds();
  255. /**
  256. * Clear previos index data related with product
  257. */
  258. $this->_getWriteAdapter()->delete(
  259. $this->getTable('catalog/category_product_index'),
  260. $this->_getWriteAdapter()->quoteInto('product_id=?', $product->getId())
  261. );
  262. if (!empty($categoryIds)) {
  263. $categoriesSelect = $this->_getWriteAdapter()->select()
  264. ->from($this->getTable('catalog/category'))
  265. ->where('entity_id IN (?)', $categoryIds);
  266. $categoriesInfo = $this->_getWriteAdapter()->fetchAll($categoriesSelect);
  267. $indexCategoryIds = array();
  268. foreach ($categoriesInfo as $categoryInfo) {
  269. $ids = explode('/', $categoryInfo['path']);
  270. $ids[] = $categoryInfo['entity_id'];
  271. $indexCategoryIds = array_merge($indexCategoryIds, $ids);
  272. }
  273. $indexCategoryIds = array_unique($indexCategoryIds);
  274. $indexProductIds = array($product->getId());
  275. Mage::getResourceSingleton('catalog/category')
  276. ->refreshProductIndex($indexCategoryIds, $indexProductIds);
  277. }
  278. else {
  279. $websites = $product->getWebsiteIds();
  280. if ($websites) {
  281. $storeIds = array();
  282. foreach ($websites as $websiteId) {
  283. $website = Mage::app()->getWebsite($websiteId);
  284. $storeIds = array_merge($storeIds, $website->getStoreIds());
  285. }
  286. Mage::getResourceSingleton('catalog/category')
  287. ->refreshProductIndex(array(), array($product->getId()), $storeIds);
  288. }
  289. }
  290. /**
  291. * Refresh enabled products index (visibility state)
  292. */
  293. $this->refreshEnabledIndex(null, $product);
  294. return $this;
  295. }
  296. /**
  297. * Refresh index for visibility of enabled product in store
  298. * if store parameter is null - index will refreshed for all stores
  299. * if product parameter is null - idex will be refreshed for all products
  300. *
  301. * @param Mage_Core_Model_Store $store
  302. * @param Mage_Core_Model_Product $product
  303. * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product
  304. */
  305. public function refreshEnabledIndex($store=null, $product=null)
  306. {
  307. $statusAttribute = $this->getAttribute('status');
  308. $visibilityAttribute = $this->getAttribute('visibility');
  309. $statusAttributeId = $statusAttribute->getId();
  310. $visibilityAttributeId = $visibilityAttribute->getId();
  311. $statusTable = $statusAttribute->getBackend()->getTable();
  312. $visibilityTable = $visibilityAttribute->getBackend()->getTable();
  313. $indexTable = $this->getTable('catalog/product_enabled_index');
  314. if (is_null($store) && is_null($product)) {
  315. Mage::throwException(
  316. Mage::helper('catalog')->__('To reindex the enabled product(s), the store or product must be specified.')
  317. );
  318. } elseif (is_null($product) || is_array($product)) {
  319. $storeId = $store->getId();
  320. $websiteId = $store->getWebsiteId();
  321. $productsCondition = '';
  322. $deleteCondition = '';
  323. if (is_array($product) && !empty($product)) {
  324. $productsCondition = $this->_getWriteAdapter()->quoteInto(
  325. ' AND t_v_default.entity_id IN (?)',
  326. $product
  327. );
  328. $deleteCondition = $this->_getWriteAdapter()->quoteInto(' AND product_id IN (?)', $product);
  329. }
  330. $this->_getWriteAdapter()->delete($indexTable, 'store_id='.$storeId.$deleteCondition);
  331. $query = "INSERT INTO $indexTable
  332. SELECT
  333. t_v_default.entity_id, {$storeId}, IF(t_v.value_id>0, t_v.value, t_v_default.value)
  334. FROM
  335. {$visibilityTable} AS t_v_default
  336. INNER JOIN {$this->getTable('catalog/product_website')} AS w
  337. ON w.product_id=t_v_default.entity_id AND w.website_id={$websiteId}
  338. LEFT JOIN {$visibilityTable} AS `t_v`
  339. ON (t_v.entity_id = t_v_default.entity_id)
  340. AND (t_v.attribute_id='{$visibilityAttributeId}')
  341. AND (t_v.store_id='{$storeId}')
  342. INNER JOIN {$statusTable} AS `t_s_default`
  343. ON (t_s_default.entity_id = t_v_default.entity_id)
  344. AND (t_s_default.attribute_id='{$statusAttributeId}')
  345. AND t_s_default.store_id=0
  346. LEFT JOIN {$statusTable} AS `t_s`
  347. ON (t_s.entity_id = t_v_default.entity_id)
  348. AND (t_s.attribute_id='{$statusAttributeId}')
  349. AND (t_s.store_id='{$storeId}')
  350. WHERE
  351. t_v_default.attribute_id='{$visibilityAttributeId}'
  352. AND t_v_default.store_id=0{$productsCondition}
  353. AND (IF(t_s.value_id>0, t_s.value, t_s_default.value) =
  354. ".Mage_Catalog_Model_Product_Status::STATUS_ENABLED.")";
  355. $this->_getWriteAdapter()->query($query);
  356. }
  357. elseif (is_null($store)) {
  358. foreach ($product->getStoreIds() as $storeId) {
  359. $store = Mage::app()->getStore($storeId);
  360. $this->refreshEnabledIndex($store, $product);
  361. }
  362. }
  363. else {
  364. $productId = $product->getId();
  365. $storeId = $store->getId();
  366. $this->_getWriteAdapter()->delete($indexTable, 'product_id='.$productId.' AND store_id='.$storeId);
  367. $query = "INSERT INTO $indexTable
  368. SELECT
  369. {$productId}, {$storeId}, IF(t_v.value_id>0, t_v.value, t_v_default.value)
  370. FROM
  371. {$visibilityTable} AS t_v_default
  372. LEFT JOIN {$visibilityTable} AS `t_v`
  373. ON (t_v.entity_id = t_v_default.entity_id)
  374. AND (t_v.attribute_id='{$visibilityAttributeId}')
  375. AND (t_v.store_id='{$storeId}')
  376. INNER JOIN {$statusTable} AS `t_s_default`
  377. ON (t_s_default.entity_id = t_v_default.entity_id)
  378. AND (t_s_default.attribute_id='{$statusAttributeId}')
  379. AND t_s_default.store_id=0
  380. LEFT JOIN {$statusTable} AS `t_s`
  381. ON (t_s.entity_id = t_v_default.entity_id)
  382. AND (t_s.attribute_id='{$statusAttributeId}')
  383. AND (t_s.store_id='{$storeId}')
  384. WHERE
  385. t_v_default.entity_id={$productId}
  386. AND t_v_default.attribute_id='{$visibilityAttributeId}' AND t_v_default.store_id=0
  387. AND (IF(t_s.value_id>0, t_s.value, t_s_default.value) =
  388. ".Mage_Catalog_Model_Product_Status::STATUS_ENABLED.")";
  389. $this->_getWriteAdapter()->query($query);
  390. }
  391. return $this;
  392. }
  393. /**
  394. * Get collection of product categories
  395. *
  396. * @param Mage_Catalog_Model_Product $product
  397. * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Category_Collection
  398. */
  399. public function getCategoryCollection($product)
  400. {
  401. $collection = Mage::getResourceModel('catalog/category_collection')
  402. ->joinField('product_id',
  403. 'catalog/category_product',
  404. 'product_id',
  405. 'category_id=entity_id',
  406. null)
  407. ->addFieldToFilter('product_id', (int) $product->getId());
  408. return $collection;
  409. }
  410. /**
  411. * Retrieve category ids where product is available
  412. *
  413. * @param Mage_Catalog_Model_Product $object
  414. * @return array
  415. */
  416. public function getAvailableInCategories($object)
  417. {
  418. $select = $this->_getReadAdapter()->select()
  419. ->from($this->getTable('catalog/category_product_index'), array('category_id'))
  420. ->where('product_id=?', $object->getEntityId());
  421. return $this->_getReadAdapter()->fetchCol($select);
  422. }
  423. /**
  424. * Get default attribute source model
  425. *
  426. * @return string
  427. */
  428. public function getDefaultAttributeSourceModel()
  429. {
  430. return 'eav/entity_attribute_source_table';
  431. }
  432. /**
  433. * Validate all object's attributes against configuration
  434. *
  435. * @todo implement full validation process with errors returning which are ignoring now
  436. *
  437. * @param Varien_Object $object
  438. * @return Varien_Object
  439. */
  440. public function validate($object)
  441. {
  442. // $this->walkAttributes('backend/beforeSave', array($object));
  443. // return parent::validate($object);
  444. parent::validate($object);
  445. return $this;
  446. }
  447. /**
  448. * Check availability display product in category
  449. *
  450. * @param int $categoryId
  451. * @return bool
  452. */
  453. public function canBeShowInCategory($product, $categoryId)
  454. {
  455. $select = $this->_getReadAdapter()->select()
  456. ->from($this->getTable('catalog/category_product_index'), 'product_id')
  457. ->where('product_id=?', $product->getId())
  458. ->where('category_id=?', $categoryId);
  459. return $this->_getReadAdapter()->fetchOne($select);
  460. }
  461. /**
  462. * Duplicate product store values
  463. *
  464. * @param int $oldId
  465. * @param int $newId
  466. * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product
  467. */
  468. public function duplicate($oldId, $newId)
  469. {
  470. $adapter = $this->_getWriteAdapter();
  471. $eavTables = array('datetime', 'decimal', 'int', 'text', 'varchar');
  472. // duplicate EAV store values
  473. foreach ($eavTables as $suffix) {
  474. $tableName = $this->getTable('catalog_product_entity_' . $suffix);
  475. $sql = 'REPLACE INTO `' . $tableName . '` '
  476. . 'SELECT NULL, `entity_type_id`, `attribute_id`, `store_id`, ' . $newId . ', `value`'
  477. . 'FROM `' . $tableName . '` WHERE `entity_id`=' . $oldId . ' AND `store_id`>0';
  478. $adapter->query($sql);
  479. }
  480. // set status as disabled
  481. $statusAttribute = $this->getAttribute('status');
  482. $statusAttributeId = $statusAttribute->getAttributeId();
  483. $statusAttributeTable = $statusAttribute->getBackend()->getTable();
  484. $updateCond[] = 'store_id > 0';
  485. $updateCond[] = $adapter->quoteInto('entity_id = ?', $newId);
  486. $updateCond[] = $adapter->quoteInto('attribute_id = ?', $statusAttributeId);
  487. $adapter->update(
  488. $statusAttributeTable,
  489. array('value' => Mage_Catalog_Model_Product_Status::STATUS_DISABLED),
  490. $updateCond
  491. );
  492. return $this;
  493. }
  494. /**
  495. * Get SKU through product identifiers
  496. *
  497. * @param array $productIds
  498. * @return array
  499. */
  500. public function getProductsSku(array $productIds)
  501. {
  502. $select = $this->_getReadAdapter()->select()
  503. ->from($this->getTable('catalog/product'), array('entity_id', 'sku'))
  504. ->where('entity_id IN (?)', $productIds);
  505. return $this->_getReadAdapter()->fetchAll($select);
  506. }
  507. /**
  508. * @deprecated after 1.4.2.0
  509. * @param $object Mage_Catalog_Model_Product
  510. * @return array
  511. */
  512. public function getParentProductIds($object)
  513. {
  514. return array();
  515. }
  516. }