· Thilanga Pitigala

Thilanga Pitigala     Archive     Feed

How to override a Magento core model

If you want to modify/ alter some of data in magento with in the default workflow you can achieve this in 2 ways.

  1. Use an Event
  2. Override the core model.

Then the big question. What is the best way from above options. Without an argument Event based override is the best option. Because this method is decoupled from the core and will give us a flexibility around upgrading.

We can't use events all the time. Because Magento got limited events. So in that case we will have to override the the core method to do our job.

Scenario

Stop custom temporary options get saved against products when adding to cart


app/code/core/Mage/Checkout/Model/Cart.php has addProduct which is responsible for saving products to the cart.

If you observer the method you can see that there is only one event we can use checkout_cart_product_add_after, Which is not gong to help in our case as we need to modify $requestInfo before it get pass in to the $this->_getProductRequest($requestInfo).

So as I explained above let's override the addProduct. By doing this we can have more flexibility around the code.

Acme/Test/etc/config.xml
    <global> <!-- Magento Scope -->
        <models> 
            <checkout> <!-- Core module we want to override -->
                <rewrite>
                    <cart>Acme_Test_Model_Cart</cart> <!-- Module file we want to override -->
                </rewrite>
            </checkout>

        </models>
    </global>
Acme/Test/Model/Cart.php
<?php

class Acme_Test_Model_Cart extends Mage_Checkout_Model_Cart
{
    /**
     * Add product to shopping cart (quote)
     *
     * @param   int|Mage_Catalog_Model_Product $productInfo
     * @param   mixed $requestInfo
     * @return  Mage_Checkout_Model_Cart
     */
    public function addProduct($productInfo, $requestInfo=null)
    {
        if (isset($requestInfo['custom_label'])) {
            unset($requestInfo['custom_label']);
        }

        //Keeping the golden rule of Magento in the head
        //we passed our modified $requestInfo to the parent method.
        //Keep the custom method foot print as small as you can

        parent::addProduct($productInfo, $requestInfo);

    }
}

How to override a Controller

Overriding controllers is bit different to Model/Blocks/Helpers as Controllers do not get loaded though the autoloader.

Scenario

Send a copy of the mail when a user/visitor submits the contact form.


In Magento contact form is handled by Mage_Contacts. If you take a look at the Mage_Contacts_IndexController you can find the postAction method which is responsible for the contact form submission.

In this example we will be overriding the postAction in Mage_Contacts_IndexController with Acme_ContactExtended_IndexController

Acme/ContactExtended/etc/config.xml
    <frontend> <!-- Magento Area -->
        <routers>
            <contacts> <!-- Core module we want to override -->
                <args>
                    <modules>
                        <Acme_ContactExtended before="Mage_Contacts">Acme_ContactExtended</Acme_ContactExtended> <!-- Our custom Controller -->
                    </modules>
                </args>
            </contacts>
        </routers>
    </frontend>
Acme/ContactExtended/controllers/IndexController.php

I haven't added system.xml in this tutorial and this code might not work if you copy-paste in to you file :). I will explain system.xml in a different article in the future.

<?php

require_once Mage::getModuleDir('controllers', 'Mage_Contacts') . DS . 'IndexController.php';

class Acme_ContactExtended_IndexController extends Mage_Contacts_IndexController
{
    const XML_PATH_SENDER_NOTIFICATION_ENABLED = 'contacts/sender_notification/enabled';
    const XML_PATH_SENDER_NOTIFICATION_EMAIL_TEMPLATE = 'contacts/sender_notification/email_template';

    public function postAction()
    {

        $post = $this->getRequest()->getPost();
        if ($post) {
            $translate = Mage::getSingleton('core/translate');
            /* @var $translate Mage_Core_Model_Translate */
            $translate->setTranslateInline(false);
            try {
                $postObject = new Varien_Object();
                $postObject->setData($post);

                $error = false;

                if (!Zend_Validate::is(trim($post['first-name']), 'NotEmpty')) {
                    $error = true;
                }

                if (!Zend_Validate::is(trim($post['last-name']), 'NotEmpty')) {
                    $error = true;
                }


                if (!Zend_Validate::is(trim($post['email']), 'EmailAddress')) {
                    $error = true;
                }

                if (!Zend_Validate::is(trim($post['comment']), 'NotEmpty')) {
                    $error = true;
                }


                if (Zend_Validate::is(trim($post['hideit']), 'NotEmpty')) {
                    $error = true;
                }

                if ($error) {
                    throw new Exception();
                }

                $mailTemplate = Mage::getModel('core/email_template');
                /* @var $mailTemplate Mage_Core_Model_Email_Template */
                $mailTemplate->setDesignConfig(array('area' => 'frontend'))
                    ->setReplyTo($post['email'])
                    ->sendTransactional(
                        Mage::getStoreConfig(self::XML_PATH_EMAIL_TEMPLATE),
                        Mage::getStoreConfig(self::XML_PATH_EMAIL_SENDER),
                        Mage::getStoreConfig(self::XML_PATH_EMAIL_RECIPIENT),
                        null,
                        array('data' => $postObject)
                    );

                if (!$mailTemplate->getSentSuccess()) {
                    throw new Exception();
                }


                /* send sender notification */
                if (Mage::getStoreConfigFlag(self::XML_PATH_SENDER_NOTIFICATION_ENABLED)) {
                    $customerMailTemplate = Mage::getModel('core/email_template');
                    /* @var $mailTemplate Mage_Core_Model_Email_Template */
                    $customerMailTemplate->setDesignConfig(array('area' => 'frontend'))
                        ->setReplyTo(self::XML_PATH_EMAIL_RECIPIENT)
                        ->sendTransactional(
                            Mage::getStoreConfig(self::XML_PATH_SENDER_NOTIFICATION_EMAIL_TEMPLATE),
                            Mage::getStoreConfig(self::XML_PATH_EMAIL_SENDER),
                            $post['email'],
                            null,
                            array('data' => $postObject)
                        );

                    if (!$customerMailTemplate->getSentSuccess()) {
                        throw new Exception();
                    }
                }
                /*  */

                $translate->setTranslateInline(true);

                Mage::getSingleton('customer/session')->addSuccess(Mage::helper('contacts')->__('Your inquiry was submitted and will be responded to as soon as possible. Thank you for contacting us.'));
                $this->_redirect('*/*/');

                return;
            } catch (Exception $e) {
                $translate->setTranslateInline(true);

                Mage::getSingleton('customer/session')->addError(Mage::helper('contacts')->__('Unable to submit your request. Please, try again later'));
                $this->_redirect('*/*/');
                return;
            }

        } else {
            $this->_redirect('*/*/');
        }
    }

}

When overriding core files always the best practice is to minimize the foot print of custom codes. We shouldn't be copying the whole function from the core file. Minimum custom foot prints will help us to upgrade Magento easily.

In Acme/ContactExtended/controllers/IndexController.php you will see that I haven't followed the best practice I mentioned above. This is because:

  1. We have custom fields in the contact form (ex: First Name, Last Name etc..)
  2. We need custom validations
  3. postAction use $_POST to get posted values in the core

Summary

By following the above method, Magento will process our Controller before Mage_Contact. So you are the master of Contact form now. Happy coding!