00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 class Mage_Catalog_Model_Product_Type_Configurable extends Mage_Catalog_Model_Product_Type_Abstract
00037 {
00038 const TYPE_CODE = 'configurable';
00039
00040
00041
00042
00043
00044
00045 protected $_usedProductAttributeIds = '_cache_instance_used_product_attribute_ids';
00046
00047
00048
00049
00050
00051
00052 protected $_usedProductAttributes = '_cache_instance_used_product_attributes';
00053
00054
00055
00056
00057
00058
00059 protected $_usedAttributes = '_cache_instance_used_attributes';
00060
00061
00062
00063
00064
00065
00066 protected $_configurableAttributes = '_cache_instance_configurable_attributes';
00067
00068
00069
00070
00071
00072
00073 protected $_usedProductIds = '_cache_instance_product_ids';
00074
00075
00076
00077
00078
00079
00080 protected $_usedProducts = '_cache_instance_products';
00081
00082
00083
00084
00085
00086
00087 protected $_isComposite = true;
00088
00089
00090
00091
00092
00093
00094 public function getRelationInfo()
00095 {
00096 $info = new Varien_Object();
00097 $info->setTable('catalog/product_super_link')
00098 ->setParentFieldName('parent_id')
00099 ->setChildFieldName('product_id');
00100 return $info;
00101 }
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113 public function getChildrenIds($parentId, $required = true)
00114 {
00115 return Mage::getResourceSingleton('catalog/product_type_configurable')
00116 ->getChildrenIds($parentId, $required);
00117 }
00118
00119
00120
00121
00122
00123
00124
00125 public function getParentIdsByChild($childId)
00126 {
00127 return Mage::getResourceSingleton('catalog/product_type_configurable')
00128 ->getParentIdsByChild($childId);
00129 }
00130
00131
00132
00133
00134
00135
00136
00137 public function getEditableAttributes($product = null)
00138 {
00139 if (is_null($this->_editableAttributes)) {
00140 $this->_editableAttributes = parent::getEditableAttributes($product);
00141 foreach ($this->_editableAttributes as $index => $attribute) {
00142 if ($this->getUsedProductAttributeIds($product)
00143 && in_array($attribute->getAttributeId(), $this->getUsedProductAttributeIds($product))) {
00144 unset($this->_editableAttributes[$index]);
00145 }
00146 }
00147 }
00148 return $this->_editableAttributes;
00149 }
00150
00151
00152
00153
00154
00155
00156
00157 public function canUseAttribute(Mage_Eav_Model_Entity_Attribute $attribute)
00158 {
00159 $allow = $attribute->getIsGlobal() == Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_GLOBAL
00160 && $attribute->getIsVisible()
00161 && $attribute->getIsConfigurable()
00162 && $attribute->usesSource();
00163
00164 return $allow;
00165 }
00166
00167
00168
00169
00170
00171
00172
00173
00174 public function setUsedProductAttributeIds($ids, $product = null)
00175 {
00176 $usedProductAttributes = array();
00177 $configurableAttributes = array();
00178
00179 foreach ($ids as $attributeId) {
00180 $usedProductAttributes[] = $this->getAttributeById($attributeId);
00181 $configurableAttributes[] = Mage::getModel('catalog/product_type_configurable_attribute')
00182 ->setProductAttribute($this->getAttributeById($attributeId));
00183 }
00184 $this->getProduct($product)->setData($this->_usedProductAttributes, $usedProductAttributes);
00185 $this->getProduct($product)->setData($this->_usedProductAttributeIds, $ids);
00186 $this->getProduct($product)->setData($this->_configurableAttributes, $configurableAttributes);
00187
00188 return $this;
00189 }
00190
00191
00192
00193
00194
00195
00196
00197 public function getUsedProductAttributeIds($product = null)
00198 {
00199 if (!$this->getProduct($product)->hasData($this->_usedProductAttributeIds)) {
00200 $usedProductAttributeIds = array();
00201 foreach ($this->getUsedProductAttributes($product) as $attribute) {
00202 $usedProductAttributeIds[] = $attribute->getId();
00203 }
00204 $this->getProduct($product)->setData($this->_usedProductAttributeIds, $usedProductAttributeIds);
00205 }
00206 return $this->getProduct($product)->getData($this->_usedProductAttributeIds);
00207 }
00208
00209
00210
00211
00212
00213
00214
00215 public function getUsedProductAttributes($product = null)
00216 {
00217 if (!$this->getProduct($product)->hasData($this->_usedProductAttributes)) {
00218 $usedProductAttributes = array();
00219 $usedAttributes = array();
00220 foreach ($this->getConfigurableAttributes($product) as $attribute) {
00221 $id = $attribute->getProductAttribute()->getId();
00222 $usedProductAttributes[$id] = $attribute->getProductAttribute();
00223 $usedAttributes[$id] = $attribute;
00224 }
00225 $this->getProduct($product)->setData($this->_usedAttributes, $usedAttributes);
00226 $this->getProduct($product)->setData($this->_usedProductAttributes, $usedProductAttributes);
00227 }
00228 return $this->getProduct($product)->getData($this->_usedProductAttributes);
00229 }
00230
00231
00232
00233
00234
00235
00236
00237 public function getConfigurableAttributes($product = null)
00238 {
00239 Varien_Profiler::start('CONFIGURABLE:'.__METHOD__);
00240 if (!$this->getProduct($product)->hasData($this->_configurableAttributes)) {
00241 $configurableAttributes = $this->getConfigurableAttributeCollection($product)
00242 ->orderByPosition()
00243 ->load();
00244 $this->getProduct($product)->setData($this->_configurableAttributes, $configurableAttributes);
00245 }
00246 Varien_Profiler::stop('CONFIGURABLE:'.__METHOD__);
00247 return $this->getProduct($product)->getData($this->_configurableAttributes);
00248 }
00249
00250
00251
00252
00253
00254
00255
00256 public function getConfigurableAttributesAsArray($product = null)
00257 {
00258 $res = array();
00259 foreach ($this->getConfigurableAttributes($product) as $attribute) {
00260 $label = $attribute->getLabel() ? $attribute->getLabel() : $attribute->getProductAttribute()->getFrontend()->getLabel();
00261 $res[] = array(
00262 'id' => $attribute->getId(),
00263 'label' => $label,
00264 'position' => $attribute->getPosition(),
00265 'values' => $attribute->getPrices() ? $attribute->getPrices() : array(),
00266 'attribute_id' => $attribute->getProductAttribute()->getId(),
00267 'attribute_code'=> $attribute->getProductAttribute()->getAttributeCode(),
00268 'frontend_label'=> $attribute->getProductAttribute()->getFrontend()->getLabel(),
00269 );
00270 }
00271 return $res;
00272 }
00273
00274
00275
00276
00277
00278
00279
00280 public function getConfigurableAttributeCollection($product = null)
00281 {
00282 return Mage::getResourceModel('catalog/product_type_configurable_attribute_collection')
00283 ->setProductFilter($this->getProduct($product));
00284 }
00285
00286
00287
00288
00289
00290
00291
00292
00293 public function getUsedProductIds($product = null)
00294 {
00295 if (!$this->getProduct($product)->hasData($this->_usedProductIds)) {
00296 $usedProductIds = array();
00297 foreach ($this->getUsedProducts(null, $product) as $product) {
00298 $usedProductIds[] = $product->getId();
00299 }
00300 $this->getProduct($product)->setData($this->_usedProductIds, $usedProductIds);
00301 }
00302 return $this->getProduct($product)->getData($this->_usedProductIds);
00303 }
00304
00305
00306
00307
00308
00309
00310
00311 public function getUsedProducts($requiredAttributeIds = null, $product = null)
00312 {
00313 Varien_Profiler::start('CONFIGURABLE:'.__METHOD__);
00314 if (!$this->getProduct($product)->hasData($this->_usedProducts)) {
00315 if (is_null($requiredAttributeIds)
00316 and is_null($this->getProduct($product)->getData($this->_configurableAttributes))) {
00317
00318 $this->getConfigurableAttributes($product);
00319
00320 Varien_Profiler::stop('CONFIGURABLE:'.__METHOD__);
00321 return $this->getProduct($product)->getData($this->_usedProducts);
00322 }
00323
00324 $usedProducts = array();
00325 $collection = $this->getUsedProductCollection($product)
00326 ->addAttributeToSelect('*')
00327 ->addFilterByRequiredOptions();
00328
00329 if (is_array($requiredAttributeIds)) {
00330 foreach ($requiredAttributeIds as $attributeId) {
00331 $attribute = $this->getAttributeById($attributeId, $product);
00332 if (!is_null($attribute))
00333 $collection->addAttributeToFilter($attribute->getAttributeCode(), array('notnull'=>1));
00334 }
00335 }
00336
00337 foreach ($collection as $item) {
00338 $usedProducts[] = $item;
00339 }
00340
00341 $this->getProduct($product)->setData($this->_usedProducts, $usedProducts);
00342 }
00343 Varien_Profiler::stop('CONFIGURABLE:'.__METHOD__);
00344 return $this->getProduct($product)->getData($this->_usedProducts);
00345 }
00346
00347
00348
00349
00350
00351
00352
00353 public function getUsedProductCollection($product = null)
00354 {
00355 $collection = Mage::getResourceModel('catalog/product_type_configurable_product_collection')
00356 ->setFlag('require_stock_items', true)
00357 ->setProductFilter($this->getProduct($product));
00358 if (!is_null($this->getStoreFilter($product))) {
00359 $collection->addStoreFilter($this->getStoreFilter($product));
00360 }
00361 return $collection;
00362 }
00363
00364
00365
00366
00367
00368
00369
00370 public function beforeSave($product = null)
00371 {
00372 parent::beforeSave($product);
00373
00374 $this->getProduct($product)->canAffectOptions(false);
00375
00376 if ($this->getProduct($product)->getCanSaveConfigurableAttributes()) {
00377 $this->getProduct($product)->canAffectOptions(true);
00378 if ($data = $this->getProduct($product)->getConfigurableAttributesData()) {
00379 if (!empty($data)) {
00380 foreach ($data as $attribute) {
00381 if (!empty($attribute['values'])) {
00382 $this->getProduct($product)->setTypeHasOptions(true);
00383 $this->getProduct($product)->setTypeHasRequiredOptions(true);
00384 break;
00385 }
00386 }
00387 }
00388 }
00389 }
00390
00391 return $this;
00392 }
00393
00394
00395
00396
00397
00398
00399
00400 public function save($product = null)
00401 {
00402 parent::save($product);
00403
00404
00405
00406 if ($data = $this->getProduct($product)->getConfigurableAttributesData()) {
00407 foreach ($data as $attributeData) {
00408 $id = isset($attributeData['id']) ? $attributeData['id'] : null;
00409 Mage::getModel('catalog/product_type_configurable_attribute')
00410 ->setData($attributeData)
00411 ->setId($id)
00412 ->setStoreId($this->getProduct($product)->getStoreId())
00413 ->setProductId($this->getProduct($product)->getId())
00414 ->save();
00415 }
00416 }
00417
00418
00419
00420
00421 $data = $this->getProduct($product)->getConfigurableProductsData();
00422 if (is_array($data)) {
00423 $productIds = array_keys($data);
00424 Mage::getResourceModel('catalog/product_type_configurable')
00425 ->saveProducts($this->getProduct($product)->getId(), $productIds);
00426 }
00427 return $this;
00428 }
00429
00430
00431
00432
00433
00434
00435 public function isSalable($product = null)
00436 {
00437 $salable = parent::isSalable($product);
00438 if (!is_null($salable)) {
00439 return $salable;
00440 }
00441
00442 $salable = false;
00443 foreach ($this->getUsedProducts(null, $product) as $child) {
00444 $salable = $salable || $child->isSalable();
00445 }
00446 return $salable;
00447 }
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459 public function getProductByAttributes($attributesInfo, $product = null)
00460 {
00461 foreach ($this->getUsedProducts(null, $product) as $productObject) {
00462 $checkRes = true;
00463 foreach ($attributesInfo as $attributeId => $attributeValue) {
00464 $code = $this->getAttributeById($attributeId, $product)->getAttributeCode();
00465 if ($productObject->getData($code) != $attributeValue) {
00466 $checkRes = false;
00467 }
00468 }
00469 if ($checkRes) {
00470 return $productObject;
00471 }
00472 }
00473 return null;
00474 }
00475
00476
00477
00478
00479
00480
00481
00482 public function getSelectedAttributesInfo($product = null)
00483 {
00484 $attributes = array();
00485 Varien_Profiler::start('CONFIGURABLE:'.__METHOD__);
00486 if ($attributesOption = $this->getProduct($product)->getCustomOption('attributes')) {
00487 $data = unserialize($attributesOption->getValue());
00488 $this->getUsedProductAttributeIds($product);
00489
00490 $usedAttributes = $this->getProduct($product)->getData($this->_usedAttributes);
00491
00492 foreach ($data as $attributeId => $attributeValue) {
00493 if (isset($usedAttributes[$attributeId])) {
00494 $attribute = $usedAttributes[$attributeId];
00495 $label = $attribute->getLabel();
00496 $value = $attribute->getProductAttribute();
00497 if ($value->getSourceModel()) {
00498 $value = $value->getSource()->getOptionText($attributeValue);
00499 }
00500 else {
00501 $value = '';
00502 }
00503
00504 $attributes[] = array('label'=>$label, 'value'=>$value);
00505 }
00506 }
00507 }
00508 Varien_Profiler::stop('CONFIGURABLE:'.__METHOD__);
00509 return $attributes;
00510 }
00511
00512
00513
00514
00515
00516
00517
00518
00519 public function prepareForCart(Varien_Object $buyRequest, $product = null)
00520 {
00521 if ($attributes = $buyRequest->getSuperAttribute()) {
00522 $result = parent::prepareForCart($buyRequest, $product);
00523 if (is_array($result)) {
00524 $product = $this->getProduct($product);
00525
00526
00527
00528 if ($subProduct = $this->getProductByAttributes($attributes, $product)) {
00529 $product->addCustomOption('attributes', serialize($attributes));
00530 $product->addCustomOption('product_qty_'.$subProduct->getId(), 1, $subProduct);
00531 $product->addCustomOption('simple_product', $subProduct->getId(), $subProduct);
00532
00533 $_result = $subProduct->getTypeInstance(true)->prepareForCart($buyRequest, $subProduct);
00534 if (is_string($_result) && !is_array($_result)) {
00535 return $_result;
00536 }
00537
00538 if (!isset($_result[0])) {
00539 return Mage::helper('checkout')->__('Can not add item to shopping cart');
00540 }
00541
00542
00543
00544
00545
00546 if ($optionIds = $product->getCustomOption('option_ids')) {
00547 $optionIds = explode(',', $optionIds->getValue());
00548 foreach ($optionIds as $optionId) {
00549 if ($option = $product->getCustomOption('option_' . $optionId)) {
00550 $_result[0]->addCustomOption('option_' . $optionId, $option->getValue());
00551 }
00552 }
00553 }
00554
00555 $_result[0]->setParentProductId($product->getId())
00556
00557 ->addCustomOption('parent_product_id', $product->getId())
00558 ->setCartQty(1);
00559
00560 $result[] = $_result[0];
00561
00562 return $result;
00563 }
00564 }
00565 }
00566 return $this->getSpecifyOptionMessage();
00567 }
00568
00569
00570
00571
00572
00573
00574 public function getSpecifyOptionMessage()
00575 {
00576 return Mage::helper('catalog')->__('Please specify the product option(s)');
00577 }
00578
00579
00580
00581
00582
00583
00584
00585
00586 public function getOrderOptions($product = null)
00587 {
00588 $options = parent::getOrderOptions($product);
00589 $options['attributes_info'] = $this->getSelectedAttributesInfo($product);
00590 if ($simpleOption = $this->getProduct($product)->getCustomOption('simple_product')) {
00591 $options['simple_name'] = $simpleOption->getProduct($product)->getName();
00592 $options['simple_sku'] = $simpleOption->getProduct($product)->getSku();
00593 }
00594
00595 $options['product_calculations'] = self::CALCULATE_PARENT;
00596 $options['shipment_type'] = self::SHIPMENT_TOGETHER;
00597
00598 return $options;
00599 }
00600
00601
00602
00603
00604
00605
00606 public function isVirtual($product = null)
00607 {
00608 if ($productOption = $this->getProduct($product)->getCustomOption('simple_product')) {
00609 if ($optionProduct = $productOption->getProduct()) {
00610
00611 return $optionProduct->isVirtual();
00612 }
00613 }
00614 return parent::isVirtual($product);
00615 }
00616
00617
00618
00619
00620
00621
00622
00623 public function hasOptions($product = null)
00624 {
00625 if ($this->getProduct($product)->getOptions()) {
00626 return true;
00627 }
00628
00629 $attributes = $this->getConfigurableAttributes($product);
00630 if (count($attributes)) {
00631 foreach ($attributes as $attribute) {
00632
00633 if ($attribute->getData('prices')) {
00634 return true;
00635 }
00636 }
00637 }
00638
00639 return false;
00640 }
00641
00642
00643
00644
00645
00646
00647
00648
00649 public function getWeight($product = null)
00650 {
00651 if ($this->getProduct($product)->hasCustomOptions() && ($simpleProductOption = $this->getProduct($product)->getCustomOption('simple_product'))) {
00652 $simpleProduct = $simpleProductOption->getProduct($product);
00653 if ($simpleProduct) {
00654 return $simpleProduct->getWeight();
00655 }
00656 }
00657
00658 return $this->getProduct($product)->getData('weight');
00659 }
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670 public function assignProductToOption($optionProduct, $option, $product = null)
00671 {
00672 if ($optionProduct) {
00673 $option->setProduct($optionProduct);
00674 } else {
00675 $option->getItem()->setHasError('error');
00676 $option->getItem()->addMessage(Mage::helper('catalog')->__('Selected configuration is not available.', $this->getProduct($product)->getName()));
00677 }
00678
00679 return $this;
00680 }
00681 }