Adding the Child Product to The Cart

When adding a configurable product to the cart on magento, the configurable – or parent – product is what is added to the cart. This means that all of the info for the product added to the cart, aside from the configurable option values, are pulled from the parent.

The Situation

If you had a product that was configurable by different sizes, you might want each size to have different prices. For instance, if you were selling lengths of material, like rope or ribbons, you wouldn’t want every length to be the same price. So in a case like this, you would want to add the sample to the cart. This requires a little bit of code tweaking.

Related: Editing Frontend Product Configurable Attributes in Magento

Setting Up Your Module

First, create the following structure:


/app/code/local/Yourname/Addsimple/controllers/CartController.php
/app/code/local/Yourname/Addsimple/Model/Cart.php
/app/code/local/Yourname/Addsimple/etc/config.xml
/app/etc/modules/Yourname_Addsimple.xml

Tweaking AddAction Function in Controller

In the CartController.php file, create the following:

/app/code/local/Yourname/Addsimple/controllers/CartController.php

	<?php 
	require_once 'Mage/Checkout/controllers/CartController.php';
	class Yourname_Addsimple_CartController extends Mage_Checkout_CartController
	{
		public function addAction()
		{
			if (!$this->_validateFormKey()) {
				$this->_goBack();
				return;
			}
			$cart   = $this->_getCart();
			$params = $this->getRequest()->getParams();
			try {
				if (isset($params['qty'])) {
					$filter = new Zend_Filter_LocalizedToNormalized(
						array('locale' => Mage::app()->getLocale()->getLocaleCode())
					);
					$params['qty'] = $filter->filter($params['qty']);
				}

				$product = $this->_initProduct();
				$child = $product->getTypeInstance(true)->getProductByAttributes($params['super_attribute'], $product);
				$related = $this->getRequest()->getParam('related_product');

				/**
				 * Check product availability
				 */
				if (!$child) {
					$this->_goBack();
					return;
				}

				$cart->addProduct($child, $params);
				if (!empty($related)) {
					$cart->addProductsByIds(explode(',', $related));
				}

				$cart->saveSpecial();

				$this->_getSession()->setCartWasUpdated(true);

				/**
				 * @todo remove wishlist observer processAddToCart
				 */
				Mage::dispatchEvent('checkout_cart_add_product_complete',
					array('product' => $product, 'request' => $this->getRequest(), 'response' => $this->getResponse())
				);

				if (!$this->_getSession()->getNoCartRedirect(true)) {
					if (!$cart->getQuote()->getHasError()) {
						$message = $this->__('%s was added to your shopping cart.', Mage::helper('core')->escapeHtml($child->getName()));
						$this->_getSession()->addSuccess($message);
					}
					$this->_goBack();
				}
			} catch (Mage_Core_Exception $e) {
				if ($this->_getSession()->getUseNotice(true)) {
					$this->_getSession()->addNotice(Mage::helper('core')->escapeHtml($e->getMessage()));
				} else {
					$messages = array_unique(explode("\n", $e->getMessage()));
					foreach ($messages as $message) {
						$this->_getSession()->addError(Mage::helper('core')->escapeHtml($message));
					}
				}

				$url = $this->_getSession()->getRedirectUrl(true);
				if ($url) {
					$this->getResponse()->setRedirect($url);
				} else {
					$this->_redirectReferer(Mage::helper('checkout/cart')->getCartUrl());
				}
			} catch (Exception $e) {
				$this->_getSession()->addException($e, $this->__('Cannot add the item to shopping cart.'));
				Mage::logException($e);
				$this->_goBack();
			}
		}
	}

What we’re doing here, is overriding the default add to cart method, and making a few changes. Instead of just adding the the parent product, we’re loading the child of that product based on the chosen attributes. Also, I am calling a custom save function – saveSpecial() – and you will see why in the next file.

Creating New Save Functionality

/app/code/local/Yourname/Addsimple/Model/Cart.php

	<?php
	class Yourname_Addsimple_Model_Cart extends Mage_Checkout_Model_Cart
	{
		public function save()
		{
			Mage::dispatchEvent('checkout_cart_save_before', array('cart'=>$this));

			$this->getQuote()->getBillingAddress();
			$this->getQuote()->getShippingAddress()->setCollectShippingRates(true);
			$this->getQuote()->collectTotals();
			$this->getQuote()->save();
			$this->getCheckoutSession()->setQuoteId($this->getQuote()->getId());
			Mage::dispatchEvent('checkout_cart_save_after', array('cart'=>$this));
			foreach($this->getQuote()->getAllItems() as $_item)
			{
				$_item->setCustomPrice($_item->getPrice());
				$_item->setRowTotal($_item->getPrice()*$_item->getQty());
				$_item->setBaseRowTotal($_item->getBasePrice()*$_item->getQty());
				$_item->setOriginalCustomPrice($_item->getCustomPrice());
				$_item->setCalculationPrice($_item->getCustomPrice());
			}
			return $this;
		}

		public function saveSpecial()
		{
			Mage::dispatchEvent('checkout_cart_save_before', array('cart'=>$this));

			$this->getQuote()->getBillingAddress();
			$this->getQuote()->getShippingAddress()->setCollectShippingRates(true);
			$this->getQuote()->collectTotals();
			$this->getQuote()->save();
			$this->getCheckoutSession()->setQuoteId($this->getQuote()->getId());
			/**
			 * Cart save usually called after changes with cart items.
			 */
			Mage::dispatchEvent('checkout_cart_save_after', array('cart'=>$this));
			return $this;
		}
	}

What we’ve done here is taken the logic of the original save function and created a new function called saveSpecial, and
tweaked the original save function with some extra functionality. Because of the nature of the object that we are adding
to the cart, when the save function is called on cart page load, the correct pricing information is not set. In the save function, we’ve added functionality in order to display the correct prices.

Module Activation and Rewrites

Lastly, we have the fairly self explanatory config file – in which we set up our rewrites – and the module file – which
tells magento of our modules existance.

/app/code/local/Yourname/Addsimple/etc/config.xml

  <?xml version="1.0" ?>
  <config>
	<modules>
		<Yourname_Addsimple>
			<global>
				<models>
					<checkout>
						<rewrite>
							<cart>Yourname_Addsimple_Model_Cart</cart>
						</rewrite>						
					</checkout>
				</models>
				<frontend>
					<routers>
						<checkout>
							<args>
								<modules>
									<Yourname_Addsimple before="Mage_Checkout">Yourname_Addsimple</Yourname_Addsimple>
								</modules>
							</args>
						</checkout>						
					</routers>					
				</frontend>				
			</global>
		</Yourname_Addsimple>
	</modules>
  </config>

/app/etc/modules/Yourname_Addsimple.xml

	<?xml version="1.0" ?>
	<config>
		<modules>
			<Yourname_Addsimple>
				<active>true</active>
				<codePool>local</codePool>
			</Yourname_Addsimple>
		</modules>
	</config>

Done!

And there you have it, now you can set any sort of pricing rules on your simple products and expect the cart and checkout to reflect those prices. However, in order for any category based catalog or shopping cart price rules to have effect now, the simple products must now also be associated to categories, not just their parents.

Related: Mini Tutorial: Configurable Layouts in the Magento Backend