rss
twitter
    Find out what I'm doing, Follow Me :)

Wednesday, May 19, 2010

Pin It

Widgets

Analysis & Usage of Collections in Magento

As a Magento Programmer I am fascinated by the use & simplicity of collection used in Magento. Simplicity, does not really mean being simple (it is rather complex structured) but easy to use. With the help of one of my colleague, I got down to understand how collection really represents the “collection” of data we are actually trying to get from database. Here is what I found drilling down into the Magento’s Core. I may be “not quite right” with the analysis, you can always comment.

Almost all the collections found inside app/code/codepool/Namespace/Module/Model/Mysql4/model/Collection.php are the child of parent Class Mage_Core_Model_Mysql4_Collection_Abstract. Primary thing done in the class constructor is initializing its resource and Model. If you look into one of the Collection class you can see in its constructor.

/**
    * @class Mage_Checkout_Model_Mysql4_Agreement_Collection
    * Initialize resource
    *
    */
    protected function _construct()
    {
      $this->_init('checkout/agreement');
    }

And this _init function has been implemented in its parent class as

/**
         * Standard resource collection initalization
         *
         * @param string $model
         * @return Mage_Core_Model_Mysql4_Collection_Abstract
         */
        protected function _init($model, $resourceModel=null)
        {
            $this->setModel($model);
            if (is_null($resourceModel)) {
                $resourceModel = $model;
            }
            $this->setResourceModel($resourceModel);
            return $this;
        }

The resource class can be found in app/code/codepool/Namespace/Module/Model/Mysql4/model.php. And this resource class in turn initializes the database table to be used in the Module along with the table’s primary key.

class Mage_Checkout_Model_Mysql4_Agreement extends Mage_Core_Model_Mysql4_Abstract
        {
     
        protected function _construct()
        {
            $this->_init('checkout/agreement', 'agreement_id');
        }
        ......
        }

It is this resource class that actually works out the database connections, read/write adapters and performs transactions. So this is the basic deduction about the link of collection with the database and its tables. But how are those collection formed still remains a mystery, not anymore! In this section of the post I will try to explain how are the collections really formed.

If I can, “collection” can be defined as collection or array of its resource. And in Magento case, most of the resources are database’s query results. Simply you can visualize “collection” to be array of your model’s resource. If a “query” in Magento returns a collection of all the products then it would mean that the very collection is an array of all the individual product’s object. But one question still remains how will the database query’s result transform into a Magento “collection“. To understand that we need to understand the collection class and its parents.

The class Structure for Mage_Core_Model_Mysql4_Collection_Abstract is like this.

Mage_Core_Model_Mysql4_Collection_Abstract
|__ Varien_Data_Collection_Db (C)
|__ Varien_Data_Collection (C)
|__ IteratorAggregate (I)
|__ Countable (I)

You can see that all collection implements two Interfaces IteratorAggregate & Countable.

IteratorAggregate is predefined in Standard PHP Library that extends Abstract Base Class Traversable. On using this Interface you can then Iterate Through Object using “foreach” construct. Countable returns the size of the Collection Object.

Among the two Interfaces, IteratorAggregate is particularly important. As you can see in Class Hierarchy both the interfaces are implemented by Varien_Data_Collection concrete class. IteratorAggregate has abstract public method getIterator() which returns the Iterator interface and the concrete Class has to implement the method on its own. It is this Iterator that provides the real iteration functionality.

So if you look into the Varien_Data_Collection you will find the getIterator() implemented like this.

/**
         * @class Varien_Data_Collection
         * Implementation of IteratorAggregate::getIterator()
         */
        public function getIterator()
        {
            $this->load();
            return new ArrayIterator($this->_items);
        }

As you can see that it first loads the “items” (I will get back to this Items) and instanciates the value to an internal Class ArrayIterator. And the Iterator returned by this function can then be iterated using foreach construct.

Looks like it is going to be a looonnnnng post, let be summarize what I’ve tried to point out until now. I’ve tried to show the link between the collection class or rather object with the database table and explain the iteration behavior of the collection object. But one question still remains how will the database query’s result transform into a Magento’s “collection“. This is where the “items” explanation need to be done.

“Items” are actually array if individual object (item) of the collection which represents the array of tuple of the database query result. As you see in the snippet above the ArrayIterator takes $this->_items are parameter. But $this->_items are not populated here on Varien_Data_Collection but rather on is child class Varien_Data_Collection_Db. Here’s the snippet from Varien_Data_Collection_Db.

/**
         * Load data
         * @class Varien_Data_Collection_Db
         * @return  Varien_Data_Collection_Db
         */
        public function load($printQuery = false, $logQuery = false)
        {
            if ($this->isLoaded()) {
                return $this;
            }
   
            $this->_renderFilters()
                 ->_renderOrders()
                 ->_renderLimit();
   
            $this->printLogQuery($printQuery, $logQuery);
   
            // Getting Data from DB
            $data = $this->getData();
   
            $this->resetData();
   
            if (is_array($data)) {
   
                // Looping on each result row
                foreach ($data as $row) {
                    // Creating Empty "item" Varien_Object's object
                    $item = $this->getNewEmptyItem();
   
                    if ($this->getIdFieldName()) {
                        $item->setIdFieldName($this->getIdFieldName());
                    }
   
                    // Setting Varien_Object's values to that of the row
                    $item->addData($row);
   
                    /**
                    * Adding the "item" to the collection @class Varien_Data_Collection
                    * So while referring to $this->_items @class Varien_Data_Collection it will return array of this "item"
                    */
                    $this->addItem($item);
                }
   
            }
   
            $this->_setIsLoaded();
            $this->_afterLoad();
            return $this;
        }
   
        /**
         * Get all data array for collection
         * @class Varien_Data_Collection_Db
         * @return array
         */
        public function getData()
        {
            if ($this->_data === null) {
                $this->_renderFilters()
                     ->_renderOrders()
                     ->_renderLimit();
   
                // Fetching all the row with the Select query set
                $this->_data = $this->_fetchAll($this->_select);
                $this->_afterLoadData();
            }
            return $this->_data;
        }

You can go through the inline comments I’ve added. This is it, I’ve finally worked out the explanation of structure & creation of Magento’s Collection and its iterative behavior. I’ve tried to show pictorially (below) what I have just described. Confused! Plz comment and of course please do comment if I am wrong, because there are “times” when you try to understand things even though they actually aren’t just like you think. I’d like to quote Paulo :“I see the world in terms of what I would like to see happen, not what actually does”!





No comments:

Post a Comment