[php]应用控制器(二)

        为了能更清晰地了解应用控制器总体实现的结构,代码中需要使用到的类都已经在之前实现了,现在只剩下的是核心的部分:AppController和Controller。

namespace demo\controller;

/**
 * Controller
 */
class Controller {
	private $appHelper;
	
	// 不能实例化,只能通过Controller::run()的方式来执行
	private function __construct() {
	
	}
	
	public static function run() {
		$instance = new self();
		// 加载配置
		$instance->init();
		// 处理请求
		$instance->handleReuqest();
	}
	
	private function init() {
		$appHelper = \demo\controller\ApplicationHelper::getInstance();
		$appHelper->init();
	}
	
	private function handleReuqest() {
		$request = new \demo\controller\Request();
		$appController = \demo\base\ApplicationRegistry::getInstance()->getAppController();

		// 执行完所有Command,有可能存在forward
		while ($cmd = $appController->getCommand($request)) {
			// var_dump($cmd);
			$cmd->execute($request);
			// 把当前Command设为已执行过
			$request->setLastCommand($cmd);
		}
		// 获取视图
		$view = $appController->getView($request);
		// 显示视图
		$this->invokeView($view);
		
		/* $cmdReslover = new \demo\command\CommandReslover();
		$cmd = $cmdReslover->getCommand($request);
		$cmd->execute($request); */
	}
	
	private function invokeView($view) {
		include("demo/view/{$view}.php");
		exit();
	}
}

         handleRequest()里面的while循环是为了处理<command>中有可能出现的<forward>,<forward>元素的值是对应的Command子类,这些<command>、<forward>、<view>和<status>的关系都已经被映射到ControllerMap中去了。

        Controller执行流程:
        1)读取xml配置到controllerMap对象;
        2)得到$request;
        3)由appController解析$request->getProperty('cmd'),返回$cmd;
        4)$cmd->execute($request),如果controllerMap中存在cmd对应的$forward,则$request->setProperties('cmd', $forward),跳到3),否则跳到5);
        5)由appController->getView($request)取得视图$view;
        6)调用视图invokeView($view);


        应用控制器AppController:

namespace demo\controller;

/**
 * AppController
 */
class AppController {
	private static $baseCmd;
	private static $defaultCmd;
	private $controllerMap;
	// 标记已经执行过的Command,防止出现forward循环
	private $invoked = array();
	
	public function __construct(\demo\controller\ControllerMap $camp) {
		if (!isset(self::$baseCmd)) {
			self::$baseCmd = new \ReflectionClass('\demo\command\Command');
			self::$defaultCmd = new \demo\command\DefaultCommand();
		}
		
		$this->controllerMap = $camp;
	}
	
	public function getView(\demo\controller\Request $request) {
		$view = $this->getResource($request, 'View');
		return $view;
	}
	
	public function getForward(\demo\controller\Request $request) {
		$forward = $this->getResource($request, 'Forward');
		if ($forward) {
			// 设置forward为新的请求
			$request->setProperties('cmd', $forward);
		}
		
		return $forward;
	}
	
	/**
	 * getView、getForward
	 * @param \demo\controller\Request $request
	 * @param string $resType
	 */
	private function getResource(\demo\controller\Request $request, $resType) {
		$cmd = $request->getProperty('cmd');
		$previous = $request->getLastCommand();		
		$status = $previous->getStatus();
		$status = $status ? $status : 0;
		$acquire = "get{$resType}";
		
		// 按指定优先级获取view或forward 
		$resource = $this->controllerMap->$acquire($cmd, $status);
		if (!$resource) {
			$resource = $this->controllerMap->$acquire('default', $status);
		}
		if (!$resource) {
			$resource = $this->controllerMap->$acquire($cmd, 0);
		}
		if (!$resource) {
			$resource = $this->controllerMap->$acquire('default', 0);
		}
		
		return $resource;
	}
	
	public function getCommand(\demo\controller\Request $request) {
		$previous = $request->getLastCommand();
		if (!$previous) {
			// 当前为第一次请求
			$cmd = $request->getProperty('cmd');
			if (!$cmd) {
				// cmd为空,返回default
				$request->setProperties('cmd', 'default');
				return self::$defaultCmd;
			} 
		} else {
			// 返回forward
			$cmd = $this->getForward($request);
			if (!$cmd) {
				return null;
			}
		}
		
		// 取得Command对象 
		$cmdObj = $this->resloveCommand($cmd);
		if  (!$cmdObj) {
			throw new \demo\base\AppException("'Command {$cmd} not found!");
		}
		
		// 判断是否forward循环
		$cmdClass = get_class($cmdObj);
		if (isset($this->invoked[$cmdClass])) {
			throw new \demo\base\AppException('Circular Forwarding!');
		}
		$this->invoked[$cmdClass] = true;
		
		return $cmdObj;
	}
	
