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。