In this post, we will understand why and how to override existing Magento functionality.

What is overriding here?

Sometimes we want to change existing Magento functionality.

One way of doing this is to modify the Magento core files which is never recommended because if you in future upgrade your Magento, you will loose all your work as core files will be replaced.

The better way is to create your own functionality inside your module/theme. You will create your code in app\code\local folder whereas you will create your templates in your current theme.

Overriding existing templates

To override Magento’s template, follow these steps:

  1. I assume that you have already created your own theme. If you have just installed Magento with sample data ( check setting Magento with sample data ), you will have rwd theme. Otherwise if you have an existing theme, you can check base with that theme.
  2. Compare the folders base\default\template and rwd\default\template
  3. Lot of folders are already copied to the rwd theme from the base theme i.e. they have same folder structure as well as same file names.
  4. We copy only those folders/files from the base theme which we want to change and there is no need to copy everything.
  5. Magento will look for the files in the current theme first. If it is not found in current theme, it then looks into the base theme.
Overriding a method from existing model

Here we will see how we can override a method from an existing model to change the functionality. Let us try to override the method getPrice() from the product model. This method exists in the file app\core\Mage\Catalog\Model\Product.php.

  1. First let us create a file test-price.php in the root directory of your website and call this method. Here is the code:
    <?php
    require 'app/Mage.php';
    
    Mage::app();
    
    $productId = 886;       // use your own product id
    
    $product = Mage::getModel('catalog/product')->load($productId);
    
    echo $product->getPrice();
  2. Visit the URL:
    http://localhost/magento/test-price.php
  3. You might see the output like this:  50.0000. If we know that no products will have floating value for price, it would be better to show 50 only. Hence, we are going to override the getPrice() method for that.
  4. Create a new module Test\Price. Create two folders etc and Model inside Price folder. Next create config.xml file inside the etc folder with following content:
    <?xml version="1.0" ?>
    <config>
        <modules>
            <Test_Price>
                <version>1.0</version>
            </Test_Price>
        </modules>
        <global>
            <models>
                <catalog>
                    <rewrite>
                        <product>Test_Price_Model_Product</product>
                    </rewrite>
                </catalog>
            </models>
        </global>
    </config>

    Since we are overriding method from Catalog module, we write:

    <catalog>
         <rewrite>

    Next, we are overriding the method from Product model, hence we write:

    <product>Test_Price_Model_Product</product>

    We are telling Magento to use class from Test\Price\Model\Product.php instead of Mage\Catalog\Model\Product.php

  5. Now, create a file Product.php in local\Test\Price\Model folder.
    Copy the getPrice() method from Mage\Catalog\Model\Product.php and paste it here.
    Next we will modify this method according to us:

    <?php
    class Test_Price_Model_Product extends Mage_Catalog_Model_Product{
    
        public function getPrice(){
            if ($this->_calculatePrice || !$this->getData('price')) {
                $price = $this->getPriceModel()->getPrice($this);
            } else {
                $price = $this->getData('price');
            }
    
            return floor($price);
        }
    }
  6. Lastly, create module configuration file app\etc\modules\Test_Price.xml with following code:
    <?xml version="1.0" ?>
    <config>
        <modules>
            <Test_Price>
                <active>true</active>
                <codePool>local</codePool>
            </Test_Price>
        </modules>
    </config>
  7. Refresh your cache and visit the URL:
    http://localhost/magento/test-price.php

    You should see the .0000 removed.

Overriding existing helper methods

Now let’s see how we can override existing helper methods. Let us override the getImageUrl() method from Mage\Catalog\Helper\Product.php file. Here are the steps:

  1. Let us create one more module Test\Product under app\local folder. Next, we will create Test_Product.xml under app\etc\modules folder with following XML:
    <?xml version="1.0" ?>
    <config>
        <modules>
            <Test_Product>
                <active>true</active>
                <codePool>local</codePool>
            </Test_Product>
        </modules>
    </config>
  2. Now create the file app\local\Test\Product\Helper\Product.php. Copy the getImageUrl() method from app\core\code\Mage\Catalog\Helper\Product.php and paste into this file. Then we will customize it according to our requirement. Here, we will simply change the default image name if the product image is not available.
    <?php
    class Test_Product_Helper_Product extends Mage_Customer_Helper_Data
    {
        public function getImageUrl($product)
        {
            $url = false;
            if (!$product->getImage()) {
                $url = Mage::getDesign()->getSkinUrl('images/default.jpg');
            }
            elseif ($attribute = $product->getResource()->getAttribute('image')) {
                $url = $attribute->getFrontend()->getUrl($product);
            }
            return $url;
        }
    }
  3. Now, we will modify our etc\config.xml to override the existing helper. Here is how we will do it:
    <?xml version="1.0" ?>
    <config>
    <modules>
        <Test_Product>
            <version>1.0</version>
        </Test_Product>
    </modules>
    <global>
        <helpers>
            <catalog>
                <rewrite>
                    <product>Test_Product_Helper_Product</product>
                </rewrite>
            </catalog>
        </helpers>
    </global>
    </config>
  4. Let us now test. Create a file test-helper.php in the root of your website. Add following code to it:
    <?php
    require_once('app/Mage.php');
    
    Mage::app();
    
    $product = Mage::getModel('catalog/product')->load(1);   // change 1 to any product id
    $helper = Mage::helper('catalog/product');
    
    $image = $helper->getImageUrl($product);
    
    echo $image;
  5. This time if an image is not found with a product, getImageUrl() will return default.jpg.