	/**
	 * 从ControllerMap中获取$cmd对应的映射
	 * @param string $cmd
	 */
	public function resloveCommand($cmd) {
		$classroot = $this->controllerMap->getClassroot($cmd);
		$sep = DIRECTORY_SEPARATOR;
		$filePath = "demo{$sep}command{$sep}{$classroot}.php";
		$className = "\\demo\\command\\{$classroot}";	
		if (file_exists($filePath)) {
			@require_once $filePath;
			if (class_exists($className)) {
				$cmdClass = new \ReflectionClass($className);
				if ($cmdClass->isSubclassOf(self::$baseCmd)) {
					return $cmdClass->newInstance();
				} 
			}
		}
		
		return null;
	}
}

        要注意的是Request对象,getResource和getCommand都是根据Request对象里面的cmd或者lastCommand来处理的。

        应用控制器依然需要从请求的url中获取cmd来判断调用Command,但它能够利用<forward>来控制程序的流程,而且能够为一个可以产生许多不同反馈的页面安排不同的视图(比如表单,提交后可能是error、success等其它反馈视图)。


        接下来就是视图模式了。模板视图是PHP自身拥有的功能,它可以和HTML标签结合在一起使用。这种方式的好处是能提高效率,但同时也会为后续开发和长期维护带来不良的后果。实现模板视图的方法是虚构一个模板系统,将特定的标签(tag)解析成系统中的值。当然也可以使用模板引擎,比如Smarty。
        我们让视图只执行“显示数据”的功能,那么通常可以先获取数据,然后传递到视图。另一方面,视图助手(ViewHelper)能够帮助实现传递数据。我们可以为他针对一个视图设计,也可以为多个视图共享。

namespace demo\view;

/**
 * 视图助手
 */
class ViewHelper {
	
	/**
	 * 返回request
	 */
	public function getRequest() {
		return \demo\base\RequestRegistry::getInstance()->getRequest();
	}
}

        这里的ViewHelper很简单,以后可以只要有需求就可给它添加方法。        


        Controller和AppController都已经完成了,那么来看个具体的例子吧。

        一个具体的Command子类,Login:

namespace demo\command;

class Login extends Command {
	protected function doExecute(\demo\controller\Request $request) {
		$userName = $request->getProperty('userName');
		$password = $request->getProperty('password');	
		
		if (!$userName || !$password) {
			// userName或者password的值不存在
			$request->addFeedback('insuffiicient_data');
			return self::status('CMD_INSUFFICIENT_DATA');
		}
		
		$user = new User($userName, $password);
		// setObject
		$request->setObject('loginUser', $user);
		if ($userName == 'root' && $password == 'root') {
			$request->addFeedback('login successfully!');
			return self::status('CMD_OK');
		} else {
			return self::status('login falied!');
		}
	}
}

         对于下面三个url请求,控制器都能得到预期的结果:
    runner.php?cmd=Login&userName=root&password  // CMD_INSUFFICIENT_DATA insufficient_data.php
    runner.php?cmd=Login&userName=root&password=root // CMD_OK success.php
    runner.php?cmd=Login&userName=root&password=123 // CMD_ERROR error.php

        

         success.php:ViewHelper的例子:

<?php 
require_once 'demo/view/ViewHelper.php';

$vh = new \demo\view\ViewHelper();
$request = $vh->getRequest();
var_dump($request);
$obj = $request->getObject('loginUser');
?>

<html>
	<head>
		<title>Login Success</title>
	</head>
	<body>
		Request Command <font color="red"><?php echo $request->getProperty('cmd'); ?></font><br/>
	
		Welcome <font color="red"><?php echo $obj->getName(); ?></font><br/>
		Your password <font color="red"><?php echo $obj->getPassword(); ?></font><br/>
		
		<b>Testing...</b>		
	</body>
</html>

        下面为入口文件runner.php的代码:
use demo\controller\Controller;
require_once 'demo/controller/Controller.php';

Controller::run();


        现在已经能完成对请求的处理了,还控制了程序流程(上面就是一个例子)。一般的情况下很少用到应用控制器。如果只需要实现单一入口功能的话,一个入口文件(runner.php)直接解析url就能完成了。

        控制器层和视图层已经完成了,业务逻辑层使用领域模型,因为它能够使用数据映射器中的大部分模式。

        那么接下来需要实现持久化层了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值