Magento Backend Tutorial – Using Collections

Magento provides a robust ORM for accessing data from various modules, without having to write any SQL. It also provides abstraction for the underlying RDBMS.

An example below illustrates loading of a product collection, if our task required getting names and SKUs of all simple products in the system, which are enabled, sorted by SKU:

$products = Mage::getModel('catalog/product')
  ->getCollection()
  ->addAttributeToSelect('name')
  ->addFieldToFilter('status', Mage_Catalog_Model_Product_Status::STATUS_ENABLED)
  ->addFieldToFilter('type_id', Mage_Catalog_Model_Product_Type::TYPE_SIMPLE)
  ->setOrder('sku', 'asc')
  ->load();

Magento Backend Tutorial – Rewriting Modules

Module rewriting should be the second choice, after it is determined to be the absolute necessity, and impossible to achieve with the use of observers.

Do not use local/Mage

It may be tempting to simply copy the desired files, in their entirety, from core/Mage to local/Mage, but this method violates Magento coding standards. This method technically functions due to Magento’s autoloader searching for the given classes inside the local pool, before proceeding to the core, effectively forcing the copied classes to be loaded first. This is considered to be bad practice, as it makes upgradability significantly more difficult. Even a minor version bump is meant to replace all files inside core/Mage. If any of these files are rewritten in local/Mage, then the updated files will never be executed, and outdated versions from local scope will continue to be used. Furthermore, copying of entire files, rather than rewriting specific methods, include redundant code, that is not necessarily being changed. Edits and customizations become difficult to track and maintain.

Magento Backend Tutorial – Observers

Observers are the preferred way of extending Magento’s core functionality. Observers simplify upgradability of code, as they do not overwrite any core classes. Furthermore, they reduce the possibility of conflict between modules and extensions. Multiple modules can observe the same event. To accomplish a similar task using rewrites, class chaining would be required.

The example below will illustrate observing an event which fires after a product is added to a cart.

config.xml (app/code/local/YourTheme/Example/etc/config.xml)

<?xml version="1.0"?>
<config>
  <modules>
    <YourTheme_Example>
      <version>0.0.0.1</version>
    </YourTheme_Example>
  </modules>
  <global>
    <models>
      <example>
        <class>YourTheme_Example_Model</class>
      </example>
    </models>
    <events>
      <checkout_cart_product_add_after>
        <observers>
          <YourTheme_example_observer>
            <type>singleton</type>
            <class>YourTheme_Example_Model_Observer</class>
            <method>testExample</method>
          </YourTheme_example_observer>
        </observers>
      </checkout_cart_product_add_after>
    </events>
  </global>
</config>

Magento Backend Tutorial – Module Structure

Code pools

  • community – reserved for extensions available through public marketplaces (ex. Magento Connect).
  • core – contains the base Magento application, separated into two namespaces: Mage & Enterprise. Files in this pool should never be edited directly.
  • local – customizations, overrides, etc. should all be placed into this namespace.

Namespaces & modules

Magento Backend Tutorial – Creating Custom Front-end Routes, Controllers, Blocks and Templates

When a situation arises that requires rendering of custom information, at a custom path, such as creation of a store locator, or a lookbook, a combination of controllers, blocks, and templates should be utilized. While it can be tempting to render all information directly in the controller, Zend and Magento coding standards should be followed when it comes to designing such module.

app/code/local/YourTheme/Example/etc/config.xml

<config> 
  <modules>
    <YourTheme_Example>
      <version>0.0.0.1</version>
    </YourTheme_Example>
  </modules>
  <global>
    <blocks>
      <example>
        <class>YourTheme_Example_Block</class>
      </example>
    </blocks>
  </global>

  <frontend>
    <routers>
      <example>
        <use>standard</use>
        <args>
          <module>YourTheme_Example</module>
          <frontName>example</frontName>
        </args>
      </example>
    </routers>
    <layout>
      <updates>
        <example>
          <file>example.xml</file>
        </example>
      </updates>
    </layout>
  </frontend>
</config>

Asynchronous javascript – Callback Hell

What is “callback hell“?

Asynchronous javascript, or javascript that uses callbacks, is hard to get right intuitively. A lot of code ends up looking like this:

fs.readdir(source, function(err, files) {
  if (err) {
    console.log('Error finding files: ' + err)
  } else {
    files.forEach(function(filename, fileIndex) {
      console.log(filename)
      gm(source + filename).size(function(err, values) {
        if (err) {
          console.log('Error identifying file size: ' + err)
        } else {
          console.log(filename + ' : ' + values)
          aspect = (values.width / values.height)
          widths.forEach(function(width, widthIndex) {
            height = Math.round(width / aspect)
            console.log('resizing ' + filename + 'to ' + height + 'x' + height)
            this.resize(width, height).write(destination + 'w' + width + '_' + filename, function(err) {
              if (err) console.log('Error writing file: ' + err)
            })
          }.bind(this))
        }
      })
    })
  }
})

See all the instances of function and })? Eek! This is affectionately known as callback hell.