Overriding controllers

In Magento, controllers handles incoming requests via its methods i.e. each URL is mapped to some method in a controller. All such methods have Action as suffix. For example: indexAction()

To change the functionality of existing controllers without messing core code is a bit different then helpers and models.
In this example, we will override the loginPostAction() method from the AccountController.
We want to do login using AJAX. For this, we will change the method to return text messages for valid or wrong login instead of redirection. Here are the steps:

  1. Create a new module Test\Customer. Then create etc and controllers folder inside the Customer folder. Next create config.xml file inside etc folder.
  2. Now, create file AccountController.php inside the controllers folder with following code:
    <?php
    require_once 'Mage/Customer/controllers/AccountController.php';
    class Test_Customer_AccountController extends Mage_Customer_AccountController
    {
        
    }
    

    Here we included AccountController.php from core. Then we extended Test_Customer_AccountController class from the core Mage_Customer_AccountController class.
    Now we can override any method we want.

  3. Copy the loginPostController() method from core\Mage\Customer\controllers\AccountController.php into our file. Make whatever change you want.
  4. Here is the complete code for our own AccountController class:
    <?php
    require_once 'Mage/Customer/controllers/AccountController.php';
    class Test_Customer_AccountController extends Mage_Customer_AccountController
    {
        public function loginPostAction()
        {
            if (!$this->_validateFormKey()) {
                echo "Invalid form";
                return;
            }
    
            if ($this->_getSession()->isLoggedIn()) {
                echo "Already logged";
                return;
            }
            $session = $this->_getSession();
    
            if ($this->getRequest()->isPost()) {
                $login = $this->getRequest()->getPost('login');
                if (!empty($login['username']) && !empty($login['password'])) {
                    try {
                        $session->login($login['username'], $login['password']);
    
                        if ($session->isLoggedIn())
                            echo "Login successful";
                        else
                            echo "Invalid login";
    
                    } catch (Mage_Core_Exception $e) {
                        switch ($e->getCode()) {
                            case Mage_Customer_Model_Customer::EXCEPTION_EMAIL_NOT_CONFIRMED:
                                $value = $this->_getHelper('customer')->getEmailConfirmationUrl($login['username']);
                                $message = $this->_getHelper('customer')->__('This account is not confirmed. <a href="%s">Click here</a> to resend confirmation email.', $value);
                                break;
                            case Mage_Customer_Model_Customer::EXCEPTION_INVALID_EMAIL_OR_PASSWORD:
                                $message = $e->getMessage();
                                break;
                            default:
                                $message = $e->getMessage();
                        }
    
                        echo $message;
    
                    } catch (Exception $e) {
                        // Mage::logException($e); // PA DSS violation: this exception log can disclose customer password
                    }
                } else {
                    echo $this->__('Login and password are required.');
                }
            }
        }
    }
  5. Update your etc\config.xml like this:
    <?xml version="1.0" ?>
    <config>
        <modules>
            <Test_Customer>
                <version>1.0</version>
            </Test_Customer>
        </modules>
        <frontend>
            <routers>
                <customer>
                    <args>
                        <modules>
                            <customer before="Mage_Customer">Test_Customer</customer>
                        </modules>
                    </args>
                </customer>
            </routers>
        </frontend>
    </config>
  6. Create the file Test_Customer.xml into app\etc\modules directory with following XML:
    <?xml version="1.0" ?>
    <config>
        <modules>
            <Test_Customer>
                <active>true</active>
                <codePool>local</codePool>
            </Test_Customer>
        </modules>
    </config>
  7. Refresh your cache.
  8. Now, if you try to login, your controller will be called and instead of redirection, you should see text messages.