Book Image

Getting Started with Magento Extension Development

By : Branko Ajzele
Book Image

Getting Started with Magento Extension Development

By: Branko Ajzele

Overview of this book

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.
Table of Contents (13 chapters)
Getting Started with Magento Extension Development
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

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.