Magento Development

Refs: Magento for Developers

Dev Tricks

Dev Mode

add the following line to .htaccess

SetEnv MAGE_IS_DEVELOPER_MODE 1

Magerun

turn on template and block hints:

magerun dev:template-hints
magerun dev:template-hints-blocks

remove cache by following line, Do Not Trust magerun cache:clean

rm -rf var/cache/*

Debug Logging

use Mage::log and the debug() method

$model = Mage::getModel('modelname');
Mage::log($model->debug(), Zend_Log::INFO, 'debug.log', true);

debug layout issues:

Mage::log($this->getLayout()->getUpdate()->asString(), Zend_Log::INFO, 'debug.log', true); // layout xml file
Mage::log(print_r($this->getLayout()->getUpdate()->getHandles(), true), Zend_Log::INFO, 'debug.log', true); // layout hanles used
die();

Security

Update Password

Update magento password

UPDATE admin_user SET password = CONCAT(MD5('xxNewPassword'), ':xx') WHERE username = 'admin';

Patching

Magento patches finder, find which patches need to be applied for a paticular magento version:

http://fabrizioballiano.net/magento-patches/

if you got different line endings error when pathcing, use following command to convert line ending:

find . -type f -regextype posix-extended -regex '.*\.(php|phtml|html|xml)' -exec dos2unix {} \;

protect version control, var and downloader directories

RewriteRule \.git/ - [F]
RewriteRule \downloader/ - [F]

MVC structure

Magento MVC structure

modules

when you want to extend Magento, put your code at
app/code/local/Package/Modulename

and create an XML file (Packagename_Modulename.xml) at
app/etc/modules

Magento is a Configuration-Based MVC system, each module got a config.xml file,it contains all the relevant configuration for a module (Models, Helpers, Routes for Controllers, Event Handlers, etc), and at runtime, all modules' config files are loaded into one large configuration tree

theme basics

Magento Theme Development: An Introduction

translation

ref: Magento translation hierarchy

Translation packages are located at `app/locale/', such as:

app/locale/zh_CN

each module got its own translation file:

Mage_Adminhtml.csv
Mage_AdminNotification.csv
Mage_AmazonPayments.csv
...

these translations can be overriden with 'app/design/frontend///locale/zh_CN/translate.csv', such as use following line to replace the Your Language: translation in Mage_Page.csv

"Mage_Page::Your Language:","测试,你好,您的语言:"

pages and static blocks should be translated in 'CMS' section of the admin area

quick code to get strings for translation (or use Poedit):

grep -o "__('[^']*')" * | sed -r "s/__\('(.*)'\)/\"\1\",\"\"/"  | sort -u

cms and blocks

it's cumbersome to add translated cms and block through admin UI, we can add cms and block through SQL:

Store Scopes

Magento got three level scopes:

Global - Website - Store - Store View

Store Views usually mean different Locales, Store is just a group of Store Views; Configurations are usually set on Website or Store View, not Store

add a new store view

migrate an installation

command line tools

install a package

./mage install community Mage_Locale_zh_CN

Routing / Dispatching / URL

http://example.com/catalog/category/view/id/25

Model loading

Magento uses static factory methods to instantiate Models, Helpers and Blocks

Mage::getModel('catalog/product');
Mage::helper('catalog/product');

all model classes inherit from Varien_Object, use magic __call method to implement getters and setters

getter/setter example:

$model = Mage::getModel('catalog/product')->load(27);
$price = $model->getPrice();
$price += 5;
$model->setPrice($price)->setSku('SK83293432');
$model->save();

collection example:

$products_collection = Mage::getModel('catalog/product')
    ->getCollection()
    ->addAttributeToSelect('*')
    ->addFieldToFilter('price','5.00');

foreach($products_collection as $product)
{
    echo $product->getName();
}

Magento has two broad types of Model objects.

* One is a traditional "One Object, One Table" Active Record style Model. When you instantiate these Models, all attributes are automatically selected.

* The second type of Model is an Entity Attribute Value (EAV) Model. EAV Models spread data across several different tables in the database. This gives the Magento system the flexibility to offer its flexible product attribute system without having to do a schema change each time you add an attribute.

Basic Model Operations

All Magento Models inherit from the Varien_Object class. This class is part of the Magento system library and not part of any Magento core module. You can find this object at

lib/Varien/Object.php

Magento Models store their data in a protected _data property. The Varien_Object class gives us several methods we can use to extract this data. You've already seen getData, which will return an array of key/value pairs. This method can also be passed a string key to get a specific field.

$model->getData();
$model->getData('title');

There's also a getOrigData method, which will return the Model data as it was when the object was initially populated, (working with the protected _origData method).

$model->getOrigData();
$model->getOrigData('title');

The Varien_Object also implements some special methods via PHP's magic __call method. You can get, set, unset, or check for the existence of any property using a method that begins with the word get, set, unset or has and is followed by the camel cased name of a property.

$model->getBlogpostId();
$model->setBlogpostId(25);
$model->unsetBlogpostId();
if($model->hasBlogpostId()){...}

For this reason, you'll want to name all your database columns with lower case characters and use underscores to separate characters.

Layout

typical controller code, load and render layout:

/**
 * View product gallery action
 */
