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 class Mage_Bundle_Model_Product_Type extends Mage_Catalog_Model_Product_Type_Abstract
00035 {
00036
00037
00038
00039
00040
00041 protected $_isComposite = true;
00042
00043
00044
00045
00046
00047
00048 protected $_keyOptionsCollection = '_cache_instance_options_collection';
00049
00050
00051
00052
00053
00054
00055 protected $_keySelectionsCollection = '_cache_instance_selections_collection';
00056
00057
00058
00059
00060
00061
00062 protected $_keyUsedSelections = '_cache_instance_used_selections';
00063
00064
00065
00066
00067
00068
00069 protected $_keyUsedSelectionsIds = '_cache_instance_used_selections_ids';
00070
00071
00072
00073
00074
00075
00076 protected $_keyUsedOptions = '_cache_instance_used_options';
00077
00078
00079
00080
00081
00082
00083 protected $_keyUsedOptionsIds = '_cache_instance_used_options_ids';
00084
00085
00086
00087
00088
00089
00090 public function getRelationInfo()
00091 {
00092 $info = new Varien_Object();
00093 $info->setTable('bundle/selection')
00094 ->setParentFieldName('parent_product_id')
00095 ->setChildFieldName('product_id');
00096 return $info;
00097 }
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109 public function getChildrenIds($parentId, $required = true)
00110 {
00111 return Mage::getResourceSingleton('bundle/selection')
00112 ->getChildrenIds($parentId, $required);
00113 }
00114
00115
00116
00117
00118
00119
00120
00121 public function getParentIdsByChild($childId)
00122 {
00123 return Mage::getResourceSingleton('bundle/selection')
00124 ->getParentIdsByChild($childId);
00125 }
00126
00127
00128
00129
00130
00131
00132
00133 public function getSku($product = null)
00134 {
00135 $sku = parent::getSku($product);
00136
00137 if ($this->getProduct($product)->getData('sku_type')) {
00138 return $sku;
00139 } else {
00140 $skuParts = array($sku);
00141
00142 if ($this->getProduct($product)->hasCustomOptions()) {
00143 $customOption = $this->getProduct($product)->getCustomOption('bundle_selection_ids');
00144 $selectionIds = unserialize($customOption->getValue());
00145 $selections = $this->getSelectionsByIds($selectionIds, $product);
00146 foreach ($selections->getItems() as $selection) {
00147 $skuParts[] = $selection->getSku();
00148 }
00149 }
00150
00151 return implode('-', $skuParts);
00152 }
00153 }
00154
00155
00156
00157
00158
00159
00160
00161 public function getWeight($product = null)
00162 {
00163 if ($this->getProduct($product)->getData('weight_type')) {
00164 return $this->getProduct($product)->getData('weight');
00165 } else {
00166 $weight = 0;
00167
00168 if ($this->getProduct($product)->hasCustomOptions()) {
00169 $customOption = $this->getProduct($product)->getCustomOption('bundle_selection_ids');
00170 $selectionIds = unserialize($customOption->getValue());
00171 $selections = $this->getSelectionsByIds($selectionIds, $product);
00172 foreach ($selections->getItems() as $selection) {
00173 $weight += $selection->getWeight();
00174 }
00175 }
00176 return $weight;
00177 }
00178 }
00179
00180
00181
00182
00183
00184
00185
00186 public function isVirtual($product = null)
00187 {
00188 if ($this->getProduct($product)->hasCustomOptions()) {
00189 $customOption = $this->getProduct($product)->getCustomOption('bundle_selection_ids');
00190 $selectionIds = unserialize($customOption->getValue());
00191 $selections = $this->getSelectionsByIds($selectionIds, $product);
00192 $virtualCount = 0;
00193 foreach ($selections->getItems() as $selection) {
00194 if ($selection->isVirtual()) {
00195 $virtualCount++;
00196 }
00197 }
00198 if ($virtualCount == count($selections)) {
00199 return true;
00200 }
00201 }
00202 return false;
00203 }
00204
00205
00206
00207
00208
00209
00210 public function beforeSave($product = null)
00211 {
00212 parent::beforeSave($product);
00213
00214 $this->getProduct($product)->canAffectOptions(false);
00215
00216 if ($this->getProduct($product)->getCanSaveBundleSelections()) {
00217 $this->getProduct($product)->canAffectOptions(true);
00218 if ($selections = $this->getProduct($product)->getBundleSelectionsData()) {
00219 if (!empty($selections)) {
00220 if ($options = $this->getProduct($product)->getBundleOptionsData()) {
00221 foreach ($options as $option) {
00222 if (empty($option['delete']) || 1 != (int)$option['delete']) {
00223 $this->getProduct($product)->setTypeHasOptions(true);
00224 if (1 == (int)$option['required']) {
00225 $this->getProduct($product)->setTypeHasRequiredOptions(true);
00226 break;
00227 }
00228 }
00229 }
00230 }
00231 }
00232 }
00233 }
00234 }
00235
00236
00237
00238
00239
00240
00241
00242 public function save($product = null)
00243 {
00244 parent::save($product);
00245
00246 if ($options = $this->getProduct($product)->getBundleOptionsData()) {
00247
00248 foreach ($options as $key => $option) {
00249 if (isset($option['option_id']) && $option['option_id'] == '') {
00250 unset($option['option_id']);
00251 }
00252
00253 $optionModel = Mage::getModel('bundle/option')
00254 ->setData($option)
00255 ->setParentId($this->getProduct($product)->getId())
00256 ->setStoreId($this->getProduct($product)->getStoreId());
00257
00258 $optionModel->isDeleted((bool)$option['delete']);
00259 $optionModel->save();
00260
00261 $options[$key]['option_id'] = $optionModel->getOptionId();
00262 }
00263
00264 $excludeSelectionIds = array();
00265
00266 if ($selections = $this->getProduct($product)->getBundleSelectionsData()) {
00267 foreach ($selections as $index => $group) {
00268 foreach ($group as $key => $selection) {
00269 if (isset($selection['selection_id']) && $selection['selection_id'] == '') {
00270 unset($selection['selection_id']);
00271 }
00272
00273 if (!isset($selection['is_default'])) {
00274 $selection['is_default'] = 0;
00275 }
00276
00277 $selectionModel = Mage::getModel('bundle/selection')
00278 ->setData($selection)
00279 ->setOptionId($options[$index]['option_id'])
00280 ->setParentProductId($this->getProduct($product)->getId());
00281
00282 $selectionModel->isDeleted((bool)$selection['delete']);
00283 $selectionModel->save();
00284
00285 $selection['selection_id'] = $selectionModel->getSelectionId();
00286
00287 if ($selectionModel->getSelectionId()) {
00288 $excludeSelectionIds[] = $selectionModel->getSelectionId();
00289 }
00290 }
00291 }
00292 Mage::getResourceModel('bundle/bundle')->dropAllUnneededSelections($this->getProduct($product)->getId(), $excludeSelectionIds);
00293 }
00294
00295 if ($this->getProduct($product)->getData('price_type') != $this->getProduct($product)->getOrigData('price_type')) {
00296 Mage::getResourceModel('bundle/bundle')->dropAllQuoteChildItems($this->getProduct($product)->getId());
00297 }
00298 }
00299
00300 return $this;
00301 }
00302
00303
00304
00305
00306
00307
00308
00309 public function getOptions($product = null)
00310 {
00311 return $this->getOptionsCollection($product)->getItems();
00312 }
00313
00314
00315
00316
00317
00318
00319
00320 public function getOptionsIds($product = null)
00321 {
00322 return $this->getOptionsCollection($product)->getAllIds();
00323 }
00324
00325
00326
00327
00328
00329
00330
00331 public function getOptionsCollection($product = null)
00332 {
00333 if (!$this->getProduct($product)->hasData($this->_keyOptionsCollection)) {
00334 $optionsCollection = Mage::getModel('bundle/option')->getResourceCollection()
00335 ->setProductIdFilter($this->getProduct($product)->getId())
00336 ->setPositionOrder()
00337 ->joinValues($this->getStoreFilter($product));
00338 $this->getProduct($product)->setData($this->_keyOptionsCollection, $optionsCollection);
00339 }
00340 return $this->getProduct($product)->getData($this->_keyOptionsCollection);
00341 }
00342
00343
00344
00345
00346
00347
00348
00349
00350 public function getSelectionsCollection($optionIds, $product = null)
00351 {
00352 if (!$this->getProduct($product)->hasData($this->_keySelectionsCollection)) {
00353 $selectionsCollection = Mage::getResourceModel('bundle/selection_collection')
00354 ->addAttributeToSelect(Mage::getSingleton('catalog/config')->getProductAttributes())
00355 ->setFlag('require_stock_items', true)
00356 ->setPositionOrder()
00357 ->addStoreFilter($this->getStoreFilter($product))
00358 ->addFilterByRequiredOptions()
00359 ->setOptionIdsFilter($optionIds);
00360 $this->getProduct($product)->setData($this->_keySelectionsCollection, $selectionsCollection);
00361 }
00362 return $this->getProduct($product)->getData($this->_keySelectionsCollection);
00363 }
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377 public function updateQtyOption($options, Varien_Object $option, $value, $product = null)
00378 {
00379 $optionProduct = $option->getProduct($product);
00380 $optionUpdateFlag = $option->getHasQtyOptionUpdate();
00381 $optionCollection = $this->getOptionsCollection($product);
00382
00383 $selections = $this->getSelectionsCollection($optionCollection->getAllIds(), $product);
00384
00385 foreach ($selections as $selection) {
00386 if ($selection->getProductId() == $optionProduct->getId()) {
00387 foreach ($options as &$option) {
00388 if ($option->getCode() == 'selection_qty_'.$selection->getSelectionId()) {
00389 if ($optionUpdateFlag) {
00390 $option->setValue(intval($option->getValue()));
00391 }
00392 else {
00393 $option->setValue($value);
00394 }
00395 }
00396 }
00397 }
00398 }
00399
00400 return $this;
00401 }
00402
00403
00404
00405
00406
00407
00408
00409
00410 public function prepareQuoteItemQty($qty, $product = null)
00411 {
00412 return intval($qty);
00413 }
00414
00415
00416
00417
00418
00419
00420
00421 public function isSalable($product = null)
00422 {
00423 $salable = parent::isSalable($product);
00424 if (!is_null($salable)) {
00425 return $salable;
00426 }
00427
00428 $optionCollection = $this->getOptionsCollection($product);
00429
00430 if (!count($optionCollection->getItems())) {
00431 return false;
00432 }
00433
00434 $requiredOptionIds = array();
00435
00436 foreach ($optionCollection->getItems() as $option) {
00437 if ($option->getRequired()) {
00438 $requiredOptionIds[$option->getId()] = 0;
00439 }
00440 }
00441
00442 $selectionCollection = $this->getSelectionsCollection($optionCollection->getAllIds(), $product);
00443
00444 if (!count($selectionCollection->getItems())) {
00445 return false;
00446 }
00447 $salableSelectionCount = 0;
00448 foreach ($selectionCollection as $selection) {
00449 if ($selection->isSalable()) {
00450 $requiredOptionIds[$selection->getOptionId()] = 1;
00451 $salableSelectionCount++;
00452 }
00453
00454 }
00455
00456 return (array_sum($requiredOptionIds) == count($requiredOptionIds) && $salableSelectionCount);
00457 }
00458
00459
00460
00461
00462
00463
00464
00465
00466 public function prepareForCart(Varien_Object $buyRequest, $product = null)
00467 {
00468 $result = parent::prepareForCart($buyRequest, $product);
00469
00470 if (is_string($result)) {
00471 return $result;
00472 }
00473
00474 $selections = array();
00475
00476 $product = $this->getProduct($product);
00477
00478 $_appendAllSelections = false;
00479 if ($product->getSkipCheckRequiredOption()) {
00480 $_appendAllSelections = true;
00481 }
00482
00483 if ($options = $buyRequest->getBundleOption()) {
00484 $qtys = $buyRequest->getBundleOptionQty();
00485 foreach ($options as $_optionId => $_selections) {
00486 if (empty($_selections)) {
00487 unset($options[$_optionId]);
00488 }
00489 }
00490 $optionIds = array_keys($options);
00491
00492 if (empty($optionIds)) {
00493 return Mage::helper('bundle')->__('Please select options for product.');
00494 }
00495
00496
00497 $product->getTypeInstance(true)->setStoreFilter($product->getStoreId(), $product);
00498 $optionsCollection = $this->getOptionsCollection($product);
00499 if (!$this->getProduct($product)->getSkipCheckRequiredOption()) {
00500 foreach ($optionsCollection->getItems() as $option) {
00501 if ($option->getRequired() && !isset($options[$option->getId()])) {
00502 return Mage::helper('bundle')->__('Required options not selected.');
00503 }
00504 }
00505 }
00506 $selectionIds = array();
00507
00508 foreach ($options as $optionId => $selectionId) {
00509 if (!is_array($selectionId)) {
00510 if ($selectionId != '') {
00511 $selectionIds[] = $selectionId;
00512 }
00513 } else {
00514 foreach ($selectionId as $id) {
00515 if ($id != '') {
00516 $selectionIds[] = $id;
00517 }
00518 }
00519 }
00520 }
00521
00522 $selections = $this->getSelectionsByIds($selectionIds, $product);
00523
00524
00525
00526
00527 foreach ($selections->getItems() as $key => $selection) {
00528 if (!$selection->isSalable()) {
00529 $_option = $optionsCollection->getItemById($selection->getOptionId());
00530 if (is_array($options[$_option->getId()]) && count($options[$_option->getId()]) > 1){
00531 $moreSelections = true;
00532 } else {
00533 $moreSelections = false;
00534 }
00535 if ($_option->getRequired() && (!$_option->isMultiSelection() || ($_option->isMultiSelection() && !$moreSelections))) {
00536 return Mage::helper('bundle')->__('Selected required options not available.');
00537 }
00538 }
00539 }
00540
00541 $optionsCollection->appendSelections($selections, false, $_appendAllSelections);
00542
00543 $selections = $selections->getItems();
00544 } else {
00545 $product->getTypeInstance(true)->setStoreFilter($product->getStoreId(), $product);
00546
00547 $optionCollection = $product->getTypeInstance(true)->getOptionsCollection($product);
00548
00549 $optionIds = $product->getTypeInstance(true)->getOptionsIds($product);
00550 $selectionIds = array();
00551
00552 $selectionCollection = $product->getTypeInstance(true)
00553 ->getSelectionsCollection(
00554 $product->getTypeInstance(true)->getOptionsIds($product),
00555 $product
00556 );
00557
00558 $options = $optionCollection->appendSelections($selectionCollection, false, $_appendAllSelections);
00559
00560 foreach ($options as $option) {
00561 if ($option->getRequired() && count($option->getSelections()) == 1) {
00562 $selections = array_merge($selections, $option->getSelections());
00563 } else {
00564 $selections = array();
00565 break;
00566 }
00567 }
00568 }
00569 if (count($selections) > 0) {
00570 $uniqueKey = array($product->getId());
00571 $selectionIds = array();
00572
00573
00574
00575
00576 usort($selections, array($this, "shakeSelections"));
00577
00578 foreach ($selections as $selection) {
00579 if ($selection->getSelectionCanChangeQty() && isset($qtys[$selection->getOptionId()])) {
00580 $qty = $qtys[$selection->getOptionId()] > 0 ? $qtys[$selection->getOptionId()] : 1;
00581 } else {
00582 $qty = $selection->getSelectionQty() ? $selection->getSelectionQty() : 1;
00583 }
00584
00585 $product->addCustomOption('selection_qty_' . $selection->getSelectionId(), $qty, $selection);
00586 $selection->addCustomOption('selection_id', $selection->getSelectionId());
00587
00588 if ($customOption = $product->getCustomOption('product_qty_' . $selection->getId())) {
00589 $customOption->setValue($customOption->getValue() + $qty);
00590 } else {
00591 $product->addCustomOption('product_qty_' . $selection->getId(), $qty, $selection);
00592 }
00593
00594
00595
00596
00597
00598
00599 $price = $product->getPriceModel()->getSelectionPrice($product, $selection, $qty);
00600 $attributes = array(
00601 'price' => Mage::app()->getStore()->convertPrice($price),
00602 'qty' => $qty,
00603 'option_label' => $selection->getOption()->getTitle(),
00604 'option_id' => $selection->getOption()->getId()
00605 );
00606
00607
00608 $_result = $selection->getTypeInstance(true)->prepareForCart($buyRequest, $selection);
00609 if (is_string($_result) && !is_array($_result)) {
00610 return $_result;
00611 }
00612
00613 if (!isset($_result[0])) {
00614 return Mage::helper('checkout')->__('Can not add item to shopping cart');
00615 }
00616
00617 $result[] = $_result[0]->setParentProductId($product->getId())
00618 ->addCustomOption('bundle_option_ids', serialize($optionIds))
00619 ->addCustomOption('bundle_selection_attributes', serialize($attributes))
00620 ->setCartQty($qty);
00621
00622
00623 $selectionIds[] = $_result[0]->getSelectionId();
00624 $uniqueKey[] = $_result[0]->getSelectionId();
00625 $uniqueKey[] = $qty;
00626 }
00627
00628
00629
00630 $uniqueKey = implode('_', $uniqueKey);
00631 foreach ($result as $item) {
00632 $item->addCustomOption('bundle_identity', $uniqueKey);
00633 }
00634 $product->addCustomOption('bundle_option_ids', serialize($optionIds));
00635 $product->addCustomOption('bundle_selection_ids', serialize($selectionIds));
00636
00637 return $result;
00638 }
00639 return $this->getSpecifyOptionMessage();
00640 }
00641
00642
00643
00644
00645
00646
00647 public function getSpecifyOptionMessage()
00648 {
00649 return Mage::helper('bundle')->__('Please specify product option(s)');
00650 }
00651
00652
00653
00654
00655
00656
00657
00658
00659 public function getSelectionsByIds($selectionIds, $product = null)
00660 {
00661 sort($selectionIds);
00662
00663 $usedSelections = $this->getProduct($product)->getData($this->_keyUsedSelections);
00664 $usedSelectionsIds = $this->getProduct($product)->getData($this->_keyUsedSelectionsIds);
00665
00666 if (!$usedSelections || serialize($usedSelectionsIds) != serialize($selectionIds)) {
00667 $usedSelections = Mage::getResourceModel('bundle/selection_collection')
00668 ->addAttributeToSelect('*')
00669 ->setFlag('require_stock_items', true)
00670 ->addStoreFilter($this->getStoreFilter($product))
00671 ->setPositionOrder()
00672 ->addFilterByRequiredOptions()
00673 ->setSelectionIdsFilter($selectionIds);
00674 $this->getProduct($product)->setData($this->_keyUsedSelections, $usedSelections);
00675 $this->getProduct($product)->setData($this->_keyUsedSelectionsIds, $selectionIds);
00676 }
00677 return $usedSelections;
00678 }
00679
00680
00681
00682
00683
00684
00685
00686
00687 public function getOptionsByIds($optionIds, $product = null)
00688 {
00689 sort($optionIds);
00690
00691 $usedOptions = $this->getProduct($product)->getData($this->_keyUsedOptions);
00692 $usedOptionsIds = $this->getProduct($product)->getData($this->_keyUsedOptionsIds);
00693
00694 if (!$usedOptions || serialize($usedOptionsIds) != serialize($optionIds)) {
00695 $usedOptions = Mage::getModel('bundle/option')->getResourceCollection()
00696 ->setProductIdFilter($this->getProduct($product)->getId())
00697 ->setPositionOrder()
00698 ->joinValues(Mage::app()->getStore()->getId())
00699 ->setIdFilter($optionIds);
00700 $this->getProduct($product)->setData($this->_keyUsedOptions, $usedOptions);
00701 $this->getProduct($product)->setData($this->_keyUsedOptionsIds, $optionIds);
00702 }
00703 return $usedOptions;
00704 }
00705
00706
00707
00708
00709
00710
00711
00712
00713 public function getOrderOptions($product = null)
00714 {
00715 $optionArr = parent::getOrderOptions($product);
00716
00717 $bundleOptions = array();
00718
00719 $product = $this->getProduct($product);
00720
00721 if ($product->hasCustomOptions()) {
00722 $customOption = $product->getCustomOption('bundle_option_ids');
00723 $optionIds = unserialize($customOption->getValue());
00724 $options = $this->getOptionsByIds($optionIds, $product);
00725 $customOption = $product->getCustomOption('bundle_selection_ids');
00726 $selectionIds = unserialize($customOption->getValue());
00727 $selections = $this->getSelectionsByIds($selectionIds, $product);
00728 foreach ($selections->getItems() as $selection) {
00729 if ($selection->isSalable()) {
00730 $selectionQty = $product->getCustomOption('selection_qty_' . $selection->getSelectionId());
00731 if ($selectionQty) {
00732 $price = $product->getPriceModel()->getSelectionPrice($product, $selection, $selectionQty->getValue());
00733
00734 $option = $options->getItemById($selection->getOptionId());
00735 if (!isset($bundleOptions[$option->getId()])) {
00736 $bundleOptions[$option->getId()] = array(
00737 'option_id' => $option->getId(),
00738 'label' => $option->getTitle(),
00739 'value' => array()
00740 );
00741 }
00742
00743 $bundleOptions[$option->getId()]['value'][] = array(
00744 'title' => $selection->getName(),
00745 'qty' => $selectionQty->getValue(),
00746 'price' => Mage::app()->getStore()->convertPrice($price)
00747 );
00748
00749 }
00750 }
00751 }
00752 }
00753
00754 $optionArr['bundle_options'] = $bundleOptions;
00755
00756
00757
00758
00759 if ($product->getPriceType()) {
00760 $optionArr['product_calculations'] = self::CALCULATE_PARENT;
00761 } else {
00762 $optionArr['product_calculations'] = self::CALCULATE_CHILD;
00763 }
00764
00765 $optionArr['shipment_type'] = $product->getShipmentType();
00766
00767 return $optionArr;
00768 }
00769
00770 public function shakeSelections($a, $b)
00771 {
00772 $aPosition = ($a->getOption()->getPosition()+1)*($a->getPosition()+1);
00773 $bPosition = ($b->getOption()->getPosition()+1)*($b->getPosition()+1);
00774 if ($aPosition == $bPosition) {
00775 if ($a->getSelectionId() == $b->getSelectionId()) {
00776 return 0;
00777 }
00778 return ($a->getSelectionId() < $b->getSelectionId()) ? -1 : 1;
00779 }
00780 return ($aPosition < $bPosition) ? -1 : 1;
00781 }
00782
00783
00784
00785
00786
00787
00788
00789 public function hasOptions($product = null)
00790 {
00791 $this->setStoreFilter($product->getStoreId(), $product);
00792 if (count($this->getSelectionsCollection($this->getOptionsCollection($product)->getAllIds(), $product)->getItems()) || $this->getProduct($product)->getOptions()) {
00793 return true;
00794 }
00795 return false;
00796 }
00797
00798
00799
00800
00801
00802
00803
00804 public function getForceChildItemQtyChanges($product = null)
00805 {
00806 return true;
00807 }
00808
00809
00810
00811
00812
00813
00814
00815
00816 public function getSearchableData($product = null)
00817 {
00818 $searchData = parent::getSearchableData($product);
00819 $product = $this->getProduct($product);
00820
00821 $optionSearchData = Mage::getSingleton('bundle/option')
00822 ->getSearchableData($product->getId(), $product->getStoreId());
00823 if ($optionSearchData) {
00824 $searchData = array_merge($searchData, $optionSearchData);
00825 }
00826
00827 return $searchData;
00828 }
00829 }