控制器

控制器是一个类文件,在一个应用中,它介于模型和视图之间。控制器向模型传递信息以获取数据或者改变数据。然后,控制器将从模型获取的数据传递给视图,最终输出并且呈现给用户。

控制器可以通过[Request::execute()]函数被调用,它基于通过url匹配的[Route],一定要阅读routing相关介绍,去理解怎么在你的应用中使用路由去映射url。

创建控制器

在程序编码中,一个控制器必须满足以下内容:

  • classes/controller目录 (或者子目录)
  • 文件名必须是小写的,例如articles.php
  • 类的名字必须可以映射到文件名 (用 _ 替换 / ) ,并且每个单词的首字母必须大写
  • 必须有 Controller这个类作为顶级父类,也就是最终要继承Controller

控制器名和文件位置的例子:

  1. // classes/controller/foobar.php
  2. class Controller_Foobar extends Controller {
  3. // classes/controller/admin.php
  4. class Controller_Admin extends Controller {

控制器也可以在子文件夹下面:

  1. // classes/controller/baz/bar.php
  2. class Controller_Baz_Bar extends Controller {
  3. // classes/controller/product/category.php
  4. class Controller_Product_Category extends Controller {

[!!] 注意,如果控制器是建在子文件夹下面,那么它将不能被默认的路由所调用。你需要定义一个包含目录参数的路由,或者需要为目录设置一个默认值。

控制器可以被其他的控制器所继承

  1. // classes/controller/users.php
  2. class Controller_Users extends Controller_Template
  3. // classes/controller/api.php
  4. class Controller_Api extends Controller_REST

[!!] [Controller_Template] 是由Kohana 提供的示例控制器

也可以让一个控制器继承另一个控制器,在其中做一些公共的事情,比如要求用户登录的处理,可以放在这里。

  1. // classes/controller/admin.php
  2. class Controller_Admin extends Controller {
  3. // This controller would have a before() that checks if the user is logged in
  4. // classes/controller/admin/plugins.php
  5. class Controller_Admin_Plugins extends Controller_Admin {
  6. // Because this controller extends Controller_Admin, it would have the same logged in check

$this->request

每个控制器都有$this->request属性,它可以通过在控制器中调用[Request]对象,获取当前请求的信息。也可以通过$this->response->body($ouput)设置响应的内容主体。

这里有一部分$this->request可用的属性和方法。 它们也可以通过 Request::instance()调用,使用 $this->request只是一个快捷的方式。要了解更多的详情,可以看看[Request] 类。

属性/方法 作用
$this->request->route() 匹配当前请求链接的 [Route]
$this->request->directory(),
$this->request->controller,
$this->request->action
匹配当前路由的目录,控制器,和动作
$this->request->param() 在路由中定义的其他参数
$this->request->redirect() 将请求重定向到另一个链接

$this->response

$this->response->body() | 请求返回的内容 $this->response->status() | 请求的http状态 (200, 404, 500, 等等) $this->response->headers() | 和相应返回的http头

Actions

你可以通过定义一个以action_为前缀的公用方法,为你的控制器创建动作。任何不是申明为public,且不是以action_为前缀的方法,都不能通过路由被调用。

一个action方法要做什么,取决与当前的请求。它控制着这个应用,是否要让用户去保存提交的博客?他们是否要提交必要的字段?他们是否有权限做这些事情?控制器会通过调用其他的类,包含相关方法,完成这件事情。任何动作都要设置$this->response->body($view),以便视图文件可以发送到浏览器输出。除非重定向或者在这之前,脚本已经结束了。

一个简单的action方法加载 视图 文件

  1. public function action_hello()
  2. {
  3. $this->response->body(View::factory('hello/world')); // This will load views/hello/world.php
  4. }

参数

参数可以通过$this->request->param('name') 方法进行调用。其中的 name是在路由中定义的name。

  1. // Assuming Route::set('example','<controller>(/<action>(/<id>(/<new>)))');
  2. public function action_foobar()
  3. {
  4. $id = $this->request->param('id');
  5. $new = $this->request->param('new');

如果参数没有设置,调用时将返回NULL。你在调用时,可以提供第二个参数,以便在没有默认值的情况下,设置一个默认值返回。

  1. public function action_foobar()
  2. {
  3. // 如果url中没有user参数,$id将会是false
  4. $id = $this->request->param('user',FALSE);

示例

一个product页面的的是view action

  1. public function action_view()
  2. {
  3. $product = new Model_Product($this->request->param('id'));
  4. if ( ! $product->loaded())
  5. {
  6. throw new HTTP_Exception_404('Product not found!');
  7. }
  8. $this->response->body(View::factory('product/view')
  9. ->set('product', $product));
  10. }

用户登录的action.

  1. public function action_login()
  2. {
  3. $view = View::factory('user/login');
  4. if ($_POST)
  5. {
  6. // Try to login
  7. if (Auth::instance()->login(arr::get($_POST, 'username'), arr::get($_POST, 'password')))
  8. {
  9. Request::current()->redirect('home');
  10. }
  11. $view->errors = 'Invalid email or password';
  12. }
  13. $this->response->body($view);
  14. }

Before and after

你可以在程序执行之前或者执行完之后使用 before()after()函数。例如,你可以检查用户是否已经登录,设置模板视图,加载一个需要的文件等等。

如果你查找Controller_Template,你将会看到那样的例子。

你可以通过$this->request->action检测正在请求的是什么action,并且做一些处理,例如在使用一个控制器时要求用户登录,除非正在使用登录的action

  1. // 在before中,检测用户的授权/登录,不通过的话,进行重定向
  2. Controller_Admin extends Controller {
  3. public function before()
  4. {
  5. // 如果这个用户不在admin组,并且没有登录,跳转到登录页
  6. if ( ! Auth::instance()->logged_in('admin') AND $this->request->action !== 'login')
  7. {
  8. $this->request->redirect('admin/login');
  9. }
  10. }
  11. public function action_login() {
  12. ...

自定义构造函数

一般情况下,你不需要去改变__construct()构造函数,因为你可以在before()函数中做相同的事情。 如果你需要改变控制器的构造函数,你必须保持之前的参数,否则php将会complain。因此,请求对象可以在控制器中调用。再者,大多数情况下你都可以使用before()函数而不改变构造函数。但是如果你需要改的话,要像下面那样:

  1. // 你几乎不需要这样做,可以使用before()实现!
  2. // 确认 Kohana_Request在你的参数里
  3. public function __construct(Request $request, Response $response)
  4. {
  5. // 你必须在你的函数中调用parent::__construct
  6. parent::__construct($request, $response);
  7. // 然后做一些其他你想做的
  8. }

扩展其他的 controllers

TODO: 扩展其他控制器的更多描述和例子,多个扩展,等等。