控制器是整个网站的逻辑实现主体,是最核心部分。
简单的,控制器是一个类方法,调用model并显示数据。
不过,一般来说功能和业务都不会很简单,会自然形成或逐渐演化到一定的功能层次,形成相对清晰的层次和功能划分。
接口层要:
针对不同的端不同的权限控制或者参数校验,分出接口层,直接面对客户端。
1. 参数获取、过滤。如果不需要存到数据库,不需要回显到网页,可直接使用$_GET/$_POST_/$_REQUEST,否则过滤后再获取,写一个get方法,如
$name=g_req('name',$default,$_GET);
2. 是否使用session。有些会在框架里自动启动,我们是到具体接口才决定是否使用(大部分在构造函数里启动)。因为不同的端是使用不同的登录控制的,页面是用到session,但开放接口用了token,并不需要使用到session功能。某些页面操作可能也不会使用session,也不需要启动。为了集群部署,一般都把session放DB或缓存,启动session意味着一个请求里一来一回的读写操作,甚至两读一写,当然可以优化,后面会讲到。
3.显示网页/返回数据。
业务层:
虽然有不同的端进来,但是调用的业务都一样的,比如登录处理,那么把登录处理作为一个业务封装起来,接口层把关后,就可调用相应的业务来处理。
业务层与业务相关,可以调用其它业务作为子业务;可以组合其它功能完成某项任务,如记录日志;调用DAO层来存取数据;调用后台接口获得数据等等。
返回给接口的话,要么是数据,要么是状态值,要么是bool,接口要根据情况回复客户端的调用。
数据访问层:
业务层把数据存取功能交接给DAO层,DAO层通过不同的数据库驱动操作数据库。有些是通过模型来做这项工作,但我们基本没有模型,主要用数组,直接就是DAO层跟数据库打交道,将数据转化为数组内容。DAO层还做些基本的数据转换等处理,还有缓存处理(前端缓存,xcache及redis)。
以上是控制器的层次结构,层次结构是一种内联结构,是控制器自己的组织关系。但是接口层是控制主要负责人,有更大的灵活性和控制权,是MVC中的C,也是整个框架的中心的意思,纵向连结自身层次结构,横向连结着外部关系如,路由(入口)、视图(出口)。
控制器的继承--网站分解结构
简单地,控制器接口就一个类文件,不继承其它类。但更常见的做法是抽象一些共性的,通过一个父类,或父父类,加上组合来完成(如一个$view模板引擎)。比如PC端有一个PC端的父类,移动端有一个移动端的父类,因为它们都要显示网页,所以它们的父类又是某个父类的子类,通过继承来分解网站结构。如果有一个首页,可以继承相应的父类,然后只要实现主体部分,比如body的显示即可:
class Action{
protected $view;
}
class PCAction extends Action{
$header_tpl = 'header.tpl';
$body_tpl = '';
$footer_tpl = 'footer.tpl';
function display(){
$this->view->display('pc.tpl');//pc.tpl包括了头、尾、body的布局</span>
}
}
class Homepage extends PCAction{
function index(){
$this->body_tpl = 'homepage.tpl';
$this->display();
}
}
如果主页又能细分为左菜单,右内容,可继续分解子类:
class Homepage extends PCAction{
$left_tpl = 'body_left.tpl';
$content_tpl = '';
function __construct() {
$this->body_tpl = 'homepage.tpl';//包含了$left_tpl和$content_tpl布局
}
}
class IntroHomepage extends Homepage{
function index(){
$this->content_tpl = 'intro_content.tpl';
$this->display();
}
}
如果是开放API接口,输出json/xml,那继承关系相对会比较简单。
路由与控制器
回顾上一节关于路由的实现:
$class = 'user';
$method = 'info';
$instance = new $class ();
$ismobile = isMobile();
$instance->IsMobile($ismobile); //基类参数设置
if (method_exists ( $class, $method )) {
$instance->before();//基类抽象方法
$instance->$method ( );
$instance->after(); //基类抽象方法
} else {
Problem::send400 ();
}
基类控制器:
class Action{
protected $isMobile;
protected $view;
function isMobile($bool){}
function before(){}
function after(){}
}
(以上是示例,我们项目基类并没有before/after/isMobile等方法)
下面是业务控制器:
class user extends Action{
function before(){
//执行info方法前执行的方法
}
function after(){
//执行info方法后执行的方法
}
function info(){
//获取参数
//调用业务方法
//输出数据
}
}
从上面代码可以看出,可以通过一个基类控制器与路由器交互,参数传递,屏蔽与业务无关的细节。基类控制器还可以预先作一些参数过滤和安全控制等预处理功能,业务控制器作为子类可以获得父类的功能和参数。从这些方面来看,有一个基类控制来对接路由,还是很方便的,但又要防止过度去做预处理。
控制器与逻辑
不是指具体的业务逻辑,而是指业务组合逻辑。
比如登录控制;比如发表文章,发表前权限控制,发表后后,增加积分、记录日志。
待续。。。
控制器与输出
控制器处理后的数据要输出给客户端,一般有两种方式,一种是把在控制器中结果返回去,由框架处理;另一种直接由控制器直接echo输出返回。
比如现在常用的是使用JOSN格式输出结果,那么简单地封装一个方法,需要时由控制器调用即可:
public function json_return($data) {
header ( 'HTTP/1.1 200 OK' );
header ( "Content-Type: application/json" );
header ( "Cache-Control: no-store" );
echo json_encode ( $data );
exit ();
}