1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25:
26:
27: 28: 29: 30: 31: 32: 33: 34: 35:
36: class Mage_Catalog_Model_Product_Type_Configurable extends Mage_Catalog_Model_Product_Type_Abstract
37: {
38: const TYPE_CODE = 'configurable';
39:
40: 41: 42: 43: 44:
45: protected $_usedProductAttributeIds = '_cache_instance_used_product_attribute_ids';
46:
47: 48: 49: 50: 51:
52: protected $_usedProductAttributes = '_cache_instance_used_product_attributes';
53:
54: 55: 56: 57: 58:
59: protected $_usedAttributes = '_cache_instance_used_attributes';
60:
61: 62: 63: 64: 65:
66: protected $_configurableAttributes = '_cache_instance_configurable_attributes';
67:
68: 69: 70: 71: 72:
73: protected $_usedProductIds = '_cache_instance_product_ids';
74:
75: 76: 77: 78: 79:
80: protected $_usedProducts = '_cache_instance_products';
81:
82: 83: 84: 85: 86:
87: protected $_isComposite = true;
88:
89: 90: 91: 92: 93:
94: protected $_canConfigure = true;
95:
96: 97: 98: 99: 100:
101: public function getRelationInfo()
102: {
103: $info = new Varien_Object();
104: $info->setTable('catalog/product_super_link')
105: ->setParentFieldName('parent_id')
106: ->setChildFieldName('product_id');
107: return $info;
108: }
109:
110: 111: 112: 113: 114: 115: 116: 117: 118: 119:
120: public function getChildrenIds($parentId, $required = true)
121: {
122: return Mage::getResourceSingleton('catalog/product_type_configurable')
123: ->getChildrenIds($parentId, $required);
124: }
125:
126: 127: 128: 129: 130: 131:
132: public function getParentIdsByChild($childId)
133: {
134: return Mage::getResourceSingleton('catalog/product_type_configurable')
135: ->getParentIdsByChild($childId);
136: }
137:
138: 139: 140: 141: 142: 143:
144: public function getEditableAttributes($product = null)
145: {
146: if (is_null($this->_editableAttributes)) {
147: $this->_editableAttributes = parent::getEditableAttributes($product);
148: foreach ($this->_editableAttributes as $index => $attribute) {
149: if ($this->getUsedProductAttributeIds($product)
150: && in_array($attribute->getAttributeId(), $this->getUsedProductAttributeIds($product))) {
151: unset($this->_editableAttributes[$index]);
152: }
153: }
154: }
155: return $this->_editableAttributes;
156: }
157:
158: 159: 160: 161: 162: 163:
164: public function canUseAttribute(Mage_Eav_Model_Entity_Attribute $attribute)
165: {
166: $allow = $attribute->getIsGlobal() == Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_GLOBAL
167: && $attribute->getIsVisible()
168: && $attribute->getIsConfigurable()
169: && $attribute->usesSource()
170: && $attribute->getIsUserDefined();
171:
172: return $allow;
173: }
174:
175: 176: 177: 178: 179: 180: 181:
182: public function setUsedProductAttributeIds($ids, $product = null)
183: {
184: $usedProductAttributes = array();
185: $configurableAttributes = array();
186:
187: foreach ($ids as $attributeId) {
188: $usedProductAttributes[] = $this->getAttributeById($attributeId);
189: $configurableAttributes[] = Mage::getModel('catalog/product_type_configurable_attribute')
190: ->setProductAttribute($this->getAttributeById($attributeId));
191: }
192: $this->getProduct($product)->setData($this->_usedProductAttributes, $usedProductAttributes);
193: $this->getProduct($product)->setData($this->_usedProductAttributeIds, $ids);
194: $this->getProduct($product)->setData($this->_configurableAttributes, $configurableAttributes);
195:
196: return $this;
197: }
198:
199: 200: 201: 202: 203: 204:
205: public function getUsedProductAttributeIds($product = null)
206: {
207: if (!$this->getProduct($product)->hasData($this->_usedProductAttributeIds)) {
208: $usedProductAttributeIds = array();
209: foreach ($this->getUsedProductAttributes($product) as $attribute) {
210: $usedProductAttributeIds[] = $attribute->getId();
211: }
212: $this->getProduct($product)->setData($this->_usedProductAttributeIds, $usedProductAttributeIds);
213: }
214: return $this->getProduct($product)->getData($this->_usedProductAttributeIds);
215: }
216:
217: 218: 219: 220: 221: 222:
223: public function getUsedProductAttributes($product = null)
224: {
225: if (!$this->getProduct($product)->hasData($this->_usedProductAttributes)) {
226: $usedProductAttributes = array();
227: $usedAttributes = array();
228: foreach ($this->getConfigurableAttributes($product) as $attribute) {
229: if (!is_null($attribute->getProductAttribute())) {
230: $id = $attribute->getProductAttribute()->getId();
231: $usedProductAttributes[$id] = $attribute->getProductAttribute();
232: $usedAttributes[$id] = $attribute;
233: }
234: }
235: $this->getProduct($product)->setData($this->_usedAttributes, $usedAttributes);
236: $this->getProduct($product)->setData($this->_usedProductAttributes, $usedProductAttributes);
237: }
238: return $this->getProduct($product)->getData($this->_usedProductAttributes);
239: }
240:
241: 242: 243: 244: 245: 246:
247: public function getConfigurableAttributes($product = null)
248: {
249: Varien_Profiler::start('CONFIGURABLE:'.__METHOD__);
250: if (!$this->getProduct($product)->hasData($this->_configurableAttributes)) {
251: $configurableAttributes = $this->getConfigurableAttributeCollection($product)
252: ->orderByPosition()
253: ->load();
254: $this->getProduct($product)->setData($this->_configurableAttributes, $configurableAttributes);
255: }
256: Varien_Profiler::stop('CONFIGURABLE:'.__METHOD__);
257: return $this->getProduct($product)->getData($this->_configurableAttributes);
258: }
259:
260: 261: 262: 263: 264: 265:
266: public function getConfigurableAttributesAsArray($product = null)
267: {
268: $res = array();
269: foreach ($this->getConfigurableAttributes($product) as $attribute) {
270: $res[] = array(
271: 'id' => $attribute->getId(),
272: 'label' => $attribute->getLabel(),
273: 'use_default' => $attribute->getUseDefault(),
274: 'position' => $attribute->getPosition(),
275: 'values' => $attribute->getPrices() ? $attribute->getPrices() : array(),
276: 'attribute_id' => $attribute->getProductAttribute()->getId(),
277: 'attribute_code' => $attribute->getProductAttribute()->getAttributeCode(),
278: 'frontend_label' => $attribute->getProductAttribute()->getFrontend()->getLabel(),
279: 'store_label' => $attribute->getProductAttribute()->getStoreLabel(),
280: );
281: }
282: return $res;
283: }
284:
285: 286: 287: 288: 289: 290:
291: public function getConfigurableAttributeCollection($product = null)
292: {
293: return Mage::getResourceModel('catalog/product_type_configurable_attribute_collection')
294: ->setProductFilter($this->getProduct($product));
295: }
296:
297:
298: 299: 300: 301: 302: 303:
304: public function getUsedProductIds($product = null)
305: {
306: if (!$this->getProduct($product)->hasData($this->_usedProductIds)) {
307: $usedProductIds = array();
308: foreach ($this->getUsedProducts(null, $product) as $product) {
309: $usedProductIds[] = $product->getId();
310: }
311: $this->getProduct($product)->setData($this->_usedProductIds, $usedProductIds);
312: }
313: return $this->getProduct($product)->getData($this->_usedProductIds);
314: }
315:
316: 317: 318: 319: 320: 321: 322:
323: public function getUsedProducts($requiredAttributeIds = null, $product = null)
324: {
325: Varien_Profiler::start('CONFIGURABLE:'.__METHOD__);
326: if (!$this->getProduct($product)->hasData($this->_usedProducts)) {
327: if (is_null($requiredAttributeIds)
328: and is_null($this->getProduct($product)->getData($this->_configurableAttributes))) {
329:
330: $this->getConfigurableAttributes($product);
331:
332: Varien_Profiler::stop('CONFIGURABLE:'.__METHOD__);
333: return $this->getProduct($product)->getData($this->_usedProducts);
334: }
335:
336: $usedProducts = array();
337: $collection = $this->getUsedProductCollection($product)
338: ->addAttributeToSelect('*')
339: ->addFilterByRequiredOptions();
340:
341: if (is_array($requiredAttributeIds)) {
342: foreach ($requiredAttributeIds as $attributeId) {
343: $attribute = $this->getAttributeById($attributeId, $product);
344: if (!is_null($attribute))
345: $collection->addAttributeToFilter($attribute->getAttributeCode(), array('notnull'=>1));
346: }
347: }
348:
349: foreach ($collection as $item) {
350: $usedProducts[] = $item;
351: }
352:
353: $this->getProduct($product)->setData($this->_usedProducts, $usedProducts);
354: }
355: Varien_Profiler::stop('CONFIGURABLE:'.__METHOD__);
356: return $this->getProduct($product)->getData($this->_usedProducts);
357: }
358:
359: 360: 361: 362: 363: 364:
365: public function getUsedProductCollection($product = null)
366: {
367: $collection = Mage::getResourceModel('catalog/product_type_configurable_product_collection')
368: ->setFlag('require_stock_items', true)
369: ->setFlag('product_children', true)
370: ->setProductFilter($this->getProduct($product));
371: if (!is_null($this->getStoreFilter($product))) {
372: $collection->addStoreFilter($this->getStoreFilter($product));
373: }
374:
375: return $collection;
376: }
377:
378: 379: 380: 381: 382: 383:
384: public function beforeSave($product = null)
385: {
386: parent::beforeSave($product);
387:
388: $this->getProduct($product)->canAffectOptions(false);
389:
390: if ($this->getProduct($product)->getCanSaveConfigurableAttributes()) {
391: $this->getProduct($product)->canAffectOptions(true);
392: $data = $this->getProduct($product)->getConfigurableAttributesData();
393: if (!empty($data)) {
394: foreach ($data as $attribute) {
395: if (!empty($attribute['values'])) {
396: $this->getProduct($product)->setTypeHasOptions(true);
397: $this->getProduct($product)->setTypeHasRequiredOptions(true);
398: break;
399: }
400: }
401: }
402: }
403: foreach ($this->getConfigurableAttributes($product) as $attribute) {
404: $this->getProduct($product)->setData($attribute->getProductAttribute()->getAttributeCode(), null);
405: }
406:
407: return $this;
408: }
409:
410: 411: 412: 413: 414: 415:
416: public function save($product = null)
417: {
418: parent::save($product);
419: 420: 421:
422: if ($data = $this->getProduct($product)->getConfigurableAttributesData()) {
423: foreach ($data as $attributeData) {
424: $id = isset($attributeData['id']) ? $attributeData['id'] : null;
425: Mage::getModel('catalog/product_type_configurable_attribute')
426: ->setData($attributeData)
427: ->setId($id)
428: ->setStoreId($this->getProduct($product)->getStoreId())
429: ->setProductId($this->getProduct($product)->getId())
430: ->save();
431: }
432: }
433:
434: 435: 436:
437: $data = $this->getProduct($product)->getConfigurableProductsData();
438: if (is_array($data)) {
439: $productIds = array_keys($data);
440: Mage::getResourceModel('catalog/product_type_configurable')
441: ->saveProducts($this->getProduct($product), $productIds);
442: }
443: return $this;
444: }
445:
446: 447: 448: 449: 450: 451:
452: public function isSalable($product = null)
453: {
454: $salable = parent::isSalable($product);
455:
456: if ($salable !== false) {
457: $salable = false;
458: if (!is_null($product)) {
459: $this->setStoreFilter($product->getStoreId(), $product);
460: }
461: foreach ($this->getUsedProducts(null, $product) as $child) {
462: if ($child->isSalable()) {
463: $salable = true;
464: break;
465: }
466: }
467: }
468:
469: return $salable;
470: }
471:
472: 473: 474: 475: 476: 477: 478:
479: public function getIsSalable($product = null)
480: {
481: return $this->isSalable($product);
482: }
483:
484: 485: 486: 487: 488: 489: 490: 491: 492: 493:
494: public function getProductByAttributes($attributesInfo, $product = null)
495: {
496: if (is_array($attributesInfo) && !empty($attributesInfo)) {
497: $productCollection = $this->getUsedProductCollection($product)->addAttributeToSelect('name');
498: foreach ($attributesInfo as $attributeId => $attributeValue) {
499: $productCollection->addAttributeToFilter($attributeId, $attributeValue);
500: }
501: $productObject = $productCollection->getFirstItem();
502: if ($productObject->getId()) {
503: return $productObject;
504: }
505:
506: foreach ($this->getUsedProducts(null, $product) as $productObject) {
507: $checkRes = true;
508: foreach ($attributesInfo as $attributeId => $attributeValue) {
509: $code = $this->getAttributeById($attributeId, $product)->getAttributeCode();
510: if ($productObject->getData($code) != $attributeValue) {
511: $checkRes = false;
512: }
513: }
514: if ($checkRes) {
515: return $productObject;
516: }
517: }
518: }
519: return null;
520: }
521:
522: 523: 524: 525: 526: 527:
528: public function getSelectedAttributesInfo($product = null)
529: {
530: $attributes = array();
531: Varien_Profiler::start('CONFIGURABLE:'.__METHOD__);
532: if ($attributesOption = $this->getProduct($product)->getCustomOption('attributes')) {
533: $data = unserialize($attributesOption->getValue());
534: $this->getUsedProductAttributeIds($product);
535:
536: $usedAttributes = $this->getProduct($product)->getData($this->_usedAttributes);
537:
538: foreach ($data as $attributeId => $attributeValue) {
539: if (isset($usedAttributes[$attributeId])) {
540: $attribute = $usedAttributes[$attributeId];
541: $label = $attribute->getLabel();
542: $value = $attribute->getProductAttribute();
543: if ($value->getSourceModel()) {
544: $value = $value->getSource()->getOptionText($attributeValue);
545: }
546: else {
547: $value = '';
548: }
549:
550: $attributes[] = array('label'=>$label, 'value'=>$value);
551: }
552: }
553: }
554: Varien_Profiler::stop('CONFIGURABLE:'.__METHOD__);
555: return $attributes;
556: }
557:
558: 559: 560: 561: 562: 563: 564: 565: 566:
567: protected function _prepareProduct(Varien_Object $buyRequest, $product, $processMode)
568: {
569: $attributes = $buyRequest->getSuperAttribute();
570: if ($attributes || !$this->_isStrictProcessMode($processMode)) {
571: if (!$this->_isStrictProcessMode($processMode)) {
572: if (is_array($attributes)) {
573: foreach ($attributes as $key => $val) {
574: if (empty($val)) {
575: unset($attributes[$key]);
576: }
577: }
578: } else {
579: $attributes = array();
580: }
581: }
582:
583: $result = parent::_prepareProduct($buyRequest, $product, $processMode);
584: if (is_array($result)) {
585: $product = $this->getProduct($product);
586: 587: 588:
589: $subProduct = true;
590: if ($this->_isStrictProcessMode($processMode)) {
591: foreach($this->getConfigurableAttributes($product) as $attributeItem){
592:
593: $attrId = $attributeItem->getData('attribute_id');
594: if(!isset($attributes[$attrId]) || empty($attributes[$attrId])) {
595: $subProduct = null;
596: break;
597: }
598: }
599: }
600: if( $subProduct ) {
601: $subProduct = $this->getProductByAttributes($attributes, $product);
602: }
603:
604: if ($subProduct) {
605: $product->addCustomOption('attributes', serialize($attributes));
606: $product->addCustomOption('product_qty_'.$subProduct->getId(), 1, $subProduct);
607: $product->addCustomOption('simple_product', $subProduct->getId(), $subProduct);
608:
609: $_result = $subProduct->getTypeInstance(true)->_prepareProduct(
610: $buyRequest,
611: $subProduct,
612: $processMode
613: );
614: if (is_string($_result) && !is_array($_result)) {
615: return $_result;
616: }
617:
618: if (!isset($_result[0])) {
619: return Mage::helper('checkout')->__('Cannot add the item to shopping cart');
620: }
621:
622: 623: 624: 625:
626: if ($optionIds = $product->getCustomOption('option_ids')) {
627: $optionIds = explode(',', $optionIds->getValue());
628: foreach ($optionIds as $optionId) {
629: if ($option = $product->getCustomOption('option_' . $optionId)) {
630: $_result[0]->addCustomOption('option_' . $optionId, $option->getValue());
631: }
632: }
633: }
634:
635: $_result[0]->setParentProductId($product->getId())
636:
637:
638: ->addCustomOption('parent_product_id', $product->getId());
639: if ($this->_isStrictProcessMode($processMode)) {
640: $_result[0]->setCartQty(1);
641: }
642: $result[] = $_result[0];
643: return $result;
644: } else if (!$this->_isStrictProcessMode($processMode)) {
645: return $result;
646: }
647: }
648: }
649:
650: return $this->getSpecifyOptionMessage();
651: }
652:
653: 654: 655: 656: 657: 658: 659:
660: public function checkProductBuyState($product = null)
661: {
662: parent::checkProductBuyState($product);
663: $product = $this->getProduct($product);
664: $option = $product->getCustomOption('info_buyRequest');
665: if ($option instanceof Mage_Sales_Model_Quote_Item_Option) {
666: $buyRequest = new Varien_Object(unserialize($option->getValue()));
667: $attributes = $buyRequest->getSuperAttribute();
668: if (is_array($attributes)) {
669: foreach ($attributes as $key => $val) {
670: if (empty($val)) {
671: unset($attributes[$key]);
672: }
673: }
674: }
675: if (empty($attributes)) {
676: Mage::throwException($this->getSpecifyOptionMessage());
677: }
678: }
679: return $this;
680: }
681:
682: 683: 684: 685: 686:
687: public function getSpecifyOptionMessage()
688: {
689: return Mage::helper('catalog')->__('Please specify the product\'s option(s).');
690: }
691:
692: 693: 694: 695: 696: 697: 698:
699: public function getOrderOptions($product = null)
700: {
701: $options = parent::getOrderOptions($product);
702: $options['attributes_info'] = $this->getSelectedAttributesInfo($product);
703: if ($simpleOption = $this->getProduct($product)->getCustomOption('simple_product')) {
704: $options['simple_name'] = $simpleOption->getProduct($product)->getName();
705: $options['simple_sku'] = $simpleOption->getProduct($product)->getSku();
706: }
707:
708: $options['product_calculations'] = self::CALCULATE_PARENT;
709: $options['shipment_type'] = self::SHIPMENT_TOGETHER;
710:
711: return $options;
712: }
713:
714: 715: 716: 717: 718: 719:
720: public function isVirtual($product = null)
721: {
722: if ($productOption = $this->getProduct($product)->getCustomOption('simple_product')) {
723: if ($optionProduct = $productOption->getProduct()) {
724:
725: return $optionProduct->isVirtual();
726: }
727: }
728: return parent::isVirtual($product);
729: }
730:
731: 732: 733: 734: 735: 736:
737: public function hasOptions($product = null)
738: {
739: if ($this->getProduct($product)->getOptions()) {
740: return true;
741: }
742:
743: $attributes = $this->getConfigurableAttributes($product);
744: if (count($attributes)) {
745: foreach ($attributes as $attribute) {
746:
747: if ($attribute->getData('prices')) {
748: return true;
749: }
750: }
751: }
752:
753: return false;
754: }
755:
756: 757: 758: 759: 760: 761: 762:
763: public function getWeight($product = null)
764: {
765: if ($this->getProduct($product)->hasCustomOptions() &&
766: ($simpleProductOption = $this->getProduct($product)->getCustomOption('simple_product'))
767: ) {
768: $simpleProduct = $simpleProductOption->getProduct($product);
769: if ($simpleProduct) {
770: return $simpleProduct->getWeight();
771: }
772: }
773:
774: return $this->getProduct($product)->getData('weight');
775: }
776:
777: 778: 779: 780: 781: 782: 783: 784: 785:
786: public function assignProductToOption($optionProduct, $option, $product = null)
787: {
788: if ($optionProduct) {
789: $option->setProduct($optionProduct);
790: } else {
791: $option->getItem()->setHasError('error');
792: $option->getItem()->addMessage(Mage::helper('catalog')->__('Selected configuration is not available.', $this->getProduct($product)->getName()));
793: }
794:
795: return $this;
796: }
797:
798: 799: 800: 801: 802: 803: 804:
805: public function getProductsToPurchaseByReqGroups($product = null)
806: {
807: $product = $this->getProduct($product);
808: return array($this->getUsedProducts(null, $product));
809: }
810:
811: 812: 813: 814: 815: 816:
817: public function getSku($product = null)
818: {
819: $simpleOption = $this->getProduct($product)->getCustomOption('simple_product');
820: if($simpleOption) {
821: $optionProduct = $simpleOption->getProduct($product);
822: $simpleSku = null;
823: if ($optionProduct) {
824: $simpleSku = $simpleOption->getProduct($product)->getSku();
825: }
826: $sku = parent::getOptionSku($product, $simpleSku);
827: } else {
828: $sku = parent::getSku($product);
829: }
830:
831: return $sku;
832: }
833:
834: 835: 836: 837: 838: 839: 840:
841: public function processBuyRequest($product, $buyRequest)
842: {
843: $superAttribute = $buyRequest->getSuperAttribute();
844: $superAttribute = (is_array($superAttribute)) ? array_filter($superAttribute, 'intval') : array();
845:
846: $options = array('super_attribute' => $superAttribute);
847:
848: return $options;
849: }
850:
851: 852: 853: 854: 855: 856: 857:
858: public function isMapEnabledInOptions($product, $visibility = null)
859: {
860: return null;
861: }
862:
863: 864: 865: 866: 867: 868:
869: public function getConfigurableOptions($product)
870: {
871: return Mage::getResourceSingleton('catalog/product_type_configurable')
872: ->getConfigurableOptions($product, $this->getUsedProductAttributes($product));
873: }
874: }
875: