Controller(控制器)负责将数据(模型)从不同的渠道中取出(可能是一个关系数据库,也可能是一个文本文件),然后将其交给View层处理。 上面的HelloWorldController就是一个简单的控制器,而appIndex和appTest1两个分别定义了控制的两个动作(action):index和test1。你可以通过给HelloWorld添加更多的appXxx方法来定义更多的动作。
控制器类放在ipr/app/controllers目录下,而且类名和文件名一定要一致,比如HelloWorldController,就需要放在HelloWorldController.php中,目录结构如下
/web
/test
/ipa
/app
/controllers
IndexController.php
HelloWorldController.php
...
/models
/config
/index.php
控制器中的动作可以通过传入参数action来调用,比如
http://localhost/test/index.php/helloWorld/index // 调用index动作
http://localhost/test/index.php/helloWorld/test1 // 调用test1动作
如果请求的动作在类中没有定义会怎么样?比如调用
http://localhost/test/index.php/helloWorld/myaction
显然HelloWorldController类里没有定义appMyaction方法,那么I-F就会自动发现这个错误,并抛出一个很有意义的异常信息:
I-Framework IException Message: fatal error: can't find action 'myaction' in 'HelloWorldController'
Script: code 0 on line 106 in file F:\publish\blog\test\I-F\util\core\ILogger.php
URL: /test/index.php/helloWorld/myaction
#0 F:\publish\blog\test\I-F\IApplication.php(293): ILogger::fatal('can't find acti...')
#1 F:\publish\blog\test\I-F\util\core\IRoute.php(220): IApplication->run()
#2 F:\publish\blog\test\I-F\IApplication.php(900): IRoute->run()
#3 F:\publish\blog\test\index.php(11): IApplication::startup()
#4 {main}
通过这条异常信息,我们很容易知道HelloWorldController里没有定义myaction这个动作.
另外需要注意的是,所有动作对应的方法appXxx不能有必需的参数,比如在控制器类中加入方法
public function appTest($arg) {
echo "i am test";
}
就是一个错误的方法,当调用test动作时,I-F将指出该方法不能带有参数:
I-Framework IException Message: fatal error: method 'HelloWorldController::apptest' can't has arguments
Script: code 0 on line 106 in file F:\publish\blog\test\I-F\util\core\ILogger.php
URL: /test/index.php/helloWorld/test
#0 F:\publish\blog\test\I-F\IApplication.php(247): ILogger::fatal('method 'HelloWo...')
#1 F:\publish\blog\test\I-F\util\core\IRoute.php(220): IApplication->run()
#2 F:\publish\blog\test\I-F\IApplication.php(900): IRoute->run()
#3 F:\publish\blog\test\index.php(11): IApplication::startup()
#4 {main}
在控制器里使用in对象访问请求的数据。对于 /helloWorld/index?id=1&title=mysql 来说,可以使用
class HelloWorldController extends IApplication {
public function appIndex() {
$id = $this->in->id;
$title = $this->in->title;
}
}
来获取id和title的值。
从0.2.1起,可以使用in的getInt/getStr方法自动对输入进行过滤:
$id = $this->in->getInt("id");//将id的值转成整型
$title = $this->in->getStr("title");//去除title两边空格并返回
使用in对象可以获取以下三种方法传递到控制器里的值:
GET方法,比如/index/query?id=1&title=mysql中的id和title两个值
POST方法,比如
<form action="/helloWorld/index" method="post">
<input type="text" name="id" value=""/>
<input type="text" name="title" value=""/>
<input type="submit" value="submit!"/>
</form>表单中的id和title两个值
通过命令行方法传递的值。比如
同样能使用$this->in->id和$this->in->title获取id和title的值。
在控制器里可以随意创建如下形式的任何空的对象:
class HelloWorldController extends IApplication {
public function appEmpty() {
$this->x->y->z = array("a", "b", "c", "d", "e");
print_r($this->x->y->z);
}
}
输出的结果将是:
Array
(
[0] => a
[1] => b
[2] => c
[3] => d
[4] => e
)
你甚至可以使用数组的形式创建新的对象:
public function appEmpty() {
$this->x["y"]["z"] = array("a", "b", "c", "d", "e");
print_r($this->x["y"]["z"]);
}
这是一个非常好的特性,免去了你得手工创建各级对象并进行初始化的烦恼。
如果Controller中定义了init和destroy方法,则会在调用任何动作之前调用init方法,在调用动作之后再调用destroy方法。同动作方法定义一样,init和destroy不能有必需的参数。
例 4.1. init/destroy
class HelloWorldController extends IApplication {
public function init() {
echo "调用前init<br/>";
}
public function appIndex() {
echo "调用 Hello,World<br/>";
}
public function destroy() {
echo "调用后destroy<br/>";
}
}
调用 /helloWorld/indx 时将输出
调用前init
调用 Hello,World
调用后destroy
init这个特性使得我们可以所有动作之前
检查用户输入
检查用户是否登录/是否有权限执行当前动作
其他需要全局控制的事情
destroy方法里可以使用 $this->getActionReturn() 方法得到动作的返回,从而可以根据此返回值进行相应的操作,比如
根据返回对应的模板
决定是显示还是跳转
记录执行日志
销毁使用的资源
总之init和destroy可以实现我们对控制器和动作的更为强大的控制。
现在举一个需要登录才能访问控制器的功能。假设我们有UserController和ProfileController需要登录才能访问,又假设我们已经写好checkLogin()函数来检查登录了,最直接的方法是:
class UserController extends IApplication {
public function init() {
checkLogin();
}
public function appIndex() {
}
}
class ProfileController extends IApplication {
public function init() {
checkLogin();
}
public function appIndex() {
}
}
当访问/user/index和/profile/index时,未登录的用户都会被init方法所拦截,现在既然两个控制器都有一模一样的init方法,我们可以将其挪到他们的父类当中去,这个父类不是IApplication,而是我们新写的一个LoginController:
class LoginController extends IApplication {
public function init() {
checkLogin();
}
}
然后使UserController和ProfileController继承该类,即可实现控制登录的功能。
class UserController extends LoginController {
public function appIndex() {
}
}
class ProfileController extends LoginController {
public function appIndex() {
}
}
这样一来,代码就会更加容易维护,而且添加更多的需要登录的控制器时,只需将该类继承LoginController即可。
在控制器里可以使用多种视图方案,让模型数据通过不同的方式显示,目前可选的有
IModelAndView - 模型和视图类结合
IModelAndTemplate - 模型和mint模板引擎结合
IModelAndSmarty - 模型和smarty模板引擎结合
IModelAndPHP - 模型使用PHP代码输出
IModelAndXml - 模型以XML格式输出
IJSON - 模型以JSON格式输出
IJSONResponse - 模型以JSON格式输出,并保证对象中含有code,message,data,exception等响应标准信息
具体的使用方法请见“View”章。
可以通过系统配置路由使控制器支持更多的功能,也可以美化我们的URL,具体请参见[MVC配置]