PageRenderTime 43ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/app/code/core/Mage/Catalog/Model/Product/Type/Abstract.php

https://bitbucket.org/claudiu_marginean/magento-hg-mirror
PHP | 987 lines | 480 code | 95 blank | 412 comment | 62 complexity | 6181cc28147e5a9e0bae32e522ef7a02 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. * Abstract model for product type implementation
  28. *
  29. * @category Mage
  30. * @package Mage_Catalog
  31. * @author Magento Core Team <core@magentocommerce.com>
  32. */
  33. abstract class Mage_Catalog_Model_Product_Type_Abstract
  34. {
  35. /**
  36. * Product model instance
  37. *
  38. * @deprecated if use as singleton
  39. * @var Mage_Catalog_Model_Product
  40. */
  41. protected $_product;
  42. /**
  43. * Product type instance id
  44. *
  45. * @var string
  46. */
  47. protected $_typeId;
  48. /**
  49. * @deprecated
  50. *
  51. * @var array
  52. */
  53. protected $_setAttributes;
  54. /**
  55. * @deprecated
  56. *
  57. * @var array
  58. */
  59. protected $_editableAttributes;
  60. /**
  61. * Is a composite product type
  62. *
  63. * @var bool
  64. */
  65. protected $_isComposite = false;
  66. /**
  67. * Is a configurable product type
  68. *
  69. * @var bool
  70. */
  71. protected $_canConfigure = false;
  72. /**
  73. * Whether product quantity is fractional number or not
  74. *
  75. * @var bool
  76. */
  77. protected $_canUseQtyDecimals = true;
  78. /**
  79. * @deprecated
  80. *
  81. * @var int
  82. */
  83. protected $_storeFilter = null;
  84. /**
  85. * File queue array
  86. *
  87. * @var array
  88. */
  89. protected $_fileQueue = array();
  90. const CALCULATE_CHILD = 0;
  91. const CALCULATE_PARENT = 1;
  92. /**
  93. * values for shipment type (invoice etc)
  94. *
  95. */
  96. const SHIPMENT_SEPARATELY = 1;
  97. const SHIPMENT_TOGETHER = 0;
  98. /**
  99. * Process modes
  100. *
  101. * Full validation - all required options must be set, whole configuration
  102. * must be valid
  103. */
  104. const PROCESS_MODE_FULL = 'full';
  105. /**
  106. * Process modes
  107. *
  108. * Lite validation - only received options are validated
  109. */
  110. const PROCESS_MODE_LITE = 'lite';
  111. /**
  112. * Specify type instance product
  113. *
  114. * @param Mage_Catalog_Model_Product $product
  115. * @return Mage_Catalog_Model_Product_Type_Abstract
  116. */
  117. public function setProduct($product)
  118. {
  119. $this->_product = $product;
  120. return $this;
  121. }
  122. /**
  123. * Specify type identifier
  124. *
  125. * @param string $typeId
  126. * @return Mage_Catalog_Model_Product_Type_Abstract
  127. */
  128. public function setTypeId($typeId)
  129. {
  130. $this->_typeId = $typeId;
  131. return $this;
  132. }
  133. /**
  134. * Retrieve catalog product object
  135. *
  136. * @param Mage_Catalog_Model_Product $product
  137. * @return Mage_Catalog_Model_Product
  138. */
  139. public function getProduct($product = null)
  140. {
  141. if (is_object($product)) {
  142. return $product;
  143. }
  144. return $this->_product;
  145. }
  146. /**
  147. * Return relation info about used products for specific type instance
  148. *
  149. * @return Varien_Object Object with information data
  150. */
  151. public function getRelationInfo()
  152. {
  153. return new Varien_Object();
  154. }
  155. /**
  156. * Retrieve Required children ids
  157. * Return grouped array, ex array(
  158. * group => array(ids)
  159. * )
  160. *
  161. * @param int $parentId
  162. * @param bool $required
  163. * @return array
  164. */
  165. public function getChildrenIds($parentId, $required = true)
  166. {
  167. return array();
  168. }
  169. /**
  170. * Retrieve parent ids array by requered child
  171. *
  172. * @param int|array $childId
  173. * @return array
  174. */
  175. public function getParentIdsByChild($childId)
  176. {
  177. return array();
  178. }
  179. /**
  180. * Get array of product set attributes
  181. *
  182. * @param Mage_Catalog_Model_Product $product
  183. * @return array
  184. */
  185. public function getSetAttributes($product = null)
  186. {
  187. return $this->getProduct($product)->getResource()
  188. ->loadAllAttributes($this->getProduct($product))
  189. ->getSortedAttributes($this->getProduct($product)->getAttributeSetId());
  190. }
  191. /**
  192. * Compare attribues sorting
  193. *
  194. * @param Mage_Catalog_Model_Entity_Attribute $attribute1
  195. * @param Mage_Catalog_Model_Entity_Attribute $attribute2
  196. * @return int
  197. */
  198. public function attributesCompare($attribute1, $attribute2)
  199. {
  200. $sort1 = ($attribute1->getGroupSortPath() * 1000) + ($attribute1->getSortPath() * 0.0001);
  201. $sort2 = ($attribute2->getGroupSortPath() * 1000) + ($attribute2->getSortPath() * 0.0001);
  202. if ($sort1 > $sort2) {
  203. return 1;
  204. } elseif ($sort1 < $sort2) {
  205. return -1;
  206. }
  207. return 0;
  208. }
  209. /**
  210. * Retrieve product type attributes
  211. *
  212. * @param Mage_Catalog_Model_Product $product
  213. * @return array
  214. */
  215. public function getEditableAttributes($product = null)
  216. {
  217. $cacheKey = '_cache_editable_attributes';
  218. if (!$this->getProduct($product)->hasData($cacheKey)) {
  219. $editableAttributes = array();
  220. foreach ($this->getSetAttributes($product) as $attributeCode => $attribute) {
  221. if (!is_array($attribute->getApplyTo())
  222. || count($attribute->getApplyTo())==0
  223. || in_array($this->getProduct($product)->getTypeId(), $attribute->getApplyTo())) {
  224. $editableAttributes[$attributeCode] = $attribute;
  225. }
  226. }
  227. $this->getProduct($product)->setData($cacheKey, $editableAttributes);
  228. }
  229. return $this->getProduct($product)->getData($cacheKey);
  230. }
  231. /**
  232. * Retrieve product attribute by identifier
  233. *
  234. * @param int $attributeId
  235. * @return Mage_Eav_Model_Entity_Attribute_Abstract
  236. */
  237. public function getAttributeById($attributeId, $product = null)
  238. {
  239. foreach ($this->getSetAttributes($product) as $attribute) {
  240. if ($attribute->getId() == $attributeId) {
  241. return $attribute;
  242. }
  243. }
  244. return null;
  245. }
  246. /**
  247. * Check is virtual product
  248. *
  249. * @param Mage_Catalog_Model_Product $product
  250. * @return bool
  251. */
  252. public function isVirtual($product = null)
  253. {
  254. return false;
  255. }
  256. /**
  257. * Check is product available for sale
  258. *
  259. * @param Mage_Catalog_Model_Product $product
  260. * @return bool
  261. */
  262. public function isSalable($product = null)
  263. {
  264. $salable = $this->getProduct($product)->getStatus() == Mage_Catalog_Model_Product_Status::STATUS_ENABLED;
  265. if ($salable && $this->getProduct($product)->hasData('is_salable')) {
  266. $salable = $this->getProduct($product)->getData('is_salable');
  267. }
  268. elseif ($salable && $this->isComposite()) {
  269. $salable = null;
  270. }
  271. return $salable;
  272. }
  273. /**
  274. * Prepare product and its configuration to be added to some products list.
  275. * Perform standard preparation process and then prepare options belonging to specific product type.
  276. *
  277. * @param Varien_Object $buyRequest
  278. * @param Mage_Catalog_Model_Product $product
  279. * @param string $processMode
  280. * @return array|string
  281. */
  282. protected function _prepareProduct(Varien_Object $buyRequest, $product, $processMode)
  283. {
  284. $product = $this->getProduct($product);
  285. /* @var Mage_Catalog_Model_Product $product */
  286. // try to add custom options
  287. try {
  288. $options = $this->_prepareOptions($buyRequest, $product, $processMode);
  289. } catch (Mage_Core_Exception $e) {
  290. return $e->getMessage();
  291. }
  292. if (is_string($options)) {
  293. return $options;
  294. }
  295. // try to found super product configuration
  296. // (if product was buying within grouped product)
  297. $superProductConfig = $buyRequest->getSuperProductConfig();
  298. if (!empty($superProductConfig['product_id'])
  299. && !empty($superProductConfig['product_type'])
  300. ) {
  301. $superProductId = (int) $superProductConfig['product_id'];
  302. if ($superProductId) {
  303. if (!$superProduct = Mage::registry('used_super_product_'.$superProductId)) {
  304. $superProduct = Mage::getModel('catalog/product')->load($superProductId);
  305. Mage::register('used_super_product_'.$superProductId, $superProduct);
  306. }
  307. if ($superProduct->getId()) {
  308. $assocProductIds = $superProduct->getTypeInstance(true)->getAssociatedProductIds($superProduct);
  309. if (in_array($product->getId(), $assocProductIds)) {
  310. $productType = $superProductConfig['product_type'];
  311. $product->addCustomOption('product_type', $productType, $superProduct);
  312. $buyRequest->setData('super_product_config', array(
  313. 'product_type' => $productType,
  314. 'product_id' => $superProduct->getId()
  315. ));
  316. }
  317. }
  318. }
  319. }
  320. $product->prepareCustomOptions();
  321. $buyRequest->unsetData('_processing_params'); // One-time params only
  322. $product->addCustomOption('info_buyRequest', serialize($buyRequest->getData()));
  323. if ($options) {
  324. $optionIds = array_keys($options);
  325. $product->addCustomOption('option_ids', implode(',', $optionIds));
  326. foreach ($options as $optionId => $optionValue) {
  327. $product->addCustomOption('option_'.$optionId, $optionValue);
  328. }
  329. }
  330. // set quantity in cart
  331. if ($this->_isStrictProcessMode($processMode)) {
  332. $product->setCartQty($buyRequest->getQty());
  333. }
  334. $product->setQty($buyRequest->getQty());
  335. return array($product);
  336. }
  337. /**
  338. * Process product configuaration
  339. *
  340. * @param Varien_Object $buyRequest
  341. * @param Mage_Catalog_Model_Product $product
  342. * @param string $processMode
  343. * @return array|string
  344. */
  345. public function processConfiguration(Varien_Object $buyRequest, $product = null,
  346. $processMode = self::PROCESS_MODE_LITE)
  347. {
  348. $_products = $this->_prepareProduct($buyRequest, $product, $processMode);
  349. $this->processFileQueue();
  350. return $_products;
  351. }
  352. /**
  353. * Initialize product(s) for add to cart process.
  354. * Advanced version of func to prepare product for cart - processMode can be specified there.
  355. *
  356. * @param Varien_Object $buyRequest
  357. * @param Mage_Catalog_Model_Product $product
  358. * @param null|string $processMode
  359. * @return array|string
  360. */
  361. public function prepareForCartAdvanced(Varien_Object $buyRequest, $product = null, $processMode = null)
  362. {
  363. if (!$processMode) {
  364. $processMode = self::PROCESS_MODE_FULL;
  365. }
  366. $_products = $this->_prepareProduct($buyRequest, $product, $processMode);
  367. $this->processFileQueue();
  368. return $_products;
  369. }
  370. /**
  371. * Initialize product(s) for add to cart process
  372. *
  373. * @param Varien_Object $buyRequest
  374. * @param Mage_Catalog_Model_Product $product
  375. * @return array|string
  376. */
  377. public function prepareForCart(Varien_Object $buyRequest, $product = null)
  378. {
  379. return $this->prepareForCartAdvanced($buyRequest, $product, self::PROCESS_MODE_FULL);
  380. }
  381. /**
  382. * Process File Queue
  383. * @return Mage_Catalog_Model_Product_Type_Abstract
  384. */
  385. public function processFileQueue()
  386. {
  387. if (empty($this->_fileQueue)) {
  388. return $this;
  389. }
  390. foreach ($this->_fileQueue as &$queueOptions) {
  391. if (isset($queueOptions['operation']) && $operation = $queueOptions['operation']) {
  392. switch ($operation) {
  393. case 'receive_uploaded_file':
  394. $src = isset($queueOptions['src_name']) ? $queueOptions['src_name'] : '';
  395. $dst = isset($queueOptions['dst_name']) ? $queueOptions['dst_name'] : '';
  396. /** @var $uploader Zend_File_Transfer_Adapter_Http */
  397. $uploader = isset($queueOptions['uploader']) ? $queueOptions['uploader'] : null;
  398. $path = dirname($dst);
  399. $io = new Varien_Io_File();
  400. if (!$io->isWriteable($path) && !$io->mkdir($path, 0777, true)) {
  401. Mage::throwException(Mage::helper('catalog')->__("Cannot create writeable directory '%s'.", $path));
  402. }
  403. $uploader->setDestination($path);
  404. if (empty($src) || empty($dst) || !$uploader->receive($src)) {
  405. /**
  406. * @todo: show invalid option
  407. */
  408. if (isset($queueOptions['option'])) {
  409. $queueOptions['option']->setIsValid(false);
  410. }
  411. Mage::throwException(Mage::helper('catalog')->__("File upload failed"));
  412. }
  413. Mage::helper('core/file_storage_database')->saveFile($dst);
  414. break;
  415. case 'move_uploaded_file':
  416. $src = $queueOptions['src_name'];
  417. $dst = $queueOptions['dst_name'];
  418. move_uploaded_file($src, $dst);
  419. Mage::helper('core/file_storage_database')->saveFile($dst);
  420. break;
  421. default:
  422. break;
  423. }
  424. }
  425. $queueOptions = null;
  426. }
  427. return $this;
  428. }
  429. /**
  430. * Add file to File Queue
  431. * @param array $queueOptions Array of File Queue
  432. * (eg. ['operation'=>'move',
  433. * 'src_name'=>'filename',
  434. * 'dst_name'=>'filename2'])
  435. */
  436. public function addFileQueue($queueOptions)
  437. {
  438. $this->_fileQueue[] = $queueOptions;
  439. }
  440. /**
  441. * Check if current process mode is strict
  442. *
  443. * @param string $processMode
  444. * @return bool
  445. */
  446. protected function _isStrictProcessMode($processMode)
  447. {
  448. return $processMode == self::PROCESS_MODE_FULL;
  449. }
  450. /**
  451. * Retrieve message for specify option(s)
  452. *
  453. * @return string
  454. */
  455. public function getSpecifyOptionMessage()
  456. {
  457. return Mage::helper('catalog')->__('Please specify the product\'s required option(s).');
  458. }
  459. /**
  460. * Process custom defined options for product
  461. *
  462. * @param Varien_Object $buyRequest
  463. * @param Mage_Catalog_Model_Product $product
  464. * @param string $processMode
  465. * @return array
  466. */
  467. protected function _prepareOptions(Varien_Object $buyRequest, $product, $processMode)
  468. {
  469. $transport = new StdClass;
  470. $transport->options = array();
  471. foreach ($this->getProduct($product)->getOptions() as $_option) {
  472. /* @var $_option Mage_Catalog_Model_Product_Option */
  473. $group = $_option->groupFactory($_option->getType())
  474. ->setOption($_option)
  475. ->setProduct($this->getProduct($product))
  476. ->setRequest($buyRequest)
  477. ->setProcessMode($processMode)
  478. ->validateUserValue($buyRequest->getOptions());
  479. $preparedValue = $group->prepareForCart();
  480. if ($preparedValue !== null) {
  481. $transport->options[$_option->getId()] = $preparedValue;
  482. }
  483. }
  484. $eventName = sprintf('catalog_product_type_prepare_%s_options', $processMode);
  485. Mage::dispatchEvent($eventName, array(
  486. 'transport' => $transport,
  487. 'buy_request' => $buyRequest,
  488. 'product' => $product
  489. ));
  490. return $transport->options;
  491. }
  492. /**
  493. * Process product custom defined options for cart
  494. *
  495. * @deprecated after 1.4.2.0
  496. * @see _prepareOptions()
  497. *
  498. * @param Varien_Object $buyRequest
  499. * @param Mage_Catalog_Model_Product $product
  500. * @return array
  501. */
  502. protected function _prepareOptionsForCart(Varien_Object $buyRequest, $product = null)
  503. {
  504. return $this->_prepareOptions($buyRequest, $product, self::PROCESS_MODE_FULL);
  505. }
  506. /**
  507. * Check if product can be bought
  508. *
  509. * @param Mage_Catalog_Model_Product $product
  510. * @return Mage_Catalog_Model_Product_Type_Abstract
  511. * @throws Mage_Core_Exception
  512. */
  513. public function checkProductBuyState($product = null)
  514. {
  515. if (!$this->getProduct($product)->getSkipCheckRequiredOption()) {
  516. foreach ($this->getProduct($product)->getOptions() as $option) {
  517. if ($option->getIsRequire()) {
  518. $customOption = $this->getProduct($product)->getCustomOption('option_' . $option->getId());
  519. if (!$customOption || strlen($customOption->getValue()) == 0) {
  520. $this->getProduct($product)->setSkipCheckRequiredOption(true);
  521. Mage::throwException(
  522. Mage::helper('catalog')->__('The product has required options')
  523. );
  524. }
  525. }
  526. }
  527. }
  528. return $this;
  529. }
  530. /**
  531. * Prepare additional options/information for order item which will be
  532. * created from this product
  533. *
  534. * @param Mage_Catalog_Model_Product $product
  535. * @return array
  536. */
  537. public function getOrderOptions($product = null)
  538. {
  539. $optionArr = array();
  540. if ($info = $this->getProduct($product)->getCustomOption('info_buyRequest')) {
  541. $optionArr['info_buyRequest'] = unserialize($info->getValue());
  542. }
  543. if ($optionIds = $this->getProduct($product)->getCustomOption('option_ids')) {
  544. foreach (explode(',', $optionIds->getValue()) as $optionId) {
  545. if ($option = $this->getProduct($product)->getOptionById($optionId)) {
  546. $confItemOption = $this->getProduct($product)->getCustomOption('option_'.$option->getId());
  547. $group = $option->groupFactory($option->getType())
  548. ->setOption($option)
  549. ->setProduct($this->getProduct())
  550. ->setConfigurationItemOption($confItemOption);
  551. $optionArr['options'][] = array(
  552. 'label' => $option->getTitle(),
  553. 'value' => $group->getFormattedOptionValue($confItemOption->getValue()),
  554. 'print_value' => $group->getPrintableOptionValue($confItemOption->getValue()),
  555. 'option_id' => $option->getId(),
  556. 'option_type' => $option->getType(),
  557. 'option_value' => $confItemOption->getValue(),
  558. 'custom_view' => $group->isCustomizedView()
  559. );
  560. }
  561. }
  562. }
  563. if ($productTypeConfig = $this->getProduct($product)->getCustomOption('product_type')) {
  564. $optionArr['super_product_config'] = array(
  565. 'product_code' => $productTypeConfig->getCode(),
  566. 'product_type' => $productTypeConfig->getValue(),
  567. 'product_id' => $productTypeConfig->getProductId()
  568. );
  569. }
  570. return $optionArr;
  571. }
  572. /**
  573. * Save type related data
  574. *
  575. * @param Mage_Catalog_Model_Product $product
  576. * @return Mage_Catalog_Model_Product_Type_Abstract
  577. */
  578. public function save($product = null)
  579. {
  580. return $this;
  581. }
  582. /**
  583. * Remove don't applicable attributes data
  584. *
  585. * @param Mage_Catalog_Model_Product $product
  586. */
  587. protected function _removeNotApplicableAttributes($product = null)
  588. {
  589. $product = $this->getProduct($product);
  590. $eavConfig = Mage::getSingleton('eav/config');
  591. $entityType = $product->getResource()->getEntityType();
  592. foreach ($eavConfig->getEntityAttributeCodes($entityType, $product) as $attributeCode) {
  593. $attribute = $eavConfig->getAttribute($entityType, $attributeCode);
  594. $applyTo = $attribute->getApplyTo();
  595. if (is_array($applyTo) && count($applyTo) > 0 && !in_array($product->getTypeId(), $applyTo)) {
  596. $product->unsetData($attribute->getAttributeCode());
  597. }
  598. }
  599. }
  600. /**
  601. * Before save type related data
  602. *
  603. * @param Mage_Catalog_Model_Product $product
  604. * @return Mage_Catalog_Model_Product_Type_Abstract
  605. */
  606. public function beforeSave($product = null)
  607. {
  608. $this->_removeNotApplicableAttributes($product);
  609. $this->getProduct($product)->canAffectOptions(true);
  610. return $this;
  611. }
  612. /**
  613. * Check if product is composite (grouped, configurable, etc)
  614. *
  615. * @param Mage_Catalog_Model_Product $product
  616. * @return bool
  617. */
  618. public function isComposite($product = null)
  619. {
  620. return $this->_isComposite;
  621. }
  622. /**
  623. * Check if product is configurable
  624. *
  625. * @param Mage_Catalog_Model_Product $product
  626. * @return bool
  627. */
  628. public function canConfigure($product = null)
  629. {
  630. return $this->_canConfigure;
  631. }
  632. /**
  633. * Check if product qty is fractional number
  634. *
  635. * @param Mage_Catalog_Model_Product $product
  636. * @return bool
  637. */
  638. public function canUseQtyDecimals()
  639. {
  640. return $this->_canUseQtyDecimals;
  641. }
  642. /**
  643. * Default action to get sku of product
  644. *
  645. * @param Mage_Catalog_Model_Product $product
  646. * @return string
  647. */
  648. public function getSku($product = null)
  649. {
  650. $sku = $this->getProduct($product)->getData('sku');
  651. if ($this->getProduct($product)->getCustomOption('option_ids')) {
  652. $sku = $this->getOptionSku($product,$sku);
  653. }
  654. return $sku;
  655. }
  656. /**
  657. * Default action to get sku of product with option
  658. *
  659. * @param Mage_Catalog_Model_Product $product Product with Custom Options
  660. * @param string $sku Product SKU without option
  661. * @return string
  662. */
  663. public function getOptionSku($product = null, $sku='')
  664. {
  665. $skuDelimiter = '-';
  666. if(empty($sku)){
  667. $sku = $this->getProduct($product)->getData('sku');
  668. }
  669. if ($optionIds = $this->getProduct($product)->getCustomOption('option_ids')) {
  670. foreach (explode(',', $optionIds->getValue()) as $optionId) {
  671. if ($option = $this->getProduct($product)->getOptionById($optionId)) {
  672. $confItemOption = $this->getProduct($product)->getCustomOption('option_'.$optionId);
  673. $group = $option->groupFactory($option->getType())
  674. ->setOption($option)->setListener(new Varien_Object());
  675. if ($optionSku = $group->getOptionSku($confItemOption->getValue(), $skuDelimiter)) {
  676. $sku .= $skuDelimiter . $optionSku;
  677. }
  678. if ($group->getListener()->getHasError()) {
  679. $this->getProduct($product)
  680. ->setHasError(true)
  681. ->setMessage(
  682. $group->getListener()->getMessage()
  683. );
  684. }
  685. }
  686. }
  687. }
  688. return $sku;
  689. }
  690. /**
  691. * Default action to get weight of product
  692. *
  693. * @param Mage_Catalog_Model_Product $product
  694. * @return decimal
  695. */
  696. public function getWeight($product = null)
  697. {
  698. return $this->getProduct($product)->getData('weight');
  699. }
  700. /**
  701. * Return true if product has options
  702. *
  703. * @param Mage_Catalog_Model_Product $product
  704. * @return bool
  705. */
  706. public function hasOptions($product = null)
  707. {
  708. if ($this->getProduct($product)->getHasOptions()) {
  709. return true;
  710. }
  711. if ($this->getProduct($product)->isRecurring()) {
  712. return true;
  713. }
  714. return false;
  715. }
  716. /**
  717. * Method is needed for specific actions to change given configuration options values
  718. * according current product type logic
  719. * Example: the cataloginventory validation of decimal qty can change qty to int,
  720. * so need to change configuration item qty option value too.
  721. *
  722. * @param array $options
  723. * @param Varien_Object $option
  724. * @param mixed $value
  725. *
  726. * @return object Mage_Catalog_Model_Product_Type_Abstract
  727. */
  728. public function updateQtyOption($options, Varien_Object $option, $value, $product = null)
  729. {
  730. return $this;
  731. }
  732. /**
  733. * Check if product has required options
  734. *
  735. * @param Mage_Catalog_Model_Product $product
  736. * @return bool
  737. */
  738. public function hasRequiredOptions($product = null)
  739. {
  740. if ($this->getProduct($product)->getRequiredOptions()) {
  741. return true;
  742. }
  743. return false;
  744. }
  745. /**
  746. * Retrive store filter for associated products
  747. *
  748. * @return int|Mage_Core_Model_Store
  749. */
  750. public function getStoreFilter($product = null)
  751. {
  752. $cacheKey = '_cache_instance_store_filter';
  753. return $this->getProduct($product)->getData($cacheKey);
  754. }
  755. /**
  756. * Set store filter for associated products
  757. *
  758. * @param $store int|Mage_Core_Model_Store
  759. * @return Mage_Catalog_Model_Product_Type_Configurable
  760. */
  761. public function setStoreFilter($store=null, $product = null)
  762. {
  763. $cacheKey = '_cache_instance_store_filter';
  764. $this->getProduct($product)->setData($cacheKey, $store);
  765. return $this;
  766. }
  767. /**
  768. * Allow for updates of chidren qty's
  769. * (applicable for complicated product types. As default returns false)
  770. *
  771. * @return boolean false
  772. */
  773. public function getForceChildItemQtyChanges($product = null)
  774. {
  775. return false;
  776. }
  777. /**
  778. * Prepare Quote Item Quantity
  779. *
  780. * @param mixed $qty
  781. * @return float
  782. */
  783. public function prepareQuoteItemQty($qty, $product = null)
  784. {
  785. return floatval($qty);
  786. }
  787. /**
  788. * Implementation of product specify logic of which product needs to be assigned to option.
  789. * For example if product which was added to option already removed from catalog.
  790. *
  791. * @param Mage_Catalog_Model_Product $optionProduct
  792. * @param Mage_Sales_Model_Quote_Item_Option $option
  793. * @param Mage_Catalog_Model_Product $product
  794. * @return Mage_Catalog_Model_Product_Type_Abstract
  795. */
  796. public function assignProductToOption($optionProduct, $option, $product = null)
  797. {
  798. if ($optionProduct) {
  799. $option->setProduct($optionProduct);
  800. } else {
  801. $option->setProduct($this->getProduct($product));
  802. }
  803. return $this;
  804. }
  805. /**
  806. * Setting specified product type variables
  807. *
  808. * @param array $config
  809. * @return Mage_Catalog_Model_Product_Type_Abstract
  810. */
  811. public function setConfig($config)
  812. {
  813. if (isset($config['composite'])) {
  814. $this->_isComposite = (bool) $config['composite'];
  815. }
  816. if (isset($config['can_use_qty_decimals'])) {
  817. $this->_canUseQtyDecimals = (bool) $config['can_use_qty_decimals'];
  818. }
  819. return $this;
  820. }
  821. /**
  822. * Retrieve additional searchable data from type instance
  823. * Using based on product id and store_id data
  824. *
  825. * @param Mage_Catalog_Model_Product $product
  826. * @return array
  827. */
  828. public function getSearchableData($product = null)
  829. {
  830. $product = $this->getProduct($product);
  831. $searchData = array();
  832. if ($product->getHasOptions()){
  833. $searchData = Mage::getSingleton('catalog/product_option')
  834. ->getSearchableData($product->getId(), $product->getStoreId());
  835. }
  836. return $searchData;
  837. }
  838. /**
  839. * Retrieve products divided into groups required to purchase
  840. * At least one product in each group has to be purchased
  841. *
  842. * @param Mage_Catalog_Model_Product $product
  843. * @return array
  844. */
  845. public function getProductsToPurchaseByReqGroups($product = null)
  846. {
  847. $product = $this->getProduct($product);
  848. if ($this->isComposite($product)) {
  849. return array();
  850. }
  851. return array(array($product));
  852. }
  853. /**
  854. * Prepare selected options for product
  855. *
  856. * @param Mage_Catalog_Model_Product $product
  857. * @param Varien_Object $buyRequest
  858. * @return array
  859. */
  860. public function processBuyRequest($product, $buyRequest)
  861. {
  862. return array();
  863. }
  864. /**
  865. * Check product's options configuration
  866. *
  867. * @param Mage_Catalog_Model_Product $product
  868. * @param Varien_Object $buyRequest
  869. * @return array
  870. */
  871. public function checkProductConfiguration($product, $buyRequest)
  872. {
  873. $errors = array();
  874. try {
  875. /**
  876. * cloning product because prepareForCart() method will modify it
  877. */
  878. $productForCheck = clone $product;
  879. $buyRequestForCheck = clone $buyRequest;
  880. $result = $this->prepareForCart($buyRequestForCheck, $productForCheck);
  881. if (is_string($result)) {
  882. $errors[] = $result;
  883. }
  884. } catch (Mage_Core_Exception $e) {
  885. $errors[] = $e->getMessages();
  886. } catch (Exception $e) {
  887. Mage::logException($e);
  888. $errors[] = Mage::helper('catalog')->__('There was an error while request processing.');
  889. }
  890. return $errors;
  891. }
  892. }