public function galleryAction()
{

    ...

    $this->loadLayout();
    $this->renderLayout();
}

A Magento Layout is an object that contains a nested/tree collection of "Block" objects. A Block object interact with Models to get data, and use .phtml files for output, so it's like a mini controller.

Layout XML file example:

<catalog_category_default>
    <reference name="left">
        <block type="catalog/navigation" name="catalog.leftnav" after="currency" template="catalog/navigation/left.phtml">
            <block type="core/template" name="foobar" template="foo/baz/bar.phtml"/>
        </block>
    </reference>
</catalog_category_default>

this works on catalog Module, category Controller and the default Action, add a block into the left structure block

layout handles

in the XML above, catalog_category_default is a layout handle, Magento will merge various layout handles on each page load, usually along the lines of:

`default`
`STORE_{$theme}`
`THEME_frontend_{$interface}_{$theme}`
`{$module}_{$controller}_{$action}`

the default handle is used on every page

When Magento reders a page, it loads all the layout XML files it can find following the previously discussed inclusion hierarchy, and looks for the above handles in turn.

Sample layout file:

<layout>
    <default>
        <block name="wrapper" type="text/list">
            <block name="header" type="core/template" template="page/html/header.phtml" >
                <block name="navigation" template="page/html/navigation.phtml" />
            </block>
            <block name="footer" type="core/template" template="page/html/footer.phtml"/>
        </block>
    </default>
    <customer_account_register>
        <reference name="wrapper">
            <block type="core/template" name="customer_hello" template="customer/helloworld.phtml" after="header" />
            <remove name="footer" />
        </reference>
    </customer_account_register>
</layout>

Tips:

1. update the `default` layout by a specific handle `customer_account_register`;
2. use `after` or `before` attribute to define the order of blocks;
3. use `<remove>` tag to remove a block;

layout best practices

1. create a new `<package>/default` folder, copy any file you need to change over there;
2. add your layout updates in `local.xml` of your theme layout folder;

example

an example layout update file, including examples for adding, removing, reordering blocks and adding css file

<?xml version="1.0"?>
<layout>
    <default>
        <!-- remove the 'back to school' banner -->
        <remove name="right.permanent.callout" />  

        <!-- reorder by removing and adding back with the before attribute -->
        <reference name="left"> 
            <remove name="left.newsletter" />
            <block type="newsletter/subscribe" template="newsletter/subscribe.phtml" before="left.permanent.callout" />
        </reference>

        <!-- add a new block -->
        <reference name="right">
            <block type="core/template" template="sponsor/mcdonalds.phtml" />
        </reference>

        <!-- add a new css file -->
        <reference name="head">
            <action method="addItem">
                <type>skin_css</type>
                <name>css/updates.css</name>
                <params />
                <if />
            </action>
        </reference>
    </default>
</layout>

Blocks

There are hundreds of different block classes in Magento (like Mage_Core_Block_Text), most of them inherit from the Mage_Core_Block_Template class. Each Mage_Core_Block_Template object has an associated phtml template file.

When a template block's toHtml method is called this phtml template will be output using PHP's built-in include statement, this means you can use all the block's methods in the template file.

$this is available in the template file, referring the block class, some standard method:

`getUrl('customer/account/login')` -> http://www.example.com/customer/account/login
`getSkinUrl('images/logo.png')` -> return file url from the `skin` folder

blocks can be nested

in parent block's template file, you can call $this->getChildHtml('foobar') to load the child block

If some blocks are not rendering, even not showing up with template hint, it may be blocked (after 1.9.2.2), go to System->Permissions->Blocks to whitelist it

Observer

settings in config.xml

<events>
    <customer_login>
        <observers>
            <unique_name>
                <type>singleton</type>
                <class>mymodule/observer</class>
                <method>iSpyWithMyLittleEye</method>
            </unique_name>
        </observers>
    </customer_login>
</events>

do somthing:

class Packagename_Mymodule_Model_Observer
{
    public function iSpyWithMyLittleEye($observer)
    {
        $data = $observer->getData();
        //code to check observer data for our user,
        //and take some action goes here
    }
}

class overrides

settings in config.xml:

<models>
    <!-- does the override for catalog/product-->
    <catalog>
        <rewrite>
            <product>Packagename_Modulename_Model_Foobazproduct</product>
        </rewrite>
    </catalog>
</models>