【深入PHP 面向对象】读书笔记(十九) - 企业模式(四) - 应用控制器
2017-11-23 01:13
891 查看
12.3.2 应用控制器
应用控制器负责映射请求到命令,并映射命令到视图;使得 Command 类能够集中精力完成包括处理输入、调用应用程序逻辑和处理结果等,而不需要对视图进行调用处理。我们围绕这张图,来看模式中的参与者(应用控制器、命令和视图)之间的通信过程。
首先是前端控制器 FrontController 使用 AppController 应用控制器接口:
//前端控制器 function handleRequest() { $request = new Request(); $app_c = ApplicationRegistry::appController(); while ($cmd = $app_c->getCommand($request)) { $cmd->execute(); } $this->invokeView($request); } function invokeView($target) { include '$target.php'; exit; }
handleRequest() 方法使用 ApplicationRegistry 注册表类的 appController() 静态方法来获取一个 AppController 应用控制器对象,并通过 AppController 应用控制器对象的 getCommand() 方法来获取命令,并通过 Command 对象的 execute() 方法来执行命令。
实现概述
针对不同的情况,我们需要加载不同的页面,比如对于一个数据输入表单:如果用户添加错误类型的数据,页面可能会重新显示表单或者显示错误页;如果用户数据填写正确,页面就需要跳转到其他页面。
因此,我们需要在 Command 命令对象中定义一个状态标识来告诉系统当前状态,比如是指示用户填写数据错误,还是用户填写数据合法。这个状态标识定义成一个数组,存储在 Command 超类中:
private static $STATUS_STRINGS = array( 'CMD_DEFAULT' => 0, 'CMD_OK' => 1, 'CMD_ERROR' => 2, 'CMD_INSUF 4000 FICIENT_DATA' =>3 );
配置文件
使用 XML 格式的配置文件:
<control> <view>main</view> <view status="CMD_OK">main</view> <view status="CMD_ERROR">Error</view> <command name="ListVenues"> <view>listvenues</view> </command> <command name="QuickAddVenue"> <classroot name="AddVenue"/> <view>quickadd</view> </command> <command name="AddVenue"> <view>addvenue</view> <status value="CMD_OK"> <forward>AddSpace</forward> </status> </command> <command name="AddSpace"> <view>addspace</view> <status value="CMD_OK"> <forward>ListVenues</forward> </status> </command> </control>
<view>main</view> <view status="CMD_OK">main</view> <view status="CMD_ERROR">Error</view>
第一个 view 元素是所有命令的默认视图,如果没有指定调用特定的视图,则此处定义的默认视图会被调用。接下来两个 view 元素制定了更为具体的内容,根据命令的状态来调用相应的视图。
<command name="ListVenues"> <view>listvenues</view> </command>
command 元素用于根据指定的命令来显示视图。
<command name="QuickAddVenue"> <classroot name="AddVenue"/> <view>quickadd</view> </command>
同时配置文件还支持别名,通过别名来指定加载其他命令,来实现加载视图。
<command name="AddVenue"> <view>addvenue</view> <status value="CMD_OK"> <forward>AddSpace</forward> </status> </command>
此外,命令可以加载多个视图,并且支持通过命令的状态来确定是否加载指定视图。
解析配置文件
定义一个 ApplicationHelper 来实现解析 XML 配置文件,XML 的解析是一个烦琐的工作,具体的解析这里不详细说,只给出大致的解析过程,通过 SimpleXML 解析:
private function getOptions() { $this->ensure(file_exists($this->config),'could not found options file'); $options = @simplexml_load_file($this->config); $map = new ControllerMap(); foreach ($options->control->view as $default_view) { $stat_str = trim($default_view['status']); $status = Command::statuses($stat_str); $map->addView('default',$status,$default_view); } ApplicationRegistry::setControllerMap($map); }
存储配置数据
ControllerMap 内封装了3个数组,用于缓存从XML中解析出的数据:
class ControllerMap { private $viewMap = array(); private $forwardMap = array(); private $classrootMap = array(); function addClassroot($command, $classroot) { $this->classrootMap[$command] = $classroot; } function getClassroot($command) { if (isset($this->classrootMap[$command])) { return $this->classrootMap[$command]; } return $command; } function addView($command='default', $status=0, $view) { $this->viewMap[$command][$status] = $view; } function getView($command, $status) { if (isset($this->viewMap[$command][$status])) { return $this->viewMap[$command][$status]; } return null; } function addForward($command='default', $status=0, $newCommand) { $this->forwardMap[$command][$status] = $newCommand; } function getForward($command, $status) { if (isset($this->forwardMap[$command][$status])) { return $this->forwardMap[$command][$status]; } return null; } }
例如这样一段 XML 配置文件:
<command name="AddVenue"> <view>addvenue</view> <status value="CMD_OK"> <forward>AddSpace</forward> </status> </command>
在调用进行缓存时,会执行下面的命令:
$map->addView('AddVenue', 0, 'addvenue'); $map->addView('AddVenue', 1, 'AddSpace');
在 $viewMap 中存储:
$viewMap['AddVenue'][0] = 'addvenue'; $viewMap['AddVenue'][1] = 'AddSpace';
接下来时应用控制器类,实现根据相应的命令来调用相应的视图:
class AppController { private static $base_cmd; private static $default_cmd; private $controllerMap; private $invoked = array(); function __contruct(ControllerMap $map) { $this->controllerMap = $map; if (!self::$base_cmd) { self::$base_cmd = new ReflectionClass("Command"); self::$default_cmd = new DefaultCommand(); } } function getView(Request $req) { $view = $this->getResource($req, 'View'); return $view; } function getForward(Request $req) { $forward = $this->getResource($req, 'Forward'); if ($forward) { $req->setProperty() } return $forward; } /* getResource() 方法执行查找工作,用于转向(getForward())或选择视图(getView())*/ private function getResource(Request $req, $res) { // 得到前一个命令及其状态 $cmd_str = $req->getProperty('cmd'); $previous = $req->getLastCommand(); $status = $previous->getStatus(); if (!$status) { $status = 0; } $acquire = "get$res"; // 得到前一个命令的资源及其状态 $resource = $this->controllerMap->$acquire($cmd_str, $status); // 如果没有查找到指定命令的资源 则查找状态为0的资源 if(!$resource) { $resource = $this->controllerMap->$acquire($cmd_str, 0); } // 如果状态为0的资源也找不到的话,查找默认状态资源 if (!$resource) { $resource = $this->controllerMap->$acquire('default', $status); } // 其他情况获取'default'失败,状态为0 if (!$resource) { $resource = $this->controllerMap->$acquire('default', 0); } return $resource; } /* getCommand() 方法负责返回转向中需要使用的所有命令。*/ function getCommand(Request $req) { $previous = $req->getLastCommand(); if (!$previous) { // 这是本次请求调用的第一个命令 $cmd = $req->getProperty('cmd'); if (!$cmd) { // 如果无法得到命令 则使用默认命令 $req->setProperty('cmd', 'default'); return self::$default_cmd; } } else { // 之前已经执行过一个命令 $cmd = $this->getForward($req); if (!$cmd) { return null; } } // 在$cmd变量中保存着命令名称,并将其解析为Command对象 $cmd_obj = $this->resolveCommand($cmd); if (!$cmd_obj) { throw new AppException("could not resolve $cmd"); } $cmd_class = get_class($cmd_obj); if (isset($this->invoked[$cmd_class])) { throw new AppException("circular forwarding"); } $this->invoked[$cmd_class] = 1; return $cmd_obj; } function resolveCommand($cmd) { $classname = $this->controllerMap->getClassroot($cmd); $filepath = 'woo/command/$classroot.php'; if (file_exists($filepath)) { require_once($filepath); if (class_exists($classname)) { $cmd_class = new ReflectionClass($classname); if ($cmd_class->isSubClassOf(self::$base_cmd)) { return $cmd_class->newInstance(); } } } return null; } }
getResource() 方法执行查找工作,用于转向(getForward())或选择视图(getView()),它会优先查找最具体的字符串和状态标识的组合,然后才搜索通用的组合。
getCommand() 方法负责返回转向中需要使用的所有命令。
Command 基类
abstract class Command { private static $STATUS_STRINGS = array( 'CMD_DEFAULT' => 0, 'CMD_OK' => 1, 'CMD_ERROR' => 2, 'CMD_INSUFFICIENT_DATA' =>3 ); private $status = 0; final function __contruct(){} /*execute() 方法使用抽象方法 doExecute() 返回的值来设置状态标识,并将它保存在 Request 对象中。*/ function execute(Request $req) { $this->status = $this->doExecute($req); $req->setCommand($this); } /*getStatus() 用于当前的状态标识*/ function getStatus() { return $this->status; } /*statuses() 方法用于将字符串状态转换成相应的数字*/ static function statuses($str='CMD_DEFAULT'){ return self::$STATUS_STRINGS[$str]; } abstract function doExecute(Request $req); }
Command 类定义了一个状态字符串数组 $STATUS_STRINGS。statuses() 方法用于将字符串状态转换成相应的数字,getStatus() 用于当前的状态标识,execute() 方法使用抽象方法 doExecute() 返回的值来设置状态标识,并将它保存在 Request 对象中。
一个具体的 Command 类:
class AddVenue extends Command { function doExecute(Request $req) { $name = $req->getProperty('venue_name'); if (!$name) { $request->addFeedback('no name provided'); return self::statuses('CMD_INSUFFICIENT_DATA'); } else { $venue_obj = new Venue(null, $name); $request->setObject('venue', $venue_obj); $request->addFeedback('$name added({$venue_obj->getId()})'); return self::statuses('CMD_OK'); } } }
具体的 venue 类:
class Venue { private $id; private $name; function __construct($id, $name) { $this->id = $id; $this->name = $name; } function getName() { return $this->name; } function getId() { return $this->id; } }
这样一个基本的应用控制器就建立起来了,系统的响应由配置文件决定。
相关文章推荐
- 【深入PHP 面向对象】读书笔记(二十) - 企业模式(五) - 页面控制器
- 【深入PHP 面向对象】读书笔记(二十二) - 企业模式(七) - 业务逻辑层与事务脚本
- 【深入PHP 面向对象】读书笔记(十八) - 企业模式(三) - 表现层
- 【深入PHP 面向对象】读书笔记(十六) - 企业模式(一) - 架构概述
- 【深入PHP 面向对象】读书笔记(二十一) - 企业模式(六) - 模板视图和视图助手
- 【深入PHP 面向对象】读书笔记(十七) - 企业模式(二) - 注册表
- 【深入PHP 面向对象】读书笔记(十三) - 执行及描述任务(三) - 观察者模式
- 【深入PHP 面向对象】读书笔记(十) - 让面向对象编程更加灵活的模式(三) - 外观模式
- 【深入PHP 面向对象】读书笔记(六) - 模式原则
- 深入PHP:面向对象、模式与实践-读书笔记:对象工具1
- 【深入PHP 面向对象】读书笔记(八) - 让面向对象编程更加灵活的模式(一) - 组合模式
- 【深入PHP 面向对象】读书笔记(十五) - 执行及描述任务(五) - 命令模式
- 【深入PHP 面向对象】读书笔记(九) - 让面向对象编程更加灵活的模式(二) - 装饰模式
- 【深入PHP 面向对象】读书笔记(十四) - 执行及描述任务(四) - 访问者模式
- 【深入PHP 面向对象】读书笔记(五) - 模式
- 深入理解PHP:高级技巧、面向对象与核心技术(原书第3版) -- 设计模式之单一模式
- [李景山php] 深入理解PHP内核[读书笔记]--第五章:类和面向对象 --访问控制的实现
- 【深入PHP 面向对象】读书笔记(二) - 高级特性
- 读《深入 PHP 面向对象、模式与实践》
- [李景山php] 深入理解PHP内核[读书笔记]--第五章:类和面向对象 --类的结构和实现