zen of coding

Intercepting “add to cart” action in Magento

(Nuts and bolts of Magento? Maybe…)

Magento EE 1.11

Once in a while we’d like to intercept Magento’s “add to cart” event and do some fun things with that. Maybe apply a discount or add a specialty product, or as I’ll show in this case, block a user from adding a product to the cart.

As I usually like to show specific examples, let’s consider the following problem:
Using Magento’s EE customer segments we’d like to ensure that a person can purchase some “restricted” product only once, while they are in the given segment.
If they try to add this restricted product to the cart again, while being in our previously-designated segment, we’ll block that action and redirect them back to the cart with an error message.

The best way to handle these sorts of tasks is by employing Magento’s Event/Observer pattern.

First, we need to listen to (or properly phrased “Observe”) an “add to cart” event. In Magento it happens to be checkout_cart_product_add_after.
Shouldn’t we listen to the “*_before” event? Well, yeah… but in the often-crazy Magento world the checkout_cart_product_add_after event actually occurs before the product is added to the cart. (My only guess at this bizarre naming convention, is that the above event is dispatched after the Cart Model is called, but before the cart is actually saved.)

So with that in mind, we’ll adjust our module’s config.xml to tell Magento that we are ready to listen to the above event:

<checkout_cart_product_add_after>
  <observers>
    <teknoid_catch_standard_add_to_cart>
      <type>singleton</type>
      <class>Teknoid_Coolmodule_Model_Observer</class>
      <method>catchAddToCart</method>
    </teknoid_catch_standard_add_to_cart>
  </observers>
</checkout_cart_product_add_after>

Next we’ll need to create an Observer with the catchAddToCart method.

Let’s take a look at that:

public function catchAddToCart($observer) {
        //getting product ID from event data
        $productId = $observer->getProduct()->getId();

        //get product's attribute set ID. (Our designated attribute set ID is 10)
        $attSetId = Mage::getModel('catalog/product')->load($productId)->getAttributeSetId();

        //get current customer
      $customer = Mage::getSingleton('customer/session')->getCustomer();

        //get website ID
      $websiteId = Mage::app()->getStore()->getWebsiteId();

        //only have one segment for testing (ID is 1)
      $segmentId = 1;

        //let's get all segments for this customer ID and website ID
        $segments = Mage::getModel('enterprise_customersegment/customer')
                ->getCustomerSegmentIdsForWebsite($customer->getId(), $websiteId);

        /**
         * If customer is in segment of "designated" and is trying to add another
         * "restricted" product (based on attribute set ID) we block "add to cart"
         * and redirect back with an error message.
         */

   if(in_array($segmentId, $segments) && ($attSetId == 10)) {
            //set error message in session
            Mage::getSingleton('core/session')->addError('Sorry, you cannot add this product to cart');

            //get URL model for cart/index
            $url = Mage::getModel('core/url')->getUrl('checkout/cart/index');

            //set redirect
            Mage::app()->getResponse()->setRedirect($url);

            //send redirect
            Mage::app()->getResponse()->sendResponse();

            //block further action
            exit;
        }
    }

I’m going to keep this post purposely light on details, as there are countless tutorials out there explaining the Event/Observer and configuration setup of Magento.
The code is also pretty straight-forward and well commented, so I’ll just summarize the outcome:

  • We’ve created an observer in Teknoid/Coolmodule/Model/Observer.php (This matches the namespace designation in config.xml)
  • We’ve added a method catchAddToCart() as defined in configuration
  • We are loading the product recieved by the observer
  • We are checking if the current customer is in our “designated” segment and our “restricted” product matches a given attribute set.
  • If so, we set the error message to the session and redirect the customer back to cart

That’s it for now. I hope to publish a couple more posts in the near future about Magento’s Event/Observer strategies and some examples of the things we can do using this powerful way to extend Magento’s functionality.

  • Timon Davis

    Thank you for sharing! I’ve looked at a few solutions for the interception of the add to cart event (or some idea like it) and all of them seem to invoke the _after_ hook. Wouldn’t it be better to terminate the process _before_ anything is committed to the database ( it seems correct to want to execute 0 transactions instead of 2 )? Is there any way to _prevent_ the action from being executed?

    **Ah, I just read your middle paragraph a little more closely. Thank you for being specific in your reasoning!

%d bloggers like this: