Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds
Getting Started with Magento Extension Development
Getting Started with Magento Extension Development

Getting Started with Magento Extension Development: This practical guide to building Magento modules from scratch takes you step-by-step through the whole process, from first principles to practical development. At the end of it you'll have acquired expertise based on thorough understanding.

eBook
$19.99 $22.99
Paperback
$38.99
Subscription
Free Trial
Renews at $19.99p/m

What do you get with Print?

Product feature icon Instant access to your digital copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Redeem a companion digital copy on all Print orders
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Table of content icon View table of contents Preview book icon Preview Book

Getting Started with Magento Extension Development

Chapter 1. An Overview of Magento Extensions

Creating Magento extensions can be an extremely challenging and time-consuming task depending on several factors such as your knowledge of Magento internals, overall development skills, and the complexity of the extension functionality itself. Having a deep insight into Magento internals, its structure, and accompanying tips and tricks will provide you with a strong foundation for clean and unobtrusive Magento extension development.

The word unobtrusive should be a constant thought throughout your entire development process. The reason is simple; given the massiveness of the Magento platform, it is way too easy to build extensions that clash with other third-party extensions. This is usually a beginner's flaw, which we will hopefully avoid once we have finished reading this book. The examples listed in this book are targeted toward Magento Community Edition 1.7.0.2. Version 1.7.0.2 is the last stable release at the time of writing.

You can download the full installation archive from the official Magento site at http://www.magentocommerce.com. You might need to register as a user on a site in order to initiate the download.

The root directory structure


Once you download the full release and set up your work environment, you should see a root Magento folder structure with the following files and folders in it:

  • Folders : app, downloader, errors, includes, js, lib, media, pkginfo, shell, skin, and var

  • Files : .htaccess, cron.sh,.htaccess.sample, LICENSE.html, mage, LICENSE.txt, favicon.ico, LICENSE_AFL.txt, get.php php.ini.sample, RELEASE_NOTES.txt, api.php, index.php, index.php.sample, cron.php, and install.php

Throughout this book we will be referencing our URL examples as if they are executing on the magento.loc domain. You are free to set your local Apache virtual host and host file to any domain you prefer, as long as you keep this in mind. If you're hearing about virtual host terminology for the first time, please refer to the Apache Virtual Host documentation at http://httpd.apache.org/docs/2.4/vhosts/.

Here is a quick summary of each of those files and folders:

  • .htaccess: This file is a directory-level configuration file supported by several web servers, most notably the Apache web server. It controls mod_rewrite for fancy URLs and sets configuration server variables (such as memory limit) and PHP maximum execution time.

  • .htaccess.sample: This is basically a .htaccess template file used for creating new stores within subfolders.

  • api.php: This is primarily used for the Magento REST API, but can be used for SOAP and XML-RPC API server functionality as well.

  • app: This is where you will find Magento core code files for the backend and for the frontend. This folder is basically the heart of the Magento platform. Later on, we will dive into this folder for more details, given that this is the folder that you as an extension developer will spend most of your time on.

  • cron.php: This file, when triggered via URL or via console PHP, will trigger certain Magento cron jobs logic.

  • cron.sh: This file is a Unix shell script version of cron.php.

  • downloader: This folder is used by the Magento Connect Manager, which is the functionality you access from the Magento administration area by navigating to System | Magento Connect | Magento Connect Manager.

  • errors: This folder is a host for a slightly separate Magento functionality, the one that jumps in with error handling when your Magento store gets an exception during code execution.

  • favicon.ico: This is your standard 16 x 16 px website icon.

  • get.php: This file hosts a feature that allows core media files to be stored and served from the database. With the Database File Storage system in place, Magento would redirect requests for media files to get.php.

  • includes: This folder is used by the Mage_Compiler extension whose functionality can be accessed via Magento administration System | Tools | Compilation. The idea behind the Magento compiler feature is that you end up with a PHP system that pulls all of its classes from one folder, thus, giving it a massive performance boost.

  • index.php: This is a main entry point to your application, the main loader file for Magento, and the file that initializes everything. Every request for every Magento page goes through this file.

  • index.php.sample: This file is just a backup copy of the index.php file.

  • js: This folder holds the core Magento JavaScript libraries, such as Prototype, scriptaculous.js, ExtJS, and a few others, some of which are from Magento itself.

  • lib: This folder holds the core Magento PHP libraries, such as 3DSecure, Google Checkout, phpseclib, Zend, and a few others, some of which are from Magento itself.

  • LICENSE*: These are the Magento licence files in various formats (LICENSE_AFL.txt, LICENSE.html, and LICENSE.txt).

  • mage: This is a Magento Connect command-line tool. It allows you to add/remove channels, install and uninstall packages (extensions), and various other package-related tasks.

  • media: This folder contains all the media files, mostly just images from various products, categories, and CMS pages.

  • php.ini.sample: This file is a sample php.ini file for PHP CGI/FastCGI installations. Sample files are not actually used by the Magento application.

  • pkginfo: This folder contains text files that largely operate as debug files to inform us about changes when extensions are upgraded in any way.

  • RELEASE_NOTES.txt: This file contains the release notes and changes for various Magento versions, starting from version 1.4.0.0 and later.

  • shell: This folder contains several PHP-based shell tools, such as compiler, indexer, and logger.

  • skin: This folder contains various CSS and JavaScript files specific for individual Magento themes. Files in this folder and its subfolder go hand in hand with files in app/design folder, as these two locations actually result in one fully featured Magento theme or package.

  • var: This folder contains sessions, logs, reports, configuration cache, lock files for application processes, and possible various other files distributed among individual subfolders. During development, you can freely select all the subfolders and delete them, as Magento will recreate all of them on the next page request. From a standpoint of a Magento extension developer, you might find yourself looking into the var/log and var/report folders every now and then.

Now that we have covered the basic root folder structure, it's time to dig deeper into the most used folder of all, the app folder, as shown in the following diagram:

app/
├── Mage.php
├── code
│   ├── community
│   │   └── Phoenix
│   │       └── Moneybookers
│   └── core
│       ├── Mage
│       └── Zend
├── design
│   ├── adminhtml
│   ├── frontend
│   │   ├── base
│   │   │   └── default
│   │   │       ├── etc
│   │   │       ├── layout
│   │   │       └── template
│   │   └── default
│   │       ├── blank
│   │       ├── default
│   │       ├── iphone
│   │       └── modern
│   └── install
├── etc
│   ├── config.xml
│   ├── local.xml.additional
│   ├── local.xml.template
│   └── modules
└── local
    └── en_US

Code pools


The folder code is a placeholder for what is called a codePool in Magento. Usually, there are three code pools in Magento, that is, three subfolders: community, core, and local.

The folder local is sometimes missing from the downloaded installation archive, as it is empty by default.

Let's take a deeper look at the community codePool for the default Magento installation as shown in the following diagram:

community/
└── Phoenix
    └── Moneybookers
        ├── Block
        │   ├── Form.php
        │   ├── ...
        │   └── Redirect.php
        ├── Helper
        │   └── Data.php
        ├── Model
        │   ├── Abstract.php
        │   ├── ...
        │   └── Wlt.php
        ├── controllers
        │   ├── MoneybookersController.php
        │   └── ProcessingController.php
        ├── etc
        │   ├── config.xml
        │   └── system.xml
        └── sql
            └── moneybookers_setup
                ├── install-1.6.0.0.php
                └── mysql4-upgrade-1.2-1.2.0.1.php

Here, the Phoenix folder is what is called the vendor namespace, and it usually matches your company identifier or something else unique to you. Within the Phoenix folder there is a Moneybookers subfolder that stands for your actual extension name.

To summarize, the formula for your extension code location should be something like app/code/community/YourNamespace/YourModuleName/ or app/code/local/YourNamespace/YourModuleName/.

There is a simple rule as to whether to choose community or local codePool:

  • Choose the community codePool for extensions that you plan to share across projects, or possibly upload to Magento Connect

  • Choose the local codePool for extensions that are specific for the project you are working on and won't be shared with the public

For example, let's imagine that our company name is Foggyline and the extension we are building is called Happy Hour . As we wish to share our extension with the community, we can put it into a folder such as app/code/community/Foggyline/HappyHour/.

All the Magento core code is also divided into extensions, and is located under the app/code/core/Mage folder. You should never place any of your code or edit any of the existing code under the app/code/core folder.

Let us get back to our example from the previous listing, the Moneybookers extension. We can see that it has several subfolders within it:

  • Block: This folder contains various PHP classes. You can think of the Block folder as a placeholder for class objects that visually manifest themselves to the user on a frontend. Most of these PHP classes extend the Mage_Core_Block_Template class from within the app/code/core/Mage/Core/Block/Template.php file. These PHP classes are then linked to various layouts and template *.phtml files within the given theme under the app/design folder.

  • controllers: This folder contains various PHP classes. You can think of controllers as a glue between our URL actions, models, blocks, and views. Most of these classes extend the Mage_Core_Controller_Front_Action class from within the app/code/core/Mage/Core/Controller/Front/Action.php file or the Mage_Adminhtml_Controller_Action class from within the app/code/core/Mage/Adminhtml/Controller/Action.php file.

  • etc: This folder contains various XML configuration files such as adminhtml.xml, api.xml, config.xml, system.xml, wsdl.xml, wsdl2.xml, and wsi.xml. Depending on what type of extension you are building, you might find some configuration files used more than the others.

  • Helper: This folder contains various PHP classes, most of which extend the Mage_Core_Helper_Abstract class from within the app/code/core/Mage/Core/Helper/Abstract.php file. The Helper classes contain various utility methods that will allow you to perform common tasks.

  • Model: This folder contains various PHP classes that usually, but not necessarily, represent an entity in a database. This is the folder where you would place most of your business logic.

  • sql: This folder contains one or more PHP files representing the installer code to be executed during the installation of the extension.

With that said, we will temporarily conclude our trip to the app/code folder structure and move on to the app/etc/modules folder.

This folder is basically a starting point for every Magento extension. The following listing shows the default content of the app/etc/modules folder for the default Magento installation, which is a collection of XML files:

  • Mage_All.xml

  • Mage_Downloadable.xml

  • Mage_Api.xml

  • Mage_ImportExport.xml

  • Mage_Api2.xml

  • Mage_Oauth.xml

  • Mage_Authorizenet.xml

  • Mage_PageCache.xml

  • Mage_Bundle.xml

  • Mage_Persistent.xml

  • Mage_Captcha.xml

  • Mage_Weee.xml

  • Mage_Centinel.xml

  • Mage_Widget.xml

  • Mage_Compiler.xml

  • Mage_XmlConnect.xml

  • Mage_Connect.xml

  • Phoenix_Moneybookers.xml

  • Mage_CurrencySymbol.xml

For example, if we were to create our Foggyline/Happy Hour extension, we would need to create a file app/etc/modules/Foggyline_HappyHour.xml as we will show later on.

Next, we move onto the app/local folder. This is where the translation files reside. If you were building an extension that would support multiple languages, for example English and German, you might want to create the following files:

  • app/locale/en_US/Foggyline_HappyHour.csv

  • app/locale/de_DE/Foggyline_HappyHour.csv

The exact filename in this case does not have to be Foggyline_HappyHour.csv; this is something that is set by you within the extension configuration.

The theme system


In order to successfully build extensions that visually manifest themselves to the user either on the backend or frontend, we need to get familiar with the theme system. The theme system is comprised of two distributed parts: one found under the app/design folder and the other under the root skin folder. Files found under the app/design folder are PHP template files and XML layout configuration files. Within the PHP template files you can find the mix of HTML, PHP, and some JavaScript.

The structure of the app/design folder is shown in the following diagram:

app/
├── design
│   ├── adminhtml
│   ├── frontend
│   │   ├── base
│   │   │   └── default
│   │   │       ├── etc
│   │   │       ├── layout
│   │   │       └── template
│   │   └── default
│   │       ├── blank
│   │       ├── default
│   │       ├── iphone
│   │       └── modern
│   └── install

There are three main subfolders here as follows:

  • adminhtml: This folder contains the XML layouts and PHTML view files used for rendering the Magento administration area, the area that the admin user sees

  • frontend: This folder contains the XML layouts and PHTML view files used for rendering the Magento frontend area, the area that the customers see.

  • install: This folder contains the XML layouts and PHTML view files used for rendering the Magento installation process

Once you step into one of them you should see a list of so called packages.

For example, stepping into the frontend shows two packages available, base and default. Drilling down into individual packages, come themes. For example, the package default has four themes in it: blank, default, iphone, and modern. By default, once you install the Magento package the initial active theme is default within the default package.

You will often hear about the frontend developer using a shorthand when talking about theme; for example, if they say default/hello, it would mean the default package with the theme named hello.

There is one important thing to know about Magento themes; they have a fallback mechanism; for example, if someone in the administration interface sets the configuration to use a theme called hello from the default package; and if the theme is missing, for example, the app/design/frontend/default/hello/template/catalog/product/view.phtml file in its structure, Magento will use app/design/frontend/default/default/template/catalog/product/view.phtml from the default theme; and if that file is missing as well, Magento will fall back to the base package for the app/design/frontend/base/default/template/catalog/product/view.phtml file.

We won't get into the details of Magento design packages and themes. There is plenty to be said about this topic that could fit into a new book. For the purpose of this book, there are a few things that you need to know as a Magento extension developer in terms of writing unobtrusive extensions.

Firstly, all your layout and view files should go under the /app/design/frontend/defaultdefault/default directory.

Secondly, you should never overwrite the existing .xml layout or template .phtml file from within the /app/design/frontend/default/default directory, rather create your own. For example, imagine you are doing some product image switcher extension, and you conclude that you need to do some modifications to the app/design/frontend/default/default/template/catalog/product/view/media.phtml file. A more valid approach would be to create a proper XML layout update file with handles rewriting the media.phtml usage to, let's say, media_product_image_switcher.phtml.

This might not make much sense for you now; but once you get your head around layout updates, the idea will be pretty clear. We will now temporarily conclude our trip to the app/design folder structure and move on to the root skin folder. The structure of the skin folder is similar to that of app/design, as shown in the following diagram:

skin/
├── adminhtml
├── frontend
│   ├── base
│   │   └── default
│   │       ├── css
│   │       ├── favicon.ico
│   │       ├── images
│   │       ├── js
│   │       └── lib
│   │           └── prototype
│   └── default
│       ├── blank
│       ├── blue
│       ├── default
│       │   ├── css
│       │   ├── favicon.ico
│       │   └── images
│       ├── french
│       ├── german
│       ├── iphone
│       └── modern
└── install

There is not much to say about the skin folder. It's a placeholder for all your theme-specific CSS, JavaScript, and image files. If you are developing community-distributed extensions, it makes sense to reduce the usage of CSS to absolute minimum, if any, in your extension. The reason is that you cannot know which theme the user will use and how your extension visual components design will impact its theme.

The same thing cannot be said for JavaScript, as you will most likely notice yourself if you keep developing a lot of community extensions. Adding your custom JavaScript code to a skin/frontend/default/default/js folder is a nice, clean, and unobtrusive way of doing it. As the skin folder has the same fallback functionality as the app/design folder, you do not need to know upfront the theme that the user will use.

Later on, as you sharpen your Magento developer skills you might ask yourself, "Why not use the root js folder to add your JavaScript code?" Technically, you could, and it would be perfectly valid; no tricks or hacks in that approach. However, the root js folder should be looked upon as a third-party JavaScript library container, while the skin/frontend/default/default/js folder should be looked upon as your custom JavaScript extension-related code container. For example, it makes no sense to place your product image switcher extension JavaScript into the root js folder if it's a JavaScript code that works only with your extension and is not intended for general re-use.

With this we can conclude the relevant Magento folder structure that you as a Magento extension developer should be familiar with. In general, these five locations should be all to build fully functional, clean, and unobtrusive extensions:

  • app/etc/modules/ – required

  • app/locale/ – optional

  • app/code/community/YourNamespace/YourModuleName/ – required

  • /app/design/frontend/default/default/ (or /app/design/adminhtml/default/default/ for backend area) – optional

  • skin/frontend/default/default/ (or skin/adminhtml/default/default/) – optional

Depending on the complexity and the functionality your extension tries to fulfill, you might end up using just a few or all of these directory locations within a single extension.

Basic extension configuration


With everything said, by now we should have a solid understanding of the Magento directory structure and be ready to grasp further concepts of the Magento internal structure.

What better way to explain things than an example; so let's start off by creating the simplest extension, which we will then extend bit by bit as we explain the Magento way of doing the object-oriented Model-View-Controller (MVC) architecture. If you are hearing of MVC for the first time, please take some time to familiarize yourself with the concept. You can find good starting material about it at http://en.wikipedia.org/wiki/Model–view–controller.

Previously, we gave examples on the extension name Foggyline_HappyHour, so let's start with that.

In Magento, everything starts with a configuration file; after all, Magento is what we call the configuration-based MVC system. In a configuration-based system, in addition to adding the new files and classes, you often need to explicitly tell the system about them.

The first file we will create is app/etc/modules/Foggyline_HappyHour.xml with the following content:

<?xml version="1.0"?>
<config>
   <modules>
      <Foggyline_HappyHour>
         <active>true</active>
            <codePool>community</codePool>
      </Foggyline_HappyHour>
   </modules>
</config>

With this file in place, Magento already becomes aware of your extension. You can confirm that by going under the Magento administration under System | Configuration | Advanced | Advanced | Disable Modules Output. Once you're there, you should see your Foggyline_HappyHour extension on the list. It is important to know that setting the Disable Modules Output value to Disabled and saving the configuration has absolutely no impact on your extension being truly enabled or disabled.

Disabling the extension output is not the same as disabling the extension itself. Disabling the output of the extension via this configuration option has an effect only on your extension block classes that represent the visually output part of your extension. To truly disable the extension, one must edit the app/etc/modules/Foggyline_HappyHour.xml file and change <active>true</active> to <active>false</active>.

Now that Magento sees our extension, we move on to the next step: creating the app/code/community/Foggyline/HappyHour/etc/config.xml file. This config.xml file is what is usually referred to as the extension configuration file by developers. The following code is the basic definition of our app/code/community/Foggyline/HappyHour/etc/config.xml file:

<?xml version="1.0"?>
<config>
   <modules>
      <Foggyline_HappyHour>
         <version>1.0.0.0</version>
      </Foggyline_HappyHour>
   </modules>
</config>

Controllers


By itself, the content of the app/code/community/Foggyline/HappyHour/etc/config.xml file won't have any additional effect on Magento, so let's move on to extending our extension. First, we will create a controller in order to output Hello World to the browser. To do this, we need to add the routers definition in frontend to our config.xml file.

<?xml version="1.0"?>"?>
<config>
<!-- … other elements ... -->
   <frontend>
      <routers>
         <foggyline_happyhour>
            <use>standard</use>
               <args>
                  <module>Foggyline_HappyHour</module>
                  <frontName>happyhour</frontName>
               </args>
         </foggyline_happyhour>
      </routers>
   </frontend>
<!-- … other elements ... -->
</config>

The frontend tag refers to a Magento area. Magento has three distinctive areas: frontend, admin, and install. The frontend area is what your customers see, the public facing shopping cart. The admin area is what your Magento admin users see, the administrative interface. The install area is what you see the very first time you install Magento, the installation process.

The routers tag encloses the configuration information about routers.

The frontName tag is sort of an alias for the desired route we want Magento to react to.

When a router parses a URL, it gets separated as follows: http://example.com/frontName/actionControllerName/actionMethod/. By defining a value of happyhour in the <frontName> tag, we're telling Magento that we want the system to respond to URLs in the form of http://example.com/happyhour/*.

It's important to understand that frontName and the Front Controller object are not the same thing.

The foggyline_happyhour tag should be the lowercase version of your extension name. Our extension name is Foggyline_HappyHour; this tag is foggyline_happyhour.

The extension tag should be the full name of your extension, including its namespace/extensionname name. This will be used by the system to locate your controller files.

Now we need to create a controller file. The module controller files are stored under the controllers subfolder. So let's create an app/code/community/Foggyline/HappyHour/controllers/HelloController.php class file with the following content:

<?php

class Foggyline_HappyHour_HelloController extends Mage_Core_Controller_Front_Action
{
   public function helloWorldAction()
   {
      echo 'Hello World #1.';
   }
}

Once you are done, you can try opening the following URL in the browser: http://magento.loc/index.php/happyhour/hello/helloWorld. You should be able to see the Hello World #1. message. The URL path is constructed from your config.xml router frontName, the controller name itself, and the controller action name. There are two main types of controllers in Magento:

  • frontend: This contains all the controller classes that extend (derive from) the Mage_Core_Controller_Front_Action class

  • backend / admin: This contains all the controller classes that extend (derive from) the Mage_Adminhtml_Controller_Action class

URLs for admin controller actions can only be accessed if you are logged in to the Magento administration interface.

Blocks


Looking within the helloWorldAction() method, you can see a call towards the createBlock() method with the string 'core/text' as a parameter. Where does 'core/text' come from and what does it mean? In order to understand that, we will further extend our config.xml file by adding a blocks element to it as follows:

<?xml version="1.0"?>
<config>
<!-- … other elements ... -->
   <global>
      <blocks>
         <foggyline_happyhour>
            <class>Foggyline_HappyHour_Block</class>
         </foggyline_happyhour>
      </blocks>
   </global>
<!-- … other elements ... -->
</config>

The element foggyline_happyhour is known as the class group. The element foggyline_happyhour is a class group name and its inner class element is basically a shortcut for your extensions Block type PHP classes. For example, the following is a modified code for our helloWorldAction() method shown previously:

<?php

class Foggyline_HappyHour_HelloController extends Mage_Core_Controller_Front_Action
{
   public function helloWorldAction()
   {
      $this->loadLayout();
      
      $block = $this->getLayout()->createBlock('foggyline_happyhour/hello');
      $block->setText('Hello World #2.');
      
      $this->getLayout()->getBlock('content')->insert($block);
      
      $this->renderLayout();
   }
}

You can see that we are no longer calling the createBlock() method with 'core/text' but with the 'foggyline_happyhour/hello' parameter. This is like telling Magento to load the hello class (the Hello.php file) that can be found under the classpath mapped by the foggyline_happyhour class group. As the foggyline_happyhour class group has its class value set to Foggyline_HappyHour_Block, Magento expects to find the app/code/community/Foggyline/HappyHour/Block/Hello.php file.

How and why exactly does Magento expect the Hello.php file to be at a certain location? The answer to this lies in a robust autoloading functionality of the Magento system based on a configuration and file naming convention. You can split all Magento classes into four parts that we'll call the vendor namespace, extension name, class group, and filename itself.

The vendor namespace helps us prevent name collisions between extensions, letting you know which extension is the owner of the class. For example, all core Magento extensions use the mage namespace.

The module name plays a crucial part in the autoloading system. All the proper customization of Magento is done through individual extensions.

The class group is a sort of alias defined within the extension's configuration file, an alias towards a class folder within the extension directory. There are several main types of class groups, such as the one for Model, Block, Helper.

Finally, the name of the file itself. Each class should have a unique name within a class group that describes its intended use or function.

Magento's autoloading functionality uses these parts to determine where to find the source for a given class as shown in the following example: VendorNamespace/ModuleName/ClassGroup/FileName.php.

Go ahead and create the file Hello.php with the following content:

<?php

class Foggyline_HappyHour_Block_Hello extends Mage_Core_Block_Text
{
 
}

Now that you have modified helloWorldAction() and created the Hello.php class file, go ahead and open the http://magento.loc/index.php/happyhour/hello/helloWorld URL in the browser. The result should be the same as in the previous case; you should be able to see the fully loaded Magento page with the Hello World #2. message shown under the content area.

Our Hello block class extends Mage_Core_Block_Text. However, chances are that most of the time you will be extending the Mage_Core_Block_Template class, where your Hello block class might look like something as follows:

<?php

class Foggyline_HappyHour_Block_Hello extends Mage_Core_Block_Template
{
   public function __construct()
   {
      parent::__construct();
      $this->setTemplate('foggyline_happyhour/hello.phtml');
   }
}

The difference between extending Mage_Core_Block_Text or Mage_Core_Block_Template is that the latter requires you to define a view *.phtml file under the theme folder, and thus, is more designer friendly. In order for it to successfully work, you need to create the app/design/frontend/default/default/template/foggyline_happyhour/hello.phtml or app/design/frontend/base/default/template/foggyline_happyhour/hello.phtml file. You might find the latter to be a safer location for your view files, as it is not dependent on your customer theme and package settings. Thus, the Magento theme fallback mechanism will always pick it up. Now if you put your Hello World #3. string within the hello.phtml file and then re-open the http://magento.loc/index.php/happyhour/hello/helloWorld URI in your browser, you should again see the fully loaded Magento page with the Hello World #3. message shown under the content area. Our goal here is to give you the basics of functional extension, so we will now leave the controllers and blocks behind and move to the model.

Tip

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

The model, resource, and collection


A model represents the data for the better part, and to a certain extent a business logic of your application. Models in Magento take the Object Relational Mapping (ORM) approach, thus having the developer to strictly deal with objects while their data is then automatically persisted to the database. If you are hearing about ORM for the first time, please take some time to familiarize yourself with the concept; you can find good starting material about it at http://en.wikipedia.org/wiki/Object-relational_mapping. Theoretically, you could write and execute raw SQL queries in Magento. However, doing so is not advised, especially if you plan on distributing your extensions.

There are two types of models in Magento:

  • Basic Data Model: This is a simpler model type, sort of like an Active Record pattern-based model. If you're hearing about Active Record for the first time, please take some time to familiarize yourself with the concept; you can find good starting material about it at https://en.wikipedia.org/wiki/Active_record_pattern.

  • EAV (Entity-Attribute-Value) Data Model: This is a complex model type, which enables you to dynamically create new attributes on an entity. As EAV Data Model is significantly more complex than Basic Data Model and Basic Data Model will suffice for most of the time, we will focus on Basic Data Model and everything important surrounding it. Each data model you plan to persist to the database, that means models that present an entity, needs to have four files in order for it to work fully:

    • The model file: This extends the Mage_Core_Model_Abstract class. This represents single entity, its properties (fields), and possible business logic within it.

    • The model resource file: This extends the Mage_Core_Model_Resource_Db_Abstract class. This is your connection to the database; think of it as the thing that saves your entity properties (fields) database.

    • The model collection file: This extends the Mage_Core_Model_Resource_Db_Collection_Abstract class. This is your collection of several entities, a collection that can be filtered, sorted, and manipulated.

    • The installation script file: In its simplest definition this is the PHP file through which you, in an object-oriented way, create your database table(s).

    For our example, we will go ahead and create our extensions User model. The first thing we need to do is to set up its configuration within the config.xml file as follows:

    <?xml version="1.0"?>
    <config>
       <global>
    <!-- … other elements ... -->
          <models>
             <foggyline_happyhour>
                <class>Foggyline_HappyHour_Model</class>
                <resourceModel>foggyline_happyhour_resource</resourceModel>
             </foggyline_happyhour>
             <foggyline_happyhour_resource>
                <class>Foggyline_HappyHour_Model_Resource</class>
                <entities>
                   <user>
                      <table>foggyline_happyhour_user</table>
                   </user>
                </entities>
             </foggyline_happyhour_resource>
          </models>
          <resources>
             <foggyline_happyhour_setup>
                <setup>
                   <model>Foggyline_HappyHour</model>
                </setup>
             </foggyline_happyhour_setup>
          </resources>
    <!-- … other elements ... -->
       </global>
    </config>

    The amount of new elements added to XML might look a bit discouraging, try not to get frightened by it. Let's break it down:

  • The element foggyline_happyhour contains our class group model definition, which actually tells Magento that our Model PHP class files can be found under our extensions directory app/code/community/Foggyline/HappyHour/Model/. Further, the foggyline_happyhour element contains the resourceModel element whose value points further to the element foggyline_happyhour_resource.

  • The element foggyline_happyhour_resource contains our class group model resource definition, which actually tells Magento that our Model Resource PHP class files can be found under our extensions directory app/code/community/Foggyline/HappyHour/Model/Resource/. Further, the foggyline_happyhour_resource element contains the entities element that is a list of all our entities and their mapped database table names.

  • The element foggyline_happyhour_setup contains the setup definition for our extension. There is a lot more you can define here, which is not visible in our example due to simplicity. For example, we could have defined completely different read / write database connections here, specific to our extension. The most important thing to keep in mind here, however, is the following: the element name foggyline_happyhour_setup must match the folder name for your installation script app/code/community/Foggyline/HappyHour/sql/foggyline_happyhour_setup/.

Now let us create the four files required for our extensions model entity to work fully.

First we will create a model file app/code/community/Foggyline/HappyHour/Model/User.php with the following content:

<?php

class Foggyline_HappyHour_Model_User extends Mage_Core_Model_Abstract
{
   protected $_eventPrefix      = 'foggyline_happyhour_user';
   protected $_eventObject      = 'user';
   
   protected function _construct()
   {
      $this->_init('foggyline_happyhour/user');
   }
}

All basic data models, such as our Foggyline_HappyHour_Model_User, should extend the Mage_Core_Model_Abstract class. This abstract class forces you to implement a single method named _construct. Please note that this is not PHP's constructor __construct.

The _construct method should call the extending class' _init method with the same identifying URI you will be using in the Mage::getModel method call. Also, note the class-protected properties $_eventPrefix and $_eventObject . It is highly recommended, although not required, for you to define these properties. Values of both the properties can be freely assigned; however, you should follow your extension-naming scheme here as shown earlier.

Once we get to the Magento event/observer system later in the chapters, the meaning of these properties and how they make your code extendible by third-party developers will become more clear.

Every model has its own resource class. When a model in Magento needs to talk to the database, Magento will make the following method call to get the model resource Mage::getResourceModel('class_group/modelname');. Without resource classes, models would not be able to write to the database. Having that in mind, we create the model resource file app/code/community/Foggyline/HappyHour/Model/Resource/User.php with the following content:

<?php

class Foggyline_HappyHour_Model_Resource_User extends Mage_Core_Model_Resource_Db_Abstract
{
   protected function _construct()
   {
      $this->_init('foggyline_happyhour/user', 'user_id');
   }
}

Again, we have the same pattern: the construct method should call the extending class' init method with the same identifying URI, with a slight exception of the existing second parameter in this case, which matches the primary key column name in the database. So in this case, the string user_id matches the primary key column name in the database.

Finally, we address the model collection file. As Magento does not like juggling its model objects through plain PHP arrays, it defines a unique collection object associated with each model. Collection objects implement the PHP IteratorAggregate and Countable interfaces, which means they can be passed to the count function and used for each constructs.

We create the model collection file app/code/community/Foggyline/HappyHour/Model/Resource/User/Collection.php with the following content:

<?php

class Foggyline_HappyHour_Model_Resource_User_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract
{
   public function _construct()
   {
      $this->_init('foggyline_happyhour/user');
   }
}

Just as we did with our other classes we define the construct method, which calls the extending class' init method with the same identifying URI.

Finally, we create an installation script file app/code/community/Foggyline/HappyHour/sql/foggyline_happyhour_setup/install-1.0.0.0.php with the following content:

<?php

/* @var $installer Mage_Core_Model_Resource_Setup */
$installer = $this;
$installer->startSetup();

$table = $installer->getConnection()
   ->newTable($installer->getTable('foggyline_happyhour/user'))
   ->addColumn('user_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
      'identity'  => true,
      'unsigned'  => true,
      'nullable'  => false,
      'primary'   => true,
      ), 'Id')
   ->addColumn('firstname', Varien_Db_Ddl_Table::TYPE_VARCHAR, null, array(
      'nullable'  => false,
      ), 'User first name')
   ->addColumn('lastname', Varien_Db_Ddl_Table::TYPE_VARCHAR, null, array(
      'nullable'  => false,
      ), 'User last name')
   ->setComment('Foggyline_HappyHour User Entity');
$installer->getConnection()->createTable($table);

$installer->endSetup();

