Magento通过工厂方法(factory method)来实例化Model, Helper和Block类。一个工厂方法就是一种设计模式,用于通过一个类的别名来实例化一个对象,而无需使用具体的类别。
Magento采用了如下几个工厂方法:
- Mage::getModel()
- Mage::getResourceModel()
- Mage::helper()
- Mage::getSingleton()
- Mage::getResourceSingleton()
- Mage::getResourceHelper()
每个方法能通过类的别名来决定我们所要实例化的对象的真实类名,比如我们想要实例化product对象,可以通过调用getModel()方法来实现:
$product = Mage::getModel('catalog/product');
注意我们所传入的工厂名由group_classname/model_
name所组成,Magento会将其解析为一个真实的类名Mage_Catalog_Model_
Product。下面让我们来分析一下getModel()方法的内部机制:
public static function getModel($modelClass = '', $arguments = array()) { return self::getConfig()->getModelInstance ($modelClass, $arguments); }
getModel调用了Mage_Core_Model_Config类中的getModelInstance方法:
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; } }
getModelInstance()又调用getModelClassName()方法,该方法将我们类的别名作一个参数,然后尝试验证所返回类是否存在,如果该类存在,则为该类创建一个实例并将其返回到getModel()方法中:
public function getModelClassName($modelClass) { $modelClass = trim($modelClass); if (strpos($modelClass, '/')===false) { return $modelClass; } return $this->getGroupedClassName('model', $modelClass); }
getModelClassName()调用getGroupedClassName()方法(/app/code/core/Mage/Core/Model/Config.php),该方法用于返回模型中真实的类别。getGroupedClassName()中有$groupType和$classId两个参数,$groupType代表我们所要实例化的对象类型(当前仅支持model, block和helper),$classId就是我们所要实例化的类。
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 (isset($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; }
可以看出,主要工作在getGroupedClassName()中进行,该方法获取类别名catalog/product并通过explode方法去除中间的斜线并创建一个数组。然后该方法加载VarienSimplexml_Element的实例并将数组中的第一个值(group_classname)传入,同时它还是检查此类是否被重写,如果被重写,将会采用相对应的组名。
Magento还使用自定义的uc_words()方法来将首字母大写并还可以转化类别名的分隔符。最终,函数会将真实类名返回给getModelInstance()方法,在这个例子中,返回值为Mage_Catalog_Model_Product。