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()方法的内部机制:
1 2 3 4 5 6 7 8 9 10 11 | public static function getModel( $modelClass = '' , $arguments = array ()) { return self::getConfig()->getModelInstance ( $modelClass , $arguments ); } |
getModel调用了Mage_Core_Model_Config类中的getModelInstance方法:
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 | 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()方法中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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就是我们所要实例化的类。
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 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | 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。