I am working to replace the options of configurable products by radio buttons.
There are interesting custom modules like:
But It is not really what I want.
My expected behavior is the following : the second attribute values group are updated when the user click on the first attribute ie When the end user click on a format, I load (via Ajax) the available dimension and the corresponding price.. Like the OSP module, I want to use the simple product price.
app/design/frontend/default/<…>/template/catalog/product/view/type/options/configurable.phtml:
<script type="text/javascript"> //<![CDATA[ var doAjaxCall=function(Forms) { var reloadurl = '/configurable/ajax/update'; new Ajax.Request(reloadurl, { method: 'post', parameters: Forms, onComplete: function(transport) { //console.log("response=" + transport.responseText); //$('ajaxdiv').update(transport.responseText); jQuery("#ajaxdiv").html(transport.responseText); } }); }; // Update Price var updatePrice=function(prix) { var priceFormat = <?php echo $this->helper('tax')->getPriceFormat(); ?>; var newPrice=formatCurrency(prix, priceFormat); jQuery('span .price:lt(2)').each(function() { // console.log( jQuery(this).html() ); jQuery(this).html(newPrice); }); }; //]]> </script> <?php $session = Mage::getSingleton('checkout/session'); $product=$this->getProduct(); $quote=$session->getQuote(); $selected=array(); if($quote->hasItems()) { foreach ($quote->getAllItems() as $item) { if($item->getHasChildren()) { foreach($item->getChildren() as $child) { array_push($selected,$product->getIdBySku($child->getSku())); } } else { array_push($selected,$product->getIdBySku($item->getSku())); } } } echo ($this->getHtml($selected)); ?>
I generate the HTML of the <input> tags into my Block Module.
An extract of my Block/Configurable.html module:
# more Configurable/Catalog/Block/Product/View/Type/Configurable.php <?php class MyStockPhoto_Configurable_Catalog_Block_Product_View_Type_Configurable extends Mage_Catalog_Block_Product_View_Type_Configurable { // Debug private function mydebug(&$var) { ob_start(); var_dump($var); $result=ob_get_clean(); return $result; } /** * Validating of super product option value * * @param array $attributeId * @param array $value * @param array $options * @return boolean */ protected function _validateAttributeValue($attributeId, &$value, &$options) { if(isset($options[$attributeId][$value['value_index']])) { return true; } return false; } /** * Validation of super product option * * @param array $info * @return boolean */ protected function _validateAttributeInfo(&$info) { if(count($info['options']) > 0) { return true; } return false; } /** * Calculation real price * * @param float $price * @param bool $isPercent * @return mixed */ protected function _preparePrice($price, $isPercent = false) { if ($isPercent && !empty($price)) { $price = $this->getProduct()->getFinalPrice() * $price / 100; } return $this->_registerJsPrice($this->_convertPrice($price, true)); } /** * Calculation price before special price * * @param float $price * @param bool $isPercent * @return mixed */ protected function _prepareOldPrice($price, $isPercent = false) { if ($isPercent && !empty($price)) { $price = $this->getProduct()->getPrice() * $price / 100; } return $this->_registerJsPrice($this->_convertPrice($price, true)); } /** * Replace ',' on '.' for js * * @param float $price * @return string */ protected function _registerJsPrice($price) { return str_replace(',', '.', $price); } /** * Convert price from default currency to current currency * * @param float $price * @param boolean $round * @return float */ protected function _convertPrice($price, $round = false) { if (empty($price)) { return 0; } $price = $this->getCurrentStore()->convertPrice($price); if ($round) { $price = $this->getCurrentStore()->roundPrice($price); } return $price; } protected function find_price($myoptions,$sattr1,$sval1,$sattr2,$sval2) { //Mage::log("search A:$sattr1 V:$sval1 A:$sattr2 V:$sval2"); foreach($myoptions as $id => $attributes) { if(isset($attributes[$sattr1][$sval1]) && isset($attributes[$sattr2][$sval2])) return $attributes[$sattr1][$sval1]; } } // selected = Product ID Array of selected product public function getHtml($selected=null) { $_product= $this->getProduct(); $baseCurrencyCode = Mage::app()->getStore()->getBaseCurrencyCode(); $currentCurrencyCode = Mage::app()->getStore()->getCurrentCurrencyCode(); $configs = $this->getRegularConfigNew(); // Update Session $session = Mage::getSingleton('core/session'); // Write configs into session for controllers use $session->setData('configs', $configs); $selected_uniq=array_unique($selected,SORT_NUMERIC); $selected_attr=array(); foreach($selected_uniq as $id) { $product=Mage::getModel('catalog/product'); $product->load($id); if($product->getId()) { foreach($this->getAllowAttributes() as $attribute) { $productAttribute = $attribute->getProductAttribute(); $productAttributeId = $productAttribute->getId(); $attributeValue = $product->getData($productAttribute->getAttributeCode()); $productAttribute = $attribute->getProductAttribute(); $productAttributeId = $productAttribute->getId(); $attributeValue = $product->getData($productAttribute->getAttributeCode()); $selected_attr[$productAttributeId]= $attributeValue; } } } $html=""; if($_product->isSaleable() && count($this->getAllowAttributes())) { $prev_key=""; $prev_value=""; $j=0; $price=0; $url="/configurable/ajax/update"; $html .= "<dl>"; foreach ($configs['attributes'] as $key => $_attribute) { $i=0; if($j>0) $html .= '<div id="ajaxdiv">'; $html .= '<dt><label class="required"><em>*</em> ' . $_attribute['label'] . '</label></dt>'; $html .= '<div class="input-box">'; foreach($_attribute['options'] as $value) { $maxattr=count($_attribute['options']); $html .= '<dd'; if($i == ($maxattr-1)) $html .= ' class="last"'; $html .= '>'; $html .= '<label class="label-radio-configurable" id="attribute'. $key . '" price="' . (string)((float)$value['price'] + (float) Mage::helpe r('directory')->currencyConvert($_product->getPrice(), $baseCurrencyCode, $currentCurrencyCode)) . '">'; $html .= '<input type="radio" name="super_attribute[' . $key . ']" id="attribute' . $key . '" class="validate-custom-configurable" value="' . $value['id'] . '"'; if(isset($selected_attr[$key]) && $value['id'] == $selected_attr[$key]) { $html .= ' checked'; if($j==0) { $prev_key=$key;$prev_value=$value['id'];} } if($j>0) $price=$this->find_price($configs['options_price'],$prev_key,$prev_value,$key,$value['id']); if($j>0 && $price != 0) $html .= ' onclick="updatePrice(\'' . sprintf("%.2f",$price) .'\');" price="' . sprintf("%.2f",$price) . ' ' . $curr entCurrencyCode .'"'; if($j==0) $html .= ' onclick="doAjaxCall(Form.serialize(this.form));"'; // Default Selection if not selected //if(($maxattr==1 && $i == 0)||(!isset($selected_attr[$key]) && $i==0 && $j==0)) $html .= ' checked '; $html .= '/>'; $html .= ' ' . $value['label']; if($j>0 && $price != 0) $html .= ' (' . sprintf("%.2f", $price) . ' ' . $currentCurrencyCode . ')'; $html .= '</label>'; $html .= '</dd>'; $i++; } if($j>0) $html .= '</div>'; $html .= '</div>'; $j++; } $html .= '</dl>'; } return $html; } public function getJsonConfigNew() { $attributes = array(); $options = array(); $options_price = array(); $store = $this->getCurrentStore(); $taxHelper = Mage::helper('tax'); $currentProduct = $this->getProduct(); $preconfiguredFlag = $currentProduct->hasPreconfiguredValues(); if ($preconfiguredFlag) { $preconfiguredValues = $currentProduct->getPreconfiguredValues(); $defaultValues = array(); } foreach ($this->getAllowProducts() as $product) { $productId = $product->getId(); foreach ($this->getAllowAttributes() as $attribute) { $productAttribute = $attribute->getProductAttribute(); $productAttributeId = $productAttribute->getId(); $attributeValue = $product->getData($productAttribute->getAttributeCode()); if (!isset($options[$productAttributeId])) { $options[$productAttributeId] = array(); } if (!isset($options[$productAttributeId][$attributeValue])) { $options[$productAttributeId][$attributeValue] = array(); } $options[$productAttributeId][$attributeValue][] = $productId; if (!isset($options_price[$productAttributeId])) { $options_price[$productAttributeId] = array(); } if (!isset($options_price[$productAttributeId][$attributeValue])) { $options_price[$productAttributeId][$attributeValue] = array(); } $options_price[$productAttributeId][$attributeValue] = $product->getFinalPrice(); } } $this->_resPrices = array( $this->_preparePrice($currentProduct->getFinalPrice()) ); foreach ($this->getAllowAttributes() as $attribute) { $productAttribute = $attribute->getProductAttribute(); $attributeId = $productAttribute->getId(); $info = array( 'id' => $productAttribute->getId(), 'code' => $productAttribute->getAttributeCode(), 'label' => $attribute->getLabel(), 'options' => array() ); $optionPrices = array(); $prices = $attribute->getPrices(); if (is_array($prices)) { foreach ($prices as $value) { if(!$this->_validateAttributeValue($attributeId, $value, $options)) { continue; } $currentProduct->setConfigurablePrice( $this->_preparePrice($value['pricing_value'], $value['is_percent']) ); $currentProduct->setParentId(true); Mage::dispatchEvent( 'catalog_product_type_configurable_price', array('product' => $currentProduct) ); $configurablePrice = $currentProduct->getConfigurablePrice(); if (isset($options[$attributeId][$value['value_index']])) { $productsIndex = $options[$attributeId][$value['value_index']]; } else { $productsIndex = array(); } $info['options'][] = array( 'id' => $value['value_index'], 'label' => $value['label'], 'price' => $this->_preparePrice($value['pricing_value'], $value['is_percent']), 'products' => isset($options[$attributeId][$value['value_index']]) ? $options[$attributeId][$value['value_index']] : array(), ); $optionPrices[] = $configurablePrice; } } /** * Prepare formated values for options choose */ foreach ($optionPrices as $optionPrice) { foreach ($optionPrices as $additional) { $this->_preparePrice(abs($additional-$optionPrice)); } } if($this->_validateAttributeInfo($info)) { $attributes[$attributeId] = $info; } // Add attribute default value (if set) if ($preconfiguredFlag) { $configValue = $preconfiguredValues->getData('super_attribute/' . $attributeId); if ($configValue) { $defaultValues[$attributeId] = $configValue; } } } $taxCalculation = Mage::getSingleton('tax/calculation'); if (!$taxCalculation->getCustomer() && Mage::registry('current_customer')) { $taxCalculation->setCustomer(Mage::registry('current_customer')); } $_request = $taxCalculation->getRateRequest(false, false, false); $_request->setProductClassId($currentProduct->getTaxClassId()); $defaultTax = $taxCalculation->getRate($_request); $_request = $taxCalculation->getRateRequest(); $_request->setProductClassId($currentProduct->getTaxClassId()); $currentTax = $taxCalculation->getRate($_request); $taxConfig = array( 'includeTax' => $taxHelper->priceIncludesTax(), 'showIncludeTax' => $taxHelper->displayPriceIncludingTax(), 'showBothPrices' => $taxHelper->displayBothPrices(), 'defaultTax' => $defaultTax, 'currentTax' => $currentTax, 'inclTaxTitle' => Mage::helper('catalog')->__('Incl. Tax') ); $config = array( 'attributes' => $attributes, 'template' => str_replace('%s', '#{price}', $store->getCurrentCurrency()->getOutputFormat()), 'basePrice' => $this->_registerJsPrice($this->_convertPrice($currentProduct->getFinalPrice())), 'oldPrice' => $this->_registerJsPrice($this->_convertPrice($currentProduct->getPrice())), 'productId' => $currentProduct->getId(), 'chooseText' => Mage::helper('catalog')->__('Choose an Option...'), 'taxConfig' => $taxConfig, 'options_price' => $options_price ); if ($preconfiguredFlag && !empty($defaultValues)) { $config['defaultValues'] = $defaultValues; } //Mage::log("config=" . $this->mydebug($config)); return Mage::helper('core')->jsonEncode($config); } public function getRegularConfigNew() { $attributes = array(); $options = array(); $options_price = array(); $store = $this->getCurrentStore(); $taxHelper = Mage::helper('tax'); $currentProduct = $this->getProduct(); $preconfiguredFlag = $currentProduct->hasPreconfiguredValues(); if ($preconfiguredFlag) { $preconfiguredValues = $currentProduct->getPreconfiguredValues(); $defaultValues = array(); } foreach ($this->getAllowProducts() as $product) { $productId = $product->getId(); foreach ($this->getAllowAttributes() as $attribute) { $productAttribute = $attribute->getProductAttribute(); $productAttributeId = $productAttribute->getId(); $attributeValue = $product->getData($productAttribute->getAttributeCode()); if (!isset($options[$productAttributeId])) { $options[$productAttributeId] = array(); } if (!isset($options[$productAttributeId][$attributeValue])) { $options[$productAttributeId][$attributeValue] = array(); } $options[$productAttributeId][$attributeValue][] = $productId; if (!isset($options_price[$productId])) { $options_price[$productId] = array(); } if (!isset($options_price[$productId][$productAttributeId])) { $options_price[$productId][$productAttributeId] = array(); } if (!isset($options_price[$productId][$productAttributeId][$attributeValue])) { $options_price[$productId][$productAttributeId][$attributeValue] = array(); } $options_price[$productId][$productAttributeId][$attributeValue] = $product->getFinalPrice(); } } $this->_resPrices = array( $this->_preparePrice($currentProduct->getFinalPrice()) ); foreach ($this->getAllowAttributes() as $attribute) { $productAttribute = $attribute->getProductAttribute(); $attributeId = $productAttribute->getId(); $info = array( 'id' => $productAttribute->getId(), 'code' => $productAttribute->getAttributeCode(), 'label' => $attribute->getLabel(), 'options' => array() ); $optionPrices = array(); $prices = $attribute->getPrices(); if (is_array($prices)) { foreach ($prices as $value) { if(!$this->_validateAttributeValue($attributeId, $value, $options)) { continue; } $currentProduct->setConfigurablePrice( $this->_preparePrice($value['pricing_value'], $value['is_percent']) ); $currentProduct->setParentId(true); Mage::dispatchEvent( 'catalog_product_type_configurable_price', array('product' => $currentProduct) ); $configurablePrice = $currentProduct->getConfigurablePrice(); if (isset($options[$attributeId][$value['value_index']])) { $productsIndex = $options[$attributeId][$value['value_index']]; } else { $productsIndex = array(); } $info['options'][] = array( 'id' => $value['value_index'], 'label' => $value['label'], 'price' => $this->_preparePrice($value['pricing_value'], $value['is_percent']), 'products' => isset($options[$attributeId][$value['value_index']]) ? $options[$attributeId][$value['value_index']] : array (), ); $optionPrices[] = $configurablePrice; } } /** * Prepare formated values for options choose */ foreach ($optionPrices as $optionPrice) { foreach ($optionPrices as $additional) { $this->_preparePrice(abs($additional-$optionPrice)); } } if($this->_validateAttributeInfo($info)) { $attributes[$attributeId] = $info; } // Add attribute default value (if set) if ($preconfiguredFlag) { $configValue = $preconfiguredValues->getData('super_attribute/' . $attributeId); if ($configValue) { $defaultValues[$attributeId] = $configValue; } } } $taxCalculation = Mage::getSingleton('tax/calculation'); if (!$taxCalculation->getCustomer() && Mage::registry('current_customer')) { $taxCalculation->setCustomer(Mage::registry('current_customer')); } $_request = $taxCalculation->getRateRequest(false, false, false); $_request->setProductClassId($currentProduct->getTaxClassId()); $defaultTax = $taxCalculation->getRate($_request); $_request = $taxCalculation->getRateRequest(); $_request->setProductClassId($currentProduct->getTaxClassId()); $currentTax = $taxCalculation->getRate($_request); $taxConfig = array( 'includeTax' => $taxHelper->priceIncludesTax(), 'showIncludeTax' => $taxHelper->displayPriceIncludingTax(), 'showBothPrices' => $taxHelper->displayBothPrices(), 'defaultTax' => $defaultTax, 'currentTax' => $currentTax, 'inclTaxTitle' => Mage::helper('catalog')->__('Incl. Tax') ); $config = array( 'attributes' => $attributes, 'template' => str_replace('%s', '#{price}', $store->getCurrentCurrency()->getOutputFormat()), 'basePrice' => $this->_registerJsPrice($this->_convertPrice($currentProduct->getFinalPrice())), 'oldPrice' => $this->_registerJsPrice($this->_convertPrice($currentProduct->getPrice())), 'productId' => $currentProduct->getId(), 'chooseText' => Mage::helper('catalog')->__('Choose an Option...'), 'taxConfig' => $taxConfig, 'options_price' => $options_price ); if ($preconfiguredFlag && !empty($defaultValues)) { $config['defaultValues'] = $defaultValues; } //:$config = array_merge($config, $this->_getAdditionalConfig()); //Mage::log("config=" . $this->mydebug($config)); return $config; } }
Note: I have modified the getRegularConfig Megento’s method to add the price of the simple products (‘options_price’ attribute here).
And I have created my Ajax controllers to update the second block of attribute values when we click on the first attribute.
Below the code of the controllers :
# more Configurable/controllers/AjaxController.php <?php require_once 'Mage/Catalog/controllers/ProductController.php'; class MyStockPhoto_Configurable_AjaxController extends Mage_Catalog_ProductController { // Debug private function mydebug(&$var) { ob_start(); var_dump($var); $result=ob_get_clean(); return $result; } /** * Product view action */ public function viewAction() { // Get initial data from request $categoryId = (int) $this->getRequest()->getParam('category', false); $productId = (int) $this->getRequest()->getParam('id'); $specifyOptions = $this->getRequest()->getParam('options'); // Prepare helper and params $viewHelper = Mage::helper('catalog/product_view'); $params = new Varien_Object(); $params->setCategoryId($categoryId); $params->setSpecifyOptions($specifyOptions); // Render page try { $viewHelper->prepareAndRender($productId, $this, $params); } catch (Exception $e) { if ($e->getCode() == $viewHelper->ERR_NO_PRODUCT_LOADED) { if (isset($_GET['store']) && !$this->getResponse()->isRedirect()) { $this->_redirect(''); } elseif (!$this->getResponse()->isRedirect()) { $this->_forward('noRoute'); } } else { Mage::logException($e); $this->_forward('noRoute'); } } } protected function find_price($myoptions,$sattr1,$sval1,$sattr2,$sval2) { //Mage::log("search A:$sattr1 V:$sval1 A:$sattr2 V:$sval2"); foreach($myoptions as $id => $attributes) { if(isset($attributes[$sattr1][$sval1]) && isset($attributes[$sattr2][$sval2])) return $attributes[$sattr1][$sval1]; } } public function updateAction() { $productId=$this->getRequest()->getParam('product'); //Mage::log("ID=$productId"); $super_attribute=$this->getRequest()->getParam('super_attribute'); $params=$this->getRequest()->getParams(); if(!isset($params)) $params=Mage::app()->getRequest()->getParams(); foreach($super_attribute as $skey => $svalue) break; Mage::log("KEY=$skey VALUE=$svalue"); // Mage::log("Params=".$this->mydebug($params)); // Get Session $session = Mage::getSingleton('core/session'); $configs=($session->getData('configs')); //Mage::log("configs=".$this->mydebug($configs)); $baseCurrencyCode = "€"; $currentCurrencyCode = $baseCurrencyCode; if(true) { $html=""; $price=0; $j=0; foreach ($configs['attributes'] as $key => $_attribute) { $i=0; if($j > 0) { $html .= '<dt><label class="required"><em>*</em> ' . $_attribute['label'] . '</label></dt>'; $html .= '<div class="input-box">'; foreach($_attribute['options'] as $value) { $html .= '<dd'; if($i == (count($_attribute['options'])-1)) $html .= ' class="last"'; $html .= '>'; $price=$this->find_price($configs['options_price'],$skey,$svalue,$key,$value['id']); $html .= '<label class="label-radio-configurable" id="attribute'. $key . '" price="' . (string)$price . + ' €' . '">'; $html .= '<input type="radio" name="super_attribute[' . $key . ']" id="attribute' . $key . '" class="validate-custom-configurable" v alue="' . $value['id'] . '" onclick="updatePrice(\'' . sprintf("%.2f",$price) . '\');"'; $html .= ' price="' . sprintf("%.2f",$price) . ' ' . $currentCurrencyCode .'"'; $html .= '/>'; $html .= ' ' . $value['label']; $html .= ' (<strong style="color: #FF0000;">' . sprintf("%.2f", $price) . ' ' . $currentCurrencyCode . '</strong>)'; $html .= '</label>'; $html .= '</dd>'; $i++; } $html .= '</div>'; } $j++; } } echo $html; } } ?>
Update : The new template of my configurable products is now online and It is working as expected : You can see the result on my store : www.mystockphoto.fr.
My article helps you ! Don’t hesitate to donate !
[pfund-progress-bar campaign_id= »940″]
My Module API to create Configurable Product is available here !
Voila,
Nicolas Portais
Author Photographer
http://www.mystockphoto.fr/
Nice Article. But could you please post the entire code. The code is not working here
Hello,
I have updated my article with the entire code.
You should have all infos to create your module now.
Nicolas