本篇主要分析控制器工厂,这个很重要,因为在页面上操作时,其实都是操作相关模块的相关方法,而相关模块对应到SuarCRM中都是通过控制器工厂来加载各个不同的控制器的。

class SugarApplication
{```function execute(){```$this->controller = ControllerFactory::getController($module);if ($this->controller->action === 'sidecar' ||($this->controller->action === 'index' && $this->controller->module === 'Home' &&(empty($_REQUEST['entryPoint']) || (isset($_REQUEST['action']) && $_REQUEST['action'] === 'DynamicAction'))) ||empty($_REQUEST)) {// check for not authorised users$this->checkMobileRedirect();$this->controller->action = 'sidecar';$this->controller->execute();return;}```}```
}

上面的逻辑是假设在地址栏上输入http://sugar来的,本篇都是以此为基础的。

/*** ./include/MVC/Controller/ControllerFactory.php* 控制器工厂主要用来依据传过来的各个模块生成相应的控制器实例的* 首先匹配的是模块的控制器类,没有的话再匹配Sugar自己的控制器类* "modules/{$module}/controller.php" > 'include/MVC/Controller/SugarController.php'*/
class ControllerFactory
{/*** Obtain tain an instance of the correct controller.** @return an instance of SugarController*/function getController($module){if(SugarAutoLoader::requireWithCustom("modules/{$module}/controller.php")) {$class = SugarAutoLoader::customClass(ucfirst($module).'Controller');} else {SugarAutoLoader::requireWithCustom('include/MVC/Controller/SugarController.php');$class = SugarAutoLoader::customClass('SugarController');}if(class_exists($class, false)) {$controller = new $class();}if(empty($controller)) {$controller = new SugarController();}//setup the controller$controller->setup($module);return $controller;}
}

比如,登录系统后,会进入到默认的Home模块(这个是在config.php中的'default_module'来获取的),那么依据上面的逻辑会加载SugarController类,因为在Home模块中没有controller.php文件,那么看看SugarController类的相关实现

/*** ./include/MVC/Controller/SugarController.php* 实例化&启动*/
class SugarController
{```/*** Constructor. This ie meant tot load up the module, action, record as well* as the mapping arrays.*/public function SugarController(){}/*** Called from SugarApplication and is meant to perform the setup operations* on the controller.* 模块的优先级是URL传过来的最高,其次是控制器工厂缓过来的*/public function setup($module = ''){if(empty($module) && !empty($_REQUEST['module']))$module = $_REQUEST['module'];// set the moduleif(!empty($module))$this->setModule($module);if(!empty($_REQUEST['target_module']) && $_REQUEST['target_module'] != 'undefined') {$this->target_module = $_REQUEST['target_module'];}// set properties on the controller from the $_REQUEST// 依据URL传过来的值设置方法、记录、试图、返回模块、返回方法、返回id$this->loadPropertiesFromRequest();// load the mapping files$this->loadMappings();}/*** Set the module on the Controller** @param object $module*/public function setModule($module){$this->module = $module;}/*** Set properties on the Controller from the $_REQUEST**/private function loadPropertiesFromRequest(){if(!empty($_REQUEST['action']))$this->action = $_REQUEST['action'];if(!empty($_REQUEST['record']))$this->record = $_REQUEST['record'];if(!empty($_REQUEST['view']))$this->view = $_REQUEST['view'];if(!empty($_REQUEST['return_module']))$this->return_module = $_REQUEST['return_module'];if(!empty($_REQUEST['return_action']))$this->return_action = $_REQUEST['return_action'];if(!empty($_REQUEST['return_id']))$this->return_id = $_REQUEST['return_id'];}/*** Load map files for use within the Controller**/private function loadMappings(){$this->loadMapping('action_view_map');$this->loadMapping('action_file_map');$this->loadMapping('action_remap', true);}/*** Generic load method to load mapping arrays.* 若include/MVC/Controller/{$var}.php和modules/{$this->module}/{$var}.php两者存在[之一]* 则加载并把其值存储到缓存和本类相关属性中*/private function loadMapping($var, $merge = false){$$var = sugar_cache_retrieve("CONTROLLER_". $var . "_".$this->module);if(!$$var){if($merge && !empty($this->$var)){$$var = $this->$var;}else{$$var = array();}foreach(SugarAutoLoader::existingCustom("include/MVC/Controller/{$var}.php", "modules/{$this->module}/{$var}.php") as $file) {require $file;}$varname = str_replace(" ","",ucwords(str_replace("_"," ", $var)));foreach(SugarAutoLoader::existing("custom/application/Ext/$varname/$var.ext.php", "custom/modules/{$this->module}/Ext/$varname/$var.ext.php") as $file) {require $file;}sugar_cache_put("CONTROLLER_". $var . "_".$this->module, $$var);}$this->$var = $$var;}```}

这里主要看看loadMapping方法,这个主要是完成导入试图动作、后台操作、前台默认进入页面的相关逻辑,以下是依次打印的三个数组

$this->loadMapping('action_view_map');
format '<action_name>' => '<view_name>'
Array
([multieditview] => multiedit[detailview] => detail[editview] => edit[listview] => list[popup] => popup[vcard] => vcard[importvcard] => importvcard[importvcardsave] => importvcardsave[modulelistmenu] => modulelistmenu[favorites] => favorites[sidecar] => sidecar[noaccess] => noaccess[quickedit] => quickedit[edit_mobile] => edit_mobile[detail_mobile] => detail_mobile[list_mobile] => list_mobile[wirelessmodule] => wirelessmodule[wirelessdetail] => wirelessdetail[wirelesslist] => wirelesslist[wirelessedit] => wirelessedit[wlsave] => wirelesssave[sugarpdf] => sugarpdf[dc] => dc[dcajax] => dcajax[quick] => quick[quickcreate] => quickcreate[spot] => spot[gs] => gs[inlinefield] => inlinefield[inlinefieldsave] => inlinefieldsave[pluginlist] => plugins[downloadplugin] => downloadplugin[metadata] => metadata[cubes] => cubes[debug] => debug[additionaldetailsretrieve] => additionaldetailsretrieve[tour] => tour
)$this->loadMapping('action_file_map');
页面相关操作涉及到文件
Array
([subpanelviewer] => include/SubPanel/SubPanelViewer.php[save2] => include/generic/Save2.php[targetlistupdate] => modules/ProspectLists/TargetListUpdate.php[deleterelationship] => include/generic/DeleteRelationship.php[import] => modules/Import/index.php[viewsugarfieldcollection] => include/SugarFields/Fields/Collection/view.sugarfieldcollection.php
)$this->loadMapping('action_remap', true);
存放的是点击模块后默认进入的就是列表页
Array
([index] => listview
)

上面已经实例化了具体控制类,下面便来看看“$this->controller->execute();”的逻辑

class SugarController
{```/*** This method is called from SugarApplication->execute and it will bootstrap the entire controller process*/final public function execute(){try{$this->process();// 为空$this->postProcess();if(!empty($this->view)){// 此方法可以看看ViewFactory一章$this->processView();}elseif(!empty($this->redirect_url)){$this->redirect();}}catch (Exception $e){$this->handleException($e);}}/*** if we have a function to support the action use it otherwise use the default action** 1) check for file* 2) check for action*/public function process(){// sidecar,这一步是在SugarApplication中$this->controller->action = 'sidecar';设置的$GLOBALS['action'] = $this->action;// Home$GLOBALS['module'] = $this->module;// check to ensure we have access to the module.// 类属性默认为trueif($this->hasAccess){// sidecar$this->do_action = $this->action;/** sidecarpublic static $action_case_file = array('editview'=>'EditView','detailview'=>'DetailView','listview'=>'ListView');public static function getActionFilename($action) {if(isset(self::$action_case_file[$action])) {return self::$action_case_file[$action];}return $action;}*/$file = self::getActionFilename($this->do_action);/** 由于Home的newBean为null,因此这一步没啥用public function loadBean(){$bean = BeanFactory::newBean($this->module);if(!empty($bean)) {$this->bean = $bean;if(!empty($this->record)){$this->bean->retrieve($this->record);if(!empty($this->bean->id)) {$GLOBALS['FOCUS'] = $this->bean;}}}}*/$this->loadBean();/** 任务列表,依次执行以下方法,只要有一个方法中$this->_processed为true,那么便结束public $process_tasks = array('blockFileAccess','handleEntryPoint','remapWirelessAction','callLegacyCode','remapAction','handle_action','handleActionMaps',);*/if (!$this->_processed) {foreach ($this->process_tasks as $process) {$this->$process();if ($this->_processed) {break;}}}$this->redirect();}else{$this->no_access();}}// 这一步不会执行,因为在配置文件中admin_access_control为false// 若是要执行,需设为trueprivate function blockFileAccess(){// check if the we have enabled file_access_control and if so then check the mappings on the request;if(!empty($GLOBALS['sugar_config']['admin_access_control']) && $GLOBALS['sugar_config']['admin_access_control']){$this->loadMapping('file_access_control_map');//since we have this turned on, check the mapping file$module = strtolower($this->module);$action = strtolower($this->do_action);if(!empty($this->file_access_control_map['modules'][$module]['links'])){$GLOBALS['admin_access_control_links'] = $this->file_access_control_map['modules'][$module]['links'];}if(!empty($this->file_access_control_map['modules'][$module]['actions']) && (in_array($action, $this->file_access_control_map['modules'][$module]['actions']) || !empty($this->file_access_control_map['modules'][$module]['actions'][$action]))){// check paramsif(!empty($this->file_access_control_map['modules'][$module]['actions'][$action]['params'])){$block = true;$params = $this->file_access_control_map['modules'][$module]['actions'][$action]['params'];foreach($params as $param => $paramVals){if(!empty($_REQUEST[$param])){if(!in_array($_REQUEST[$param], $paramVals)){$block = false;break;}}}if($block){$this->_processed = true;$this->no_access();}}else{$this->_processed = true;$this->no_access();}}}else$this->_processed = false;}/*** 统一入口* This code is part of the entry points reworking. We have consolidated all* entry points to go through index.php. Now in order to bring up an entry point* it will follow the format:* 'index.php?entryPoint=download'* the download entry point is mapped in the following file: entry_point_registry.php**/private function handleEntryPoint(){if(!empty($_REQUEST['entryPoint'])){$this->loadMapping('entry_point_registry');$entryPoint = $_REQUEST['entryPoint'];if(!empty($this->entry_point_registry[$entryPoint])){require_once($this->entry_point_registry[$entryPoint]['file']);$this->_processed = true;$this->view = '';}}}/*** 这个主要是从手机端来的* Remap the action to the wireless equivalent if the module supports it and* the user is on a mobile device. Also, map wireless actions to normal ones if the* user is not using a wireless device.*/private function remapWirelessAction(){$this->loadMapping('wireless_module_registry');$this->view_object_map['wireless_module_registry'] = $this->wireless_module_registry;$action = strtolower($this->action);if ($this->module == 'Home' && $this->action == 'index' && isset($_SESSION['isMobile'])) {header('Location:index.php?module=Users&action=wirelessmain&mobile=1');}elseif(isset($this->wireless_module_registry[$this->module]) && isset($_SESSION['isMobile'])){if ( $action == 'editview' ) $this->action = 'wirelessedit';if ( $action == 'detailview' ) $this->action = 'wirelessdetail';if ( $action == 'listview' ) $this->action = 'wirelesslist';}else {if ( $action == 'wirelessedit' ) $this->action = 'editview';if ( $action == 'wirelessdetail' ) $this->action = 'detailview';if ( $action == 'wirelesslist' ) $this->action = 'listview';if ( $action == 'wirelessmodule' ) $this->action = 'listview';}$this->do_action = $this->action;}/*** Meant to handle old views e.g. DetailView.php.* 兼容就的视图展示,可以通过此方法来二次开发*/protected function callLegacyCode(){$file = self::getActionFilename($this->do_action);if ( isset($this->action_view_map[strtolower($this->do_action)]) ) {$action = $this->action_view_map[strtolower($this->do_action)];}else {$action = $this->do_action;}// index actions actually maps to the view.list.php viewif ( $action == 'index' ) {$action = 'list';}$action = strtolower($action);if((SugarAutoLoader::existing("modules/{$this->module}/{$file}.php") &&!SugarAutoLoader::existing("modules/{$this->module}/views/view.{$action}.php")) ||(SugarAutoLoader::existing("custom/modules/{$this->module}/{$file}.php") &&!SugarAutoLoader::existing("custom/modules/{$this->module}/views/view.{$action}.php"))) {// A 'classic' module, using the old pre-MVC display files// We should now discard the bean we just obtained for tracking as the pre-MVC module will instantiate its ownunset($GLOBALS['FOCUS']);$GLOBALS['log']->debug('Module:' . $this->module . ' using file: '. $file);$this->action_default();$this->_processed = true;}}/*** Actually remap the action if required.**/protected function remapAction(){if(!empty($this->action_remap[$this->do_action])){$this->action = $this->action_remap[$this->do_action];$this->do_action = $this->action;}}/*** This array holds the methods that handleAction() will invoke, in sequence.*/protected $tasks = array('pre_action','do_action','post_action');/*** 在类中加载上述三个方法,如在实际操作中,需要在方法之前进行些工作,可以放在pre_action中 etc* This method is called from the process method. I could also be called within an action_* method.* It allows a developer to override any one of these methods contained within,* or if the developer so chooses they can override the entire action_* method.** @return true if any one of the pre_, do_, or post_ methods have been defined,* false otherwise.  This is important b/c if none of these methods exists, then we will run the* action_default() method.*/protected function handle_action(){$processed = false;foreach($this->tasks as $task){$processed = ($this->$task() || $processed);}$this->_processed = $processed;}/*** 在上面的loadMapping中找执行的方法是否在里面,不在的话便拒绝执行* 这一步时在process_task最后一个* If the action has been remapped to a different action as defined in* action_file_map.php or action_view_map.php load those maps here.**/private function handleActionMaps(){if(!empty($this->action_file_map[strtolower($this->do_action)])){$this->view = '';$GLOBALS['log']->debug('Using Action File Map:' . $this->action_file_map[strtolower($this->do_action)]);require_once($this->action_file_map[strtolower($this->do_action)]);$this->_processed = true;}elseif(!empty($this->action_view_map[strtolower($this->do_action)])){$GLOBALS['log']->debug('Using Action View Map:' . $this->action_view_map[strtolower($this->do_action)]);$this->view = $this->action_view_map[strtolower($this->do_action)];$this->_processed = true;}else$this->no_action();}```}

SugarCRM源码分析之ControllerFactory相关推荐

  1. SugarCRM源码分析之数据库连接

    这里详细介绍下SugarCRM里的数据库连接相关逻辑,数据库的相关配置在config.php中的dbconfig和dbconfigoption数据项. 'dbconfig' => array ( ...

  2. SugarCRM源码分析之缓存

    本篇分析下SugarCRM的缓存,缓存文件主要存放在./include/SugarCache里,实例化主要是SugarCache::instance()方法来实现. // ./include/Suga ...

  3. SugarCRM源码分析之日志

    本章主要介绍下SugarCRM里的日志处理类. // 获取日志实例,以后调用日志处理时,只需类似$GLOBAL['log']->fatal('msg');即可 $GLOBALS['log'] = ...

  4. 【转】ABP源码分析三:ABP Module

    Abp是基于模块化设计思想进行构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modul ...

  5. 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析

    目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...

  6. SpringBoot-web开发(四): SpringMVC的拓展、接管(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) SpringBoot-web开发(二): 页面和图标定制(源码分析) SpringBo ...

  7. SpringBoot-web开发(二): 页面和图标定制(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) 目录 一.首页 1. 源码分析 2. 访问首页测试 二.动态页面 1. 动态资源目录t ...

  8. SpringBoot-web开发(一): 静态资源的导入(源码分析)

    目录 方式一:通过WebJars 1. 什么是webjars? 2. webjars的使用 3. webjars结构 4. 解析源码 5. 测试访问 方式二:放入静态资源目录 1. 源码分析 2. 测 ...

  9. Yolov3Yolov4网络结构与源码分析

    Yolov3&Yolov4网络结构与源码分析 从2018年Yolov3年提出的两年后,在原作者声名放弃更新Yolo算法后,俄罗斯的Alexey大神扛起了Yolov4的大旗. 文章目录 论文汇总 ...

最新文章

  1. hdoj1423 最长上升公共子序列
  2. 全国大学生智能车竞赛(创意组)陕西理工大学校级选拔赛顺利举行
  3. vmware虚拟机中ubuntu上网问题
  4. 微信版花呗“分付”要来了!花呗,白条你们怎么看?
  5. 智能优化算法:蜻蜓优化算法-附代码
  6. Swift4-有妖气漫画精仿框架部分
  7. 计算机主机ppt课件,怎么用电脑制作ppt课件
  8. linux debian安装字体,Debian安装/设置笔记(字体设置)
  9. CnOpenData工商注册企业数量统计数据:省份-年度-企业数量信息表
  10. 绘制神经元的结构模式图,神经元结构示意图简易
  11. 我们都是被上帝咬过的苹果
  12. 图像风格迁移cvpr2020_CVPR 2018:一种交互式纹理迁移通用框架
  13. sld样式文件demo
  14. 如何评估ASO优化方案案例推广效果,优化aso方案
  15. 微信小程序的key值
  16. 原学而思培优、智联招聘CTO李京峰加盟T3出行
  17. 微信公众号(服务号/订阅号/小程序)注册详细流程
  18. UBuntu20.04+ROS noetic安装Baxter SDK软件
  19. MySQL对 DROP TABLE 处理过程(转自老金)
  20. 再谈数字化转型的基本认识、重点和策略

热门文章

  1. Elasticsearch系列之:Centos7安装部署Elasticsearch详细步骤
  2. 静态编译goahead
  3. C语言中scanf函数详解
  4. 字典是什么,如何获取字典中的值
  5. java xml文件解析工具_Xml文件解析工具 - java
  6. 柱状图柱子间隔和圆角及渐变色设置
  7. 概率论基础(1)古典和几何概型及事件运算
  8. CSS的display的多种布局方式总结(inline-block,table,flex)
  9. 智能电视、数字家庭、智慧城市的三位一体化中层设计
  10. 人工神经网络技术及应用,人工智能神经网络算法