Introduction

You’ve probably wondered sometimes why Magento database is so extensive, what kinds of information are stored in its particular tables and how MySQL4 differs from Enity (and what it actually is). The range of topics is in fact so wide that we could be writing about it forever, discussing various specific cases.

If you’re just beginning your adventure with the Magento platform, this article is perfect for you! It provides an introduction to DB, familiarizing you with the basic terms and database correlations of the framework.

Prerequisites

  • knowledge of the Magento folder structure,
  • ability to create Magento modules,
  • knowledge of MySQL.

What is ORM?

Wikipedia provides the following definition of ORM:

Object-relational mapping (ORM, O/RM, and O/R mapping) in computer science is a programming technique for converting data between incompatible type systems in object-oriented programming languages. This creates, >in effect, a “virtual object database” that can be used from within the programming language. There are both free and commercial packages available that perform object-relational mapping, although some programmers opt >to create their own ORM tools.

Using a simpler language, object-relational mapping means a mechanism that, using the objects themselves and their methods, allows performing operations on DB. Thanks to ORM, the models of Magento’s business logic don’t contain the logic of communication with the database and are independent from it. As a result, Magento can be compatible with various types of databases and platforms, although at the moment the framework only supports the MySQL system.

At this point, you probably ask yourself: “Hey, but how can I communicate with a specific DB, not telling Magento what language or what queries it should use?!” Well, truth be told, the SQL queries string in Magento is implemented elsewhere, so unfortunately we’re not dealing with some sort of magical methods that spontaneously match relevant queries, including e.g. connecting tables in case of relational bases. However, using specific tools (the characteristics of which falls outside the scope of this article), we can implement any code in the resource model layer by writing a new resource class. As mentioned above, it enables Magento to support various databases without the need to dramatically rebuild the existing classes and their methods responsible for performing operations on data collections.

Organizing what we know – structure of the database models and their classes

Magento data models are used for manipulating and reading data. All models that are strictly database-related (the so-called “Resources” models) inherit from the Mage_Core_Model_Resource_Abstract class.

Resource models can be divided into:

  • Simple, regular models (MySQL4) – standard representation of the “one table – one model” relation implementation, also understood as “one object for one table”, which means that object’s attributes correspond to every field and structure of the table. Simple models inherit additionally from the Mage_Core_Model_Mysql4_Abstract class.
  • Complex EAV (entity-attribute-value) models – these models are much more complex than the regular ones, since the data on a given “unit” (entity) is broken down into many interrelated tables. The EAV model will be elaborated on in the further part of the article, but for now you should know that an entity is a single item that represents a particular global object (in database terms), such as a product, client or order. The EAV model additionally inherits from the Mage_Eav_Model_Entity_Abstract class.

Building on the above, every type of model is shaped by the following class groups:

  • classes of the business logic model (you can find them in app/code/codePool/PackageName/ModuleName/Model/) – they contain application’s logic implementations used to manipulate the fetched data; as a rule, they don’t communicate directly with the database;
  • resource model classes (you can find them in app/code/codePool/PackageName/ModuleName/Model/Resource and in app/code/codePool/PackageName/ModuleName/sql) – they are responsible for all operations directly related to the database, especially all basic CRUD-type (Create, Read, Update, Delete) queries. However, you should also bear in mind that they don’t contain the logic of communication with DB, which in turn results from the basic ORM assumption;
  • classes of the collection’s model (the collections are usually implemented in app/code/codePool/PackageName/ModuleName/Model/ ) – they store one or more instances of the Magento model. An example of such instance may be a collection of products from a given category.

Relation of the Varien_Object to the basic database queries in the ORM terms

Before we move on, I have to mention the existence of one of the most important framework classes, that is Varien_Object. You can find it in MagentoDirectory/lib/Varien/Object.php. Varien_Object is the basic class for all Magento models, which means that every Magento model inherits from the Varien_Object class.

Varien_Object enables performing basic ORM DB-related operations, such as “get” (reading), “set” (setting), “unset” (deleting) and “has” (checking). For example, having in place $_product, which is actually an instance of the model, we can use the methods embedded in it by default, such as getName() or getPrice(), but also setPrice() and setName(), which are not implemented in the relevant class. So how do we perform operations that are theoretically invalid…? Well, the magic PHP __call() method, which is a part of the Varien_Object class, is here to help. Let’s take a closer look at this method (you’ll find it around line 620 of the code in Magento 1.9.x):

/** 
     * Set/Get attribute wrapper 
     * 
     * @param   string $method 
     * @param   array $args 
     * @return  mixed 
     */ 
    public function __call($method, $args) 
    { 
        switch (substr($method, 0, 3)) { 
            case 'get' : 
                //Varien_Profiler::start('GETTER: '.get_class($this).'::'.$method); 
                $key = $this->_underscore(substr($method,3)); 
                $data = $this->getData($key, isset($args[0]) ? $args[0] : null); 
                //Varien_Profiler::stop('GETTER: '.get_class($this).'::'.$method); 
                return $data; 

            case 'set' : 
                //Varien_Profiler::start('SETTER: '.get_class($this).'::'.$method); 
                $key = $this->_underscore(substr($method,3)); 
                $result = $this->setData($key, isset($args[0]) ? $args[0] : null); 
                //Varien_Profiler::stop('SETTER: '.get_class($this).'::'.$method); 
                return $result; 

            case 'uns' : 
                //Varien_Profiler::start('UNS: '.get_class($this).'::'.$method); 
                $key = $this->_underscore(substr($method,3)); 
                $result = $this->unsetData($key); 
                //Varien_Profiler::stop('UNS: '.get_class($this).'::'.$method); 
                return $result; 

            case 'has' : 
                //Varien_Profiler::start('HAS: '.get_class($this).'::'.$method); 
                $key = $this->_underscore(substr($method,3)); 
                //Varien_Profiler::stop('HAS: '.get_class($this).'::'.$method); 
                return isset($this->_data[$key]); 
        } 
        throw new Varien_Exception("Invalid method ".get_class($this)."::".$method."(".print_r($args,1).")"); 
    }

Having analysed this method, we can see that in accordance with the __call() implementation, “get”, “set”, “uns” and “has” are the available prefixes of undefined methods.

Returning to our example, when calling a method that is not defined in the model’s class (that is e.g. setPrice), Magento (more precisely PHP) starts to search for the method in the parent classes. When the search fails, PHP takes one last try, calling the magic __call() method from the Varien_Object class. In the example with the product, the search process can be described as the following schedule:

Mage_Catalog_Model_Product > Mage_Catalog_Model_Abstract > Mage_Core_Model_Abstract > Varien_Object.

When Varien_Object finds the “set” prefix, it can miraculously transform the call of an undefined method to a desired operation, at the same time fulfilling the basic EAV postulates (don’t worry, we’ll get to EAV soon).

Support of the Varien_Object class as the “last resort” for undefined methods that are supposed to handle CRUD attributes is invaluable. However, we should bear in mind that the whole process might take long milliseconds, so we should use this class responsibly.

We’ll elaborate on EAV and explain how ORM helps in handling it next time. Stay tuned!