We suppose you continue following our Magento certification-dedicated posts in our blog. This time we will describe class group configuration and use in factory methods.
Magento uses factory methods to instantiate Models, Blocks and Helpers classes, applying a necessary method (for example getModel, helper etc.). You should pass an abstract name of a class group, followed by an entity name. Class groups are described in configuration XML files in /etc/config.xml files of appropriate modules.
Let’s have a look at the proccess of class instantiating by the example of models and try to instantiate the product object. This can be done by using getModel() method:
$product = Mage::getModel(‘catalog/product’);
This method retrieves an instance of Mage_Catalog_Model_Product this way:
app/Mage.php
1 2 3 4 5 6 7 8 9 10 11 12 |
/** * Retrieve model object * * @link Mage_Core_Model_Config::getModelInstance * @param string $modelClass * @param array|object $arguments * @return Mage_Core_Model_Abstract|false */ public static function getModel($modelClass = '', $arguments = array()) { return self::getConfig()->getModelInstance($modelClass, $arguments); } |
getModel() calls getModelInstance() method, which gets class instance with the help of getModelClassName
Magento Extension
Take your online store to the next level with BelVG Magento Extension
Visit the storeapp/code/core/Mage/Core/Model/Config.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
/** * Get model class instance. * * Example: * $config->getModelInstance('catalog/product') * * Will instantiate Mage_Catalog_Model_Mysql4_Product * * @param string $modelClass * @param array|object $constructArguments * @return Mage_Core_Model_Abstract|false */ public function getModelInstance($modelClass='', $constructArguments=array()) { $className = $this->getModelClassName($modelClass); if (class_exists($className)) { Varien_Profiler::start('CORE::create_object_of::'.$className); $obj = new $className($constructArguments); Varien_Profiler::stop('CORE::create_object_of::'.$className); return $obj; } else { return false; } } /** * Retrieve module class name * * @param sting $modelClass * @return string */ public function getModelClassName($modelClass) { $modelClass = trim($modelClass); if (strpos($modelClass, '/')===false) { return $modelClass; } return $this->getGroupedClassName('model', $modelClass); } |
getGroupedClassName() method does all the work. As you can see from getModelClassName, we pass the group type (model, block or helper) and the class identifier (in our case catalog/product) to this method. It explodes our string by using ‘/’ as needle, removes whitespaces and forms array of strings.
Then we load Varien_Simplexml_Element and pass our group name (catalog) to find class prefix name from config.xml of our module. In our example it would be Mage_Catalog_Model; in case this extension was overwritten, it could be Namespace_Catalog_Model. We also add entity class name (product) and after using Magento uc_words (based on PHP ucwords that returns a string with the first character of each word capitalized, if that character is alphabetic), we finally receive Mage_Catalog_Model_Product that will be returned in getModelInstance. As we noticed from above, it will create the object, using a new operator.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
/** * Retrieve class name by class group * * @param string $groupType currently supported model, block, helper * @param string $classId slash separated class identifier, ex. group/class * @param string $groupRootNode optional config path for group config * @return string */ public function getGroupedClassName($groupType, $classId, $groupRootNode=null) { if (empty($groupRootNode)) { $groupRootNode = 'global/'.$groupType.'s'; } $classArr = explode('/', trim($classId)); $group = $classArr[0]; $class = !empty($classArr[1]) ? $classArr[1] : null; if (isset($this->_classNameCache[$groupRootNode][$group][$class])) { return $this->_classNameCache[$groupRootNode][$group][$class]; } $config = $this->_xml->global->{$groupType.'s'}->{$group}; // First - check maybe the entity class was rewritten $className = null; if (isset($config->rewrite->$class)) { $className = (string)$config->rewrite->$class; } else { /** * Backwards compatibility for pre-MMDB extensions. * In MMDB release resource nodes <..._mysql4> were renamed to <..._resource>. So is left * to keep name of previously used nodes, that still may be used by non-updated extensions. */ if ($config->deprecatedNode) { $deprecatedNode = $config->deprecatedNode; $configOld = $this->_xml->global->{$groupType.'s'}->$deprecatedNode; if (isset($configOld->rewrite->$class)) { $className = (string) $configOld->rewrite->$class; } } } // Second - if entity is not rewritten then use class prefix to form class name if (empty($className)) { if (!empty($config)) { $className = $config->getClassName(); } if (empty($className)) { $className = 'mage_'.$group.'_'.$groupType; } if (!empty($class)) { $className .= '_'.$class; } $className = uc_words($className); } $this->_classNameCache[$groupRootNode][$group][$class] = $className; return $className; } |
Now let’s review how to declare class alias – a string that calls class (model, block, helper – depends from usage context)) we have declared. Alias naming rule is: group/entity.
As we observed, the group name is declared in configuration files in /etc/folder of the module.
General case
1 |
Namespace_Modulename_Model |
In our specific case
1 |
Mage_Catalog_Model |
Creating models objects, we can also try to use Mage::getSingleton() method. The method differs from getModel() by using different Design Pattern called Singleton, which means that only one instantiation of a class is used during the runtime process. However, when calling getModel(), a new object will be created every time the method is called.
Partner With Us
Let's discuss how to grow your business. Get a Free Quote.Helpers
Instantiate a helper class the same way you do models. We call Mage::helper(‘group/entity’) and it calls getHelperClassName() in its turn, which has a default value ‘data’ in entity name. It means that if you pass only a group name, for example Mage::helper(‘catalog’), it will create an object of Mage_Catalog_Helper_Data class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/** * Retrieve helper object * * @param string $name the helper name * @return Mage_Core_Helper_Abstract */ public static function helper($name) { $registryKey = '_helper/' . $name; if (!self::registry($registryKey)) { $helperClass = self::getConfig()->getHelperClassName($name); self::register($registryKey, new $helperClass); } return self::registry($registryKey); } |
From the code above you may notice that Magento registers a variable when the helper is being created. So helper instance can be created only one time during runtime.
1 2 3 4 5 6 7 |
public function getHelperClassName($helperName) { if (strpos($helperName, '/') === false) { $helperName .= '/data'; } return $this->getGroupedClassName('helper', $helperName); } |
1 |
Namespace_Modulename_Helper |
Blocks
The proccess of instantiating blocks is similar to the one of creating models and helpers. Blocks can be instantiated via layout xml (
1 |
Namespace_Modulename_Block |
P.S. In Magento 2.0 this process will be changed as follows: Support to receive and process direct class names was added to all basic Magento methods that create objects. Support for class-id strings was retained to ensure a smooth transition (Framework Changes from 1.x to 2.x).
So instead of Mage::getModel(‘catalog/product’) it will be Mage::getModel(‘Mage_Catalog_Model_Product’). But this would be a completely new story;)
Read our previous posts
Explaining how Magento loads and manipulates configuration information. Part II
Explaining how Magento loads and manipulates configuration information. Part I
Class Naming Conventions and Their Relationship with the Autoloader
The Main Magento Design Areas and More
Magento Custom Development
Take your online store to the next level with BelVG Magento Custom Development
Visit the page
Haha, thanks for the comment Marijus :) Shame on me for such a silly mistake
The very last sentence of “Models” section says:
“However, when calling getModel(), a new class will be created every time the method is called.”
As far as I have encountered, the very first lesson in OOP is always the definition of what “class” is and what “object”/”instance” is and the distinction between these concepts. It’s highly astonishing that developers with 5-8 years of experience still happen to mix those! Just as surprising is that no one in almost a year had any problem with it :)
Keep up the good work!
Sergei, these are very good posts and helpful for certification.
As Milen pointed out class name errors in this post, which you are aware of, you should correct it.
Hi Sergei,
I’m preparing for MCD and these tutorials are really helpful. Thanks for these great tutorials.
Happy MCD to all who are preparing for MCD.
Best Regards,
Shailendra Khare
Hi Milen,
Thanks for mentioning this typing error, my mistake:) Really glad that someone reads these articles carefully. Will try to post something new at least once in a week.
I do not think you are correct. For helpers the class name must be Namespace_Modulename_Helper and for blocks it must be Namespace_Modulename_Block.
Anyway your posts are very good and useful and I would love to see them more frequently, as with this speed until you cover all the topics Magento 2 will be already available and widely used ;)
Regards