There is one thing we need to pay special attention to here, the naming of the install-1.0.0.0.php file. The number 1.0.0.0 must be equal to the numbers placed under the version element value, or else Magento won't trigger your installation script.

Ever since version 1.6, Magento (in theory) supports more database backends than only MySQL. Thus, technically, the meaning of code within this install script may vary from database to database depending on the implementation.

Given that MySQL is still the default and far more dominant database backend for Magento, it is worth noting what actually goes on behind this installation script. It starts by calling $installer->startSetup(), which internally sets SQL_MODE to NO_AUTO_VALUE_ON_ZERO, and FOREIGN_KEY_CHECKS to 0. The call to $installer->startSetup(), on the other hand restores the two mentioned values to their previous states. The rest of the code that lies in between is responsible for the actual table definition and creation.

In our preceding example, we defined a table that will be named foggyline_happyhour_user, and three columns named user_id, firstname, and lastname.

These four files conclude our requirement for a fully persistent entity model. In order to check if everything is functioning, load any Magento URL in the browser and then take a look at the database. If the extension is installed correctly, there should be two changes to the database:

  • The table core_resource should contain an entry with the column code value foggyline_happyhour_setup and column version value 1.0.0.0.

  • The table foggyline_happyhour_user should have been successfully created in the database with all the columns as defined within the install-1.0.0.0.php file.

Note, if you experience issues with your installation script during their execution, such as breaking up due to invalid instructions, be sure to remove the core_resource table entry that your extension might have created. After that, simply open the browser and reload any web page from your shop; this will trigger the installation process again.

Now that we have successfully created single entity (User) model file, we need to make sure it's working. We can do so by going back to our Foggyline_HappyHour_HelloController class and adding the following action to it:

<?php

class Foggyline_HappyHour_HelloController extends Mage_Core_Controller_Front_Action
{
  /* … other code … */  
   public function testUserSaveAction()
   {
      $user = Mage::getModel('foggyline_happyhour/user');
      
      $user->setFirstname('John');
       /* or: $user->setData('firstname', 'John'); */
      
      $user->setLastname('Doe');
      /* or: $user->setDatata('lastname', 'Doe'); */
      
      try {
         $user->save();
         echo 'Successfully saved user.';
      } catch (Exception $e) {
         echo $e->getMessage();
         Mage::logException($e);
/* oror: Mage::log($e->getTraceAsString(), null, 'exception.log', true); */
      }
   }
  /* … other code … */  
}

Models in Magento get called (instantiated) all across the code. Instantiating the model class is done by the statement $model = Mage::getModel('classGroup/modelClassName); which can be seen in the preceding code.

What confuses most of the Magento newcomers is the fact that our model class Foggyline_HappyHour_Model_User has absolutely no methods defined other than _construct(), which is not the default PHP construct (__construct()).

So how is it then that the statements such as $user->setLastname('Doe'); work? The answer lies in the derived from the Varien_Object class found in the lib/Varien/Object.php file. One of the things Varien_Object provides is Magento's famous getter and setter methods. If you study the class code, you will see that Magento actually uses the class protected $_data property internally via the help of PHP magic methods. Executing $user->setLastname('Doe'); actually sets $_data['username'] = 'Doe';. Or to put it differently, it would virtually create a property named 'úsername' with the value 'Doe' on a $user object instance.

The same logic goes for setting values. Executing a statement such as $user->setData('firstname', 'John'); does almost the same as the previous example.

The difference between the two is that setData() directly changes the value on the protected $_data['username'] property, while setLastname('Doe'); will first try to look for the setLastname() method within the Foggyline_HappyHour_Model_User class. If the method is found, the value is passed to the method and the method is in charge of passing the value to the protected $_data['username'] property, possibly doing some modifications on it.

You should take some time to study the inner workings of the Varien_Object class, as it is the base class for all of your models.

To continue with our preceding example, if you now try to open the URL http://magento1702ce.loc/index.php/happyhour/hello/testUserSave in your browser, you should be able to see the Successfully saved user message.

Once you confirm that the entity save action is working, you should test and confirm that the model collection is working too. Create a new action under the Foggyline_HappyHour_HelloController class as follows:

<?php

class Foggyline_HappyHour_HelloController extends Mage_Core_Controller_Front_Action
{
   /* … other code … */  
   public function testUserCollectionAction()
   {
      $users = Mage::getModel('foggyline_happyhour/user')
          ->getCollection();
      
      foreach ($users as $user) {
         $firstname = $user->getFirstname();
         /* or: $user->getData('firstname') */
         
         $lastname = $user->getLastname();
         /* or: $user->getData('lastname') */
         
         echo "$firstname $lastname<br />";
      }
   }
   /* … other code … */
}

If you now try to open the URL http://magento.loc/index.php/happyhour/hello/testUserCollection in your browser, you should be able to see the list of your users within the foggyline_happyhour_user database table.

If you were able to follow up and all went well, you should now have a fully working model entity. There is a lot more to be said about models; however, this is enough to get you started.

The event/observer pattern


Next, we will move on to an event/observer pattern implemented by Magento. Events and observers are extremely important in Magento because they enable you to easily hook onto various parts of Magento and add your own pieces of code to it. In certain situations they are slightly underestimated by extension developers, either due to their knowledge of the platform, or due to the lack of grand vision or forced quick and dirty implementations.

Events and observers are the key to writing unobtrusive code in cases where you need to change or add to the default Magento behavior. For example, if you need to send all your newly created orders to an external fulfillment system, most of the time you simply need to observe a proper event and implement your business logic within an observer.

There are several types of events getting fired in Magento depending on how you differentiate them. For example, we could divide them into static and dynamic events.

Static events are all those events defined through code with full event names such as Mage::dispatchEvent('admin_session_user_login_failed', array('user_name' => $username, 'exception' => $e));, Mage::dispatchEvent('cms_page_prepare_save', array('page' => $model, 'request' => $this->getRequest()));, Mage::dispatchEvent('catalog_product_get_final_price', array('product' => $product, 'qty' => $qty));, Mage::dispatchEvent('catalog_product_flat_prepare_columns', array('columns' => $columnsObject));, and Mage::dispatchEvent('catalog_prepare_price_select', $eventArgs);.

Dynamic events are all those events defined through code dynamically at runtime such as Mage::dispatchEvent($this->_eventPrefix.'_load_before', $params);, Mage::dispatchEvent($this->_eventPrefix.'_load_after', $this->_getEventData());, Mage::dispatchEvent($this->_eventPrefix.'_save_before', $this->_getEventData());, Mage::dispatchEvent($this->_eventPrefix.'_save_after', $this->_getEventData());, and Mage::dispatchEvent('controller_action_layout_render_before_'.$this->getFullActionName());.

Both types of events are absolutely the same; they function the same, and the preceding differentiation is simply a matter of terminology. We are calling the other ones dynamic because their full name is not known until the runtime.

For example, each time you wish to intercept certain parameters passed to a controller action, you could simply create an event observer that would observe the controller_action_predispatch_* event, which is triggered within the Mage_Core_Controller_Varien_Action class file as follows: Mage::dispatchEvent('controller_action_predispatch_' . $this->getFullActionName(), array('controller_action' => $this));.

Now, let us see how exactly do we define the event observer and place some of our code to be executed upon certain events. First, we need to create an entry within our extensions config.xml file.

Let's say we want to introspect all the parameters passed to the controller action during the customer registration process. When a customer fills in the required registration fields and clicks on Submit, the form posts the data to the http://{{shop.domain}}/index.php/customer/account/createpost/ URL.

If you look at the previously mentioned the controller_action_predispatch_* event, the expression $this->getFullActionName() would return the customer_account_createpost string. You can find that out easily by placing the var_dump($this->getFullActionName()); exit; expression right there under the Mage::dispatchEvent('controller_action_predispatch_... expression. Please note that we are using var_dump here just for the simplicity of demonstration. So now that we know this, we can safely conclude that the full event name we need to observe in this case is controller_action_predispatch_customer_account_createpost.

Now we know that the event name is a requirement upon which we create a proper config.xml entry for defining our event observer as shown in the following code:

<?xml version="1.0"?>
<config>
   <!-- … other elements ... -->
   <frontend>
      <events>
         <controller_action_predispatch_customer_account_createpost>
            <observers>
              <foggyline_happyhour_intercept>
                 <class>foggyline_happyhour/observerobserver</class>
                 <method>intercept</method>
                 </foggyline_happyhour_intercept>
              </observers>
         </controller_action_predispatch_customer_account_createpost>
      </events>
   </frontend>
   <!-- … other elements ... -->
</config>

Within the observer's element comes the definition of our observer, which we call foggyline_happyhour_intercept in this case. Each observer needs two properties-defined classes, which in this case points to the foggyline_happyhour class group and Observer class file thus, the string foggyline_happyhour/observer; the other one is the method within the Observer class file.

Next, we create the actual Observer class file app/code/community/Foggyline/HappyHour/Model/Observer.php with the following content:

<?php

class Foggyline_HappyHour_Model_Observer
{
   public function intercept($observer = null)
   {
      $event = $observer->getEvent();
      $controllerAction = $event->getControllerAction();
      $params = $controllerAction->getRequest()->getParams();
      
      Mage::log($params);
   }
}

A quick look at Foggyline_HappyHour_Model_Observer reveals one important thing: unlike Model, Block, and Controller classes, the Observer classes do not need to extend anything.

If you now go to your browser and try to create a new customer account, you will get your var/log/system.log file filled with the HTTP POST parameters provided by the customer during the registration process. You might need to refresh/re-open system.log in your editor in order to pick up the changes, in case you don't see the log entries.

Sometimes, the right event might not be there; so you might need to look for the second best. For example, if we did not have the controller_action_predispatch_customer_account_createpost event dispatched, the next best event would probably be the following one: Mage::dispatchEvent('controller_action_predispatch', array('controller_action' => $this));.

However, the event controller_action_predispatch is pretty generic, which means it will get triggered for every controller action predispatch. In this case, you would have to do a little if/else logic within your event observer code. Just as we have controller fired events, we also have model-fired events. If you open a class file like Mage_Catalog_Model_Product, you can see property definitions like protected $_eventPrefix = 'catalog_product'; and protected $_eventObject = 'product';.

Now, if you trace the code a little bit down to the Mage_Core_Model_Abstract class file, you will see that the properties $_eventPrefix and $_eventObject are used for dynamic events such as (along with the static events for the same action) Mage::dispatchEvent($this->_eventPrefix.'_load_before', $params);, Mage::dispatchEvent($this->_eventPrefix.'_load_after', $this->_getEventData());, Mage::dispatchEvent($this->_eventPrefix.'_save_commit_after', $this->_getEventData());, Mage::dispatchEvent($this->_eventPrefix.'_save_before', $this->_getEventData());, Mage::dispatchEvent($this->_eventPrefix.'_save_after', $this->_getEventData());, Mage::dispatchEvent($this->_eventPrefix.'_delete_before', $this->_getEventData());, Mage::dispatchEvent($this->_eventPrefix.'_delete_after', $this->_getEventData());, Mage::dispatchEvent($this->_eventPrefix.'_delete_commit_after', $this->_getEventData());, and Mage::dispatchEvent($this->_eventPrefix.'_clear', $this->_getEventData());.

Knowing this is extremely important, as it enables you to create all sorts of event observers for specific models and their actions, for example customer, order, and invoice entity create/update/delete actions. This is why defining the $_eventPrefix and $_eventObject properties on your custom model classes is something you should adopt as a sign of good coding practice. Doing so enables other third-party developers to easily hook onto your extension code via the observer in a clean and unobtrusive way.

Cron jobs


Cron jobs are used to schedule commands to be executed periodically by the cron. Cron is a a time-based job scheduler software in Unix-like computer operating systems. It is driven by a crontab (cron table) file, a configuration file that specifies shell commands to run periodically on a given schedule.

Magento cron jobs is a different kind of functionality than that just mentioned. It merely relies on the system cron software to trigger the root Magento cron.php or cron.sh files periodically. Keep that in mind while talking about cron and Magento cron jobs.

It is highly important that the system administrator has the system cron set to trigger the Magento cron.sh file at regular intervals, at least every five minutes. This can be done by adding the following line to system cron:*/5 * * * * /path/to /magento/root/folder/cron.sh. This way, you as a Magento extension developer have the ability to create a new Magento cron jobs definitions through your extensions configuration files and rest assured they will get executed. Magento cron jobs are defined in the config.xml file as follows:

<?xml version="1.0"?>
<config>
<!-- … other elements ... -->
   <crontab>
      <jobs>
         <foggyline_happyhour_ordersToFulfilment>
            <schedule>
               <cron_expr>*/2 * * * *</cron_expr>
            </schedule>
            <run>
               <model>foggyline_happyhour/service::ordersToFulfilment</model>
            </run>
         </foggyline_happyhour_ordersToFulfilment>
      </jobs>
   </crontab>   
<!-- … other elements ... -->
</config>

In this example, our cron job has been defined with the name foggyline_happyhour_ordersToFulfilment and set to execute every two minutes, which can be seen by the schedule >cron_expr value. For more details on writing cron expressions, check out the following URL: http://en.wikipedia.org/wiki/Cron.

The model element, which in this case is set to foggyline_happyhour/service::ordersToFulfilment, means that the following code from the app/code/community/Foggyline/HappyHour/Model/Service.php file would get executed by this cron job:

<?php

class Foggyline_HappyHour_Model_Service
{
   public function ping() 
   {
      Mage::log('ping');
   }
}

The same as with observer classes, cron-defined model classes do not need to extend anything.

Even though this Magento cron job has been set to run every two minutes, you as an extension developer have no guarantee that the system cron will ever be run on a third-party website, or if it's run it might not be run in small enough intervals. Thus, you might end up with a code logic that never gets executed, or gets executed in larger than planned intervals.

During development, however, you do not need to have the system cron set up. It is sufficient to just execute the http://magento.loc/cron.php URL in the browser, as this will trigger the Magento cron system.

For example, if you were writing an extension that has one or more Magento cron jobs defined in config.xml, the easiest way to check if you correctly defined your cron job would be to truncate the cron_schedule database table and then trigger the http://magento.loc/cron.php URL in the browser. Obviously, in this case, later on you would have to periodically trigger the http://magento.loc/cron.php URL to check if your cron job execution went through, monitoring the executed_at column within the cron_schedule database table.

Helpers


There is one part of Magento functionality that gets equally used across all the individual functionality mentioned so far, and that's helpers. Magento helper is a class that usually extends the Mage_Core_Helper_Data class directly found in the app/code/core/Mage/Core/Helper/Data.php file or at the very least derived from the Mage_Core_Helper_Abstract class found under the app/code/core/Mage/Core/Helper/Abstract.php file.

The Helper classes contain various utility methods that will allow you to perform common tasks on different objects and variables. Helpers too are defined via the config.xml elements as follows:

<?xml version="1.0"?>
<config>
   <!-- … other elements ... -->
   <global>
      <helpers>
         <foggyline_happyhour>
            <class>Foggyline_HappyHour_Helper</class>
         </foggyline_happyhour>
      </helpers>
   </global>
   <!-- … other elements ... -->
</config>

Similar to blocks and models, helpers have a class element defined to point to their folder locations within an extension. In this example, a helper is defined with the name foggyline_happyhour.

As you are allowed to have multiple helpers under the app/code/community/Foggyline/HappyHour/Helper/ folder, it is important to know that the default helper PHP filename is Data.php.

What this really means is that when you execute a statement such as Mage::helper('foggyline_happyhour');, Magento will load the Data.php helper. If, however, you execute a statement such as Mage::helper('foggyline_happyhour/image');, Magento will load the Image.php helper (the app/code/community/Foggyline/HappyHour/Helper/Image.php file).

System configuration options


Besides being a utility method container, the Helper classes play an indispensable role for extensions that provide Magento-style configuration options for users. For example, if you were building a payment extension, you would most certainly need a configuration area in order to set up the access data for it. Magento comes with its own built-in configuration area, located under System | Configuration. This entire section is built from the XML elements found under the extension etc/system.xml.

Here is where things get a little complicated. In order for the Magento admin user to have access to your extension configuration interface defined through system.xml, it needs permissions for that. These permissions are defined in another configuration file located in the same folder called adminhtml.xml.

Let us demonstrate this with a simple example. We will create a configuration options section for our extension within system.xml, define permissions to it via adminhtml.xml, and then use the data helper class to fetch that configuration value from within our controller.

First, we need to create the app/code/community/Foggyline/HappyHour/etc/system.xml configuration file with the following content:

<?xml version="1.0"?>
<config>
   <tabs>
      <foggyline module="foggyline_happyhour">">
         <label>Foggyline</label>
        <sort_order>10</sort_order>
      </foggyline>
   </tabs>
   <sections>
      <foggyline_happyhour module="foggyline_happyhour">
         <label>FoggylineHappyHour</label>
         <tab>foggyline</tab>
         <sort_order>10</sort_order>
         <show_in_default>1</show_in_default>
         <groups>
            <settings>
               <label>FoggylineHappyHour Settings</label>
               <sort_order>10</sort_order>
               <show_in_default>1</show_in_default>
               <fields>
                  <custom_message>
                     <label>Custom Message</label>
                     <frontend_type>text</frontend_type>
                     <sort_order>20</sort_order>
                     <show_in_default>1</show_in_default>
                  </custom_message>
               </fields>
            </settings>
         </groups>
      </foggyline_happyhour>
   </sections>
</config>

The first thing that we did was add a custom tab called Foggyline to the system configuration. Tabs are the navigation headers down the left-hand side of the Magento administration are a under System | Configuration. The default tabs are General, Catalog, Customers, Sales, Services, and Advanced. Adding a new tab is as simple as defining your own element under Configuration | Tabs. In our example, we have defined the foggyline element, where foggyline is a freely given element name. The attribute module="foggyline_happyhour" simply tells Magento what helper to use for this part of functionality while referencing helpers internally. The string foggyline_happyhour points to the helper group defined under config.xml. The label element specifies the label for this tab to be shown under the navigation sidebar. The sort_order element specifies the order in the sidebar with regards to other elements; a larger number pushes the item in the sidebar to the bottom after other elements.

Once we have defined the actual tab, we need to add one or more sections to it. In our example, we have defined one section through the foggyline_happyhour element.

The foggyline_happyhour element is an arbitrary name that's used to identify your new section.

The label element defines the display value used in the HTML interface for your new section.

The tab element identifies which tab your new section should be grouped under. We want our section to show up under our new Foggyline tab. The name foggyline comes from the tag used to create the Foggyline tab.

The sort_order element determines where this section shows up vertically compared to other sections in the tab.

The show_in_default element is a Boolean configuration option with a valid value of 1 or 0. They determine the level of configuration scope this section has.

The groups element determines the logical grouping of configuration options, sort of like the fieldset element in HTML forms.

The settings element within groups is an arbitrary name that's used to identify this group.

The elements label, sort_order, and show_in_default are analogous to those previously explained for this section.

The fields element is a container for one or more elements that will be visually manifested into HTML form elements later. Within the fields element, again we have label, sort_order, show_in_default, and this time one new element called frontend_type. The frontend_type element determines what HTML element will be used for rendering in the browser.

At this point if you try to log in to Magento and navigate to System | Configuration, you will be able to see the FoggylineHappyHour menu in the left sidebar. However, accessing the menu item would give you a 404 Error Page not found error.

This might be a good time to explain what ACL actually is. ACL, short for Access Control Lists, is a functionality that allows a store owner to create fine-grained roles for each and every user in their system. A default Magento installation comes with one role, Administrators. Magento ACL implementation allows you to add new roles to the system via System | Permissions | Roles. A role is essentially a collection of resources, while a resource is basically an action such as "delete user".

While adding new system configuration sections, you need to define resources for it so that Magento can use it via its ACL system. So the reason why we might be getting a 404 Error Page not found error is that we are missing the ACL definition.

This is why we need to create the app/code/community/Foggyline/HappyHour/etc/adminhtml.xml file with the following content:

<?xml version="1.0"?>
<config>
   <acl>
      <resources>
         <admin>
            <children>
               <system>
                  <children>
                     <config>
                        <children>
                           <foggyline_happyhour module="foggyline_happyhour">">
                              <title>FoggylineHappyHour</title>
                           </foggyline_happyhour>
                        </children>
                     </config>
                  </children>
               </system>
            </children>
         </admin>
      </resources>
   </acl>
</config>

Once done, you need to log out and then log back in to Magento administration in order for acl (access list) to kick in, otherwise you will still be getting a 404 Error Page not found error when you try to access System | Configuration | Foggyline | FoggylineHappyHour.

Now get back to the adminhtml.xml file. The syntax of the file seems somewhat recursive with all those children elements repeating. We could say that we need to define a resource whose path matches the system configuration option defined under system.xml. So if a base path for the acl resource within adminhtml.xml is config > acl > resources > admin > children > system > children > config > children, we simply need to define a new child within it called foggyline_happyhour like we did in the preceding example. The element name foggyline_happyhour must match the element name of the section from within system.xml.

The title element simply dictates what will show up in the Magento administration panel when the node tree is displayed.

If all went well, you should be able to see your configuration options interface as shown in the following screenshot:

Magento allows each configuration option defined through system.xml to have a default value. For example, let's say we want the default value of our custom_message to be Hello World string. To do so, we turn to our config.xml file as follows:

<?xml version="1.0"?>
<config>
   <!-- … other elements ... -->
   <default>
      <foggyline_happyhour>
         <settings>
            <custom_message><![CDATA[Hello World]]></custom_message>
         </settings>
      </foggyline_happyhour>
   </default>
   <!-- … other elements ... -->
</config>

It might look a bit confusing at first, but notice how the config.xml element paths within the default element path foggyline_happyhour> settings >foggyline_happyhour follow the system.xml element paths within the sections element path foggyline_happyhour> settings > foggyline_happyhour (minus the groups element). Now if you open the System | Configuration | Foggyline | FoggylineHappyHour, you should see, if you haven't previously saved some other value, the text Hello World under the Custom Message option value.

Finally, as shown in the following code snippet, we will use the helper Data class to add utility methods for extracting our configuration option value from the system.xml:

<?php

class Foggyline_HappyHour_Helper_Data extends Mage_Core_Helper_Data
{
   const XML_PATH_CUSTOM_MESSAGE = 'foggyline_happyhour/settings/custom_message';
   
   public function getCustomMessage($storestore = null)
   {
      return Mage::getStoreConfig(self::XML_PATH_CUSTOM_MESSAGE, $store);
   }
}

Looking at the preceding code, it is easy to conclude how the const XML_PATH_CUSTOM_MESSAGE string value follows the same XML elements path as previously mentioned for system.xml and config.xml. Passing that string to Mage::getStoreConfig() will retrieve our custom message.

To confirm everything is working, add the following action to your app/code/community/Foggyline/HappyHour/controllers/HelloController.php class file:

<?php

class Foggyline_HappyHour_HelloController extends Mage_Core_Controller_Front_Action
{   
   public function helperTestAction()
   {
      echo Mage::helper('foggyline_happyhour')->getCustomMessage();
   }
}

Now open the http://magento.loc/index.php/happyhour/hello/helperTest URL in the browser; if all is good, you should be able to see the Hello World message.

Even though we have covered a lot of ground so far, we have barely scratched the surface. The massiveness of the Magento platform hides far more features. These, however, are left for you to discover.

Magento is known for its poor developer documentation regarding certain features. Every now and then you will find yourself tracing the Magento core code trying to understand its inner workings. Hopefully, the preceding introduction will give you a good starting point.

Summary


Up until now, we have covered the basics of Magento's overall structure. We took a dive into the extension structure itself. As we progressed through individual extension structures, we familiarized ourselves with configuration file, Model, Block, Helper, controller classes, and a few other important concepts. We gave a brief intro to the Magento event/observer pattern, cron jobs functionality, and access lists. With this combined knowledge, we should now be ready for the next chapter, the full-blown extension.

Left arrow icon Right arrow icon

Key benefits

  • Get a detailed insight into the structure of Magento
  • Learn about event/observer-driven modules
  • Get a hands-on introduction to custom shipping and payment methods

Description

Modules, are a group of php and xml files meant to extend the system with new functionality, or override core system behavior. Most of the base Magento system is built using the module system, so you can see why they are an important feature for this rich open-source e-commerce solutions. This book explores key module development techniques and teaches you to modify, understand and structure your modules making it easy for you to get a strong foundation for clean and unobtrusive Magento module development. Getting Started with Magento Extension Development is a practical, hands-on guide to building Magento modules from scratch. This book provides an in depth introduction and helps you discover features such as; blocks, controllers, models, configuration files, and other crucial elements which contribute to the Magento architecture. This book introduces the you to real-world modules and helps provide a strong foundation which you need to become a professional Magento module developer. The book further explores best practices and tips and tricks offering you the ultimate go to guide. Getting Started with Magento Extension Development focuses on three areas. First you are guided through the entire Magento structure, where each important directory or file is explored in detail. Then the essence of the module structure and development is explained through the detailed coverage of models, blocks, controllers, configuration, and other files that manifest a single module. Finally, a detailed set of instructions is given for building four real-world modules, including a payment and shipping module.

Who is this book for?

This book contains valuable insights for both newbies and already established Magento developers. This book is targeted at new and intermediate PHP developers starting afresh with Magento module development.

What you will learn

  • Explore the module structure
  • Learn about Block classes, Model classes, and Controller classes
  • Get to grips with Configuration files
  • Understand the importance of Event/Observer programming
  • Learn CRON jobs
  • Discover the key module functionality
Estimated delivery fee Deliver to United States

Economy delivery 10 - 13 business days

Free $6.95

Premium delivery 6 - 9 business days

$21.95
(Includes tracking information)

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Sep 25, 2013
Length: 128 pages
Edition : 1st
Language : English
ISBN-13 : 9781783280391
Concepts :
Tools :

What do you get with Print?

Product feature icon Instant access to your digital copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Redeem a companion digital copy on all Print orders
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Estimated delivery fee Deliver to United States

Economy delivery 10 - 13 business days

Free $6.95

Premium delivery 6 - 9 business days

$21.95
(Includes tracking information)

Product Details

Publication date : Sep 25, 2013
Length: 128 pages
Edition : 1st
Language : English
ISBN-13 : 9781783280391
Concepts :
Tools :

Packt Subscriptions

See our plans and pricing
Modal Close icon
$19.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
$199.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts
$279.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total $ 93.98
Magento : Beginner's Guide - Second Edition
$54.99
Getting Started with Magento Extension Development
$38.99
Total $ 93.98 Stars icon

Table of Contents

6 Chapters
An Overview of Magento Extensions Chevron down icon Chevron up icon
Building the Extension – Maximum Order Amount Chevron down icon Chevron up icon
Building the Extension – Logger Chevron down icon Chevron up icon
Building the Extension – Shipping Chevron down icon Chevron up icon
Building the Extension – Payment Chevron down icon Chevron up icon
Packaging and Publishing Your Extension Chevron down icon Chevron up icon

Customer reviews

Rating distribution
Full star icon Full star icon Full star icon Full star icon Full star icon 5
(2 Ratings)
5 star 100%
4 star 0%
3 star 0%
2 star 0%
1 star 0%
mrGott Nov 01, 2014
Full star icon Full star icon Full star icon Full star icon Full star icon 5
This is a really good book. I would recommend it to those who would like to start with basics.
Amazon Verified review Amazon
Cosenodefi Nov 24, 2013
Full star icon Full star icon Full star icon Full star icon Full star icon 5
An excellent book for Magento developers, written by the CTO of Inchoo (inchoo.net), Branko Ajzele. IMHO, this book and the new 'Magento PHP Developer's Guide', from the same publisher... are the best reading about Magento development.The book begins with an overview of the Magento structure, from the directory level to the module level. It guides reader through several building examples of extensions, involving critical areas, and real-world techniques like:- Using event/observer patterns.- Using Models with installation/uninstallation scripts.- Usage of datagrid in the administration backed.- Developing your own payment method extension.- Processing and packaging the extension for distribution over Magento Connect marketplace.
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is the digital copy I get with my Print order? Chevron down icon Chevron up icon

When you buy any Print edition of our Books, you can redeem (for free) the eBook edition of the Print Book you’ve purchased. This gives you instant access to your book when you make an order via PDF, EPUB or our online Reader experience.

What is the delivery time and cost of print book? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela
What is custom duty/charge? Chevron down icon Chevron up icon

Customs duty are charges levied on goods when they cross international borders. It is a tax that is imposed on imported goods. These duties are charged by special authorities and bodies created by local governments and are meant to protect local industries, economies, and businesses.

Do I have to pay customs charges for the print book order? Chevron down icon Chevron up icon

The orders shipped to the countries that are listed under EU27 will not bear custom charges. They are paid by Packt as part of the order.

List of EU27 countries: www.gov.uk/eu-eea:

A custom duty or localized taxes may be applicable on the shipment and would be charged by the recipient country outside of the EU27 which should be paid by the customer and these duties are not included in the shipping charges been charged on the order.

How do I know my custom duty charges? Chevron down icon Chevron up icon

The amount of duty payable varies greatly depending on the imported goods, the country of origin and several other factors like the total invoice amount or dimensions like weight, and other such criteria applicable in your country.

For example:

  • If you live in Mexico, and the declared value of your ordered items is over $ 50, for you to receive a package, you will have to pay additional import tax of 19% which will be $ 9.50 to the courier service.
  • Whereas if you live in Turkey, and the declared value of your ordered items is over € 22, for you to receive a package, you will have to pay additional import tax of 18% which will be € 3.96 to the courier service.
How can I cancel my order? Chevron down icon Chevron up icon

Cancellation Policy for Published Printed Books:

You can cancel any order within 1 hour of placing the order. Simply contact customercare@packt.com with your order details or payment transaction id. If your order has already started the shipment process, we will do our best to stop it. However, if it is already on the way to you then when you receive it, you can contact us at customercare@packt.com using the returns and refund process.

Please understand that Packt Publishing cannot provide refunds or cancel any order except for the cases described in our Return Policy (i.e. Packt Publishing agrees to replace your printed book because it arrives damaged or material defect in book), Packt Publishing will not accept returns.

What is your returns and refunds policy? Chevron down icon Chevron up icon

Return Policy:

We want you to be happy with your purchase from Packtpub.com. We will not hassle you with returning print books to us. If the print book you receive from us is incorrect, damaged, doesn't work or is unacceptably late, please contact Customer Relations Team on customercare@packt.com with the order number and issue details as explained below:

  1. If you ordered (eBook, Video or Print Book) incorrectly or accidentally, please contact Customer Relations Team on customercare@packt.com within one hour of placing the order and we will replace/refund you the item cost.
  2. Sadly, if your eBook or Video file is faulty or a fault occurs during the eBook or Video being made available to you, i.e. during download then you should contact Customer Relations Team within 14 days of purchase on customercare@packt.com who will be able to resolve this issue for you.
  3. You will have a choice of replacement or refund of the problem items.(damaged, defective or incorrect)
  4. Once Customer Care Team confirms that you will be refunded, you should receive the refund within 10 to 12 working days.
  5. If you are only requesting a refund of one book from a multiple order, then we will refund you the appropriate single item.
  6. Where the items were shipped under a free shipping offer, there will be no shipping costs to refund.

On the off chance your printed book arrives damaged, with book material defect, contact our Customer Relation Team on customercare@packt.com within 14 days of receipt of the book with appropriate evidence of damage and we will work with you to secure a replacement copy, if necessary. Please note that each printed book you order from us is individually made by Packt's professional book-printing partner which is on a print-on-demand basis.

What tax is charged? Chevron down icon Chevron up icon

Currently, no tax is charged on the purchase of any print book (subject to change based on the laws and regulations). A localized VAT fee is charged only to our European and UK customers on eBooks, Video and subscriptions that they buy. GST is charged to Indian customers for eBooks and video purchases.

What payment methods can I use? Chevron down icon Chevron up icon

You can pay with the following card types:

  1. Visa Debit
  2. Visa Credit
  3. MasterCard
  4. PayPal
What is the delivery time and cost of print books? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela