组件

组件(Components)是在控制器之间共享的封装了的逻辑。CakePHP 附带一套非常棒的核心组件,可帮你完成各种常见任务。你也可以创建自己的组件。如果你发现自己要在控制器间复制粘贴代码,就应当考虑创建自己的组件,封装这些功能。创建组件可以保持控制器代码简洁,并且让你可以在不同的项目中重用代码。

每个核心组件都会在各自的章节中详细介绍,请参看组件 。本节将描述如何配置和使用组件,以及如何创建你自己的组件。

配置组件

许多核心组件需要进行配置。一些要求配置的组件的例子为Authentication 以及Cookie 。这些组件以及一般组件的配置,通常都是用$components 数组、或者控制器的 beforeFilter() 方法来进行:

  1. class PostsController extends AppController {
  2. public $components = array(
  3. 'Auth' => array(
  4. 'authorize' => array('controller'),
  5. 'loginAction' => array(
  6. 'controller' => 'users',
  7. 'action' => 'login'
  8. )
  9. ),
  10. 'Cookie' => array('name' => 'CookieMonster')
  11. );

上面的代码片段是使用 $components 数组配置组件的例子。所有的核心组件都可以使用这种方式进行配置。此外,你也可以在控制器的 beforeFilter() 方法中配置组件。这种方式适用于你需要将一个函数的结果赋值给一个组件属性的情况下。上面的例子也可以这样:

  1. public function beforeFilter() {
  2. $this->Auth->authorize = array('controller');
  3. $this->Auth->loginAction = array(
  4. 'controller' => 'users',
  5. 'action' => 'login'
  6. );
  7.  
  8. $this->Cookie->name = 'CookieMonster';
  9. }

然而,也有这种可能:一个组件的特定配置选项要在控制器的 beforeFilter() 运行前设置。为此,一些组件允许在 $components 数组中设置配置选项:

  1. public $components = array(
  2. 'DebugKit.Toolbar' => array('panels' => array('history', 'session'))
  3. );

请查阅相关文档来确定每个组件提供的配置选项。

常用的一个设置是 className,这让你可以赋予组件别名。这个选项可用于当你想要用定制实现的组件来替换 $this->Auth 或者其它常用组件时:

  1. // app/Controller/PostsController.php
  2. class PostsController extends AppController {
  3. public $components = array(
  4. 'Auth' => array(
  5. 'className' => 'MyAuth'
  6. )
  7. );
  8. }
  9.  
  10. // app/Controller/Component/MyAuthComponent.php
  11. App::uses('AuthComponent', 'Controller/Component');
  12. class MyAuthComponent extends AuthComponent {
  13. // 添加你的代码来重载核心 AuthComponent
  14. }

上面的例子,将在控制器中赋予 $this->Auth 别名 MyAuthComponent

注解

赋予组件别名,会在任何用到该组件的地方,替换原来的实例,包括在其它组件内。

使用组件

一旦你已经在控制器中引入了一些组件,用起来是非常简单的。每个组件都作为控制器中的属性可供调用。如果你已经在控制器中加载了 SessionComponentCookieComponent ,你就可以像下面这样访问它们:

  1. class PostsController extends AppController {
  2. public $components = array('Session', 'Cookie');
  3.  
  4. public function delete() {
  5. if ($this->Post->delete($this->request->data('Post.id')) {
  6. $this->Session->setFlash('Post deleted.');
  7. return $this->redirect(array('action' => 'index'));
  8. }
  9. }

注解

由于模型和组件都是作为属性被引入到控制器中,它们共享相同的'命名空间'。确保不要给组件和模型起相同的名字。

动态加载组件

你可能不需要在控制器的所有动作中让所有组件可以使用。这种情况下,你可以使用Component Collection 在运行时(即动态)加载组件。在控制器方法中,你可以这样做:

  1. $this->OneTimer = $this->Components->load('OneTimer');
  2. $this->OneTimer->getTime();

注解

切记,动态加载组件并不会调用它的 initialize 方法。如果调用的组件有这个方法,就需要在加载后手动调用。

组件回调

组件提供了一些请求生命周期的回调函数,来增强请求周期(的操作)。欲知组件提供的回调函数的更多信息,请参考 组件 API

创建组件

设想我们的在线应用需要在程序的很多不同地方执行一个复杂的数学运算。我们可以创建一个组件把这个共享的逻辑封装起来,从而可以在很多不同的控制器中使用。

首先要创建一个新组件的文件和类。创建app/Controller/Component/MathComponent.php 文件。组件的基本构造如下:

  1. App::uses('Component', 'Controller');
  2. class MathComponent extends Component {
  3. public function doComplexOperation($amount1, $amount2) {
  4. return $amount1 + $amount2;
  5. }
  6. }

注解

所有的组件必须继承自 Component。如果不这样做,就会导致异常。

在控制器中引入组件

一旦编写好了组件,就可以在应用程序的控制器的 $components 数组中引用组件的名称(不包括 "Component")来使用它。控制器会自动获得一个以组件名称命名的属性,通过这个属性就可以访问组件实例了:

  1. /* 让新的组件可以通过 $this->Math 访问,以及标准的 $this->Session */
  2. public $components = array('Math', 'Session');

AppController 控制器中声明的组件会与其它控制器中声明的组件进行合并。因此没有必要重复声明同一组件。

在控制器中引用组件时,也可以声明一些参数,传递给组件的构造函数。这些参数可以被组件处理:

  1. public $components = array(
  2. 'Math' => array(
  3. 'precision' => 2,
  4. 'randomGenerator' => 'srand'
  5. ),
  6. 'Session', 'Auth'
  7. );

上面的例子会将包含 precision 和 randomGenerator 的数组作为第二个参数传递给MathComponent::__construct() 。按照约定,如果数组的键与组件的公共属性吻合,属性将被设置为该键对应的值。

在组件中使用其他组件

有时候组件可能会用到其它组件。这种情况下,可以在组件中引入其它组件,方式和在控制器中引入组件完全一样—使用 $components 变量:

  1. // app/Controller/Component/CustomComponent.php
  2. App::uses('Component', 'Controller');
  3. class CustomComponent extends Component {
  4. // 你的组件使用的其它组件
  5. public $components = array('Existing');
  6.  
  7. public function initialize(Controller $controller) {
  8. $this->Existing->foo();
  9. }
  10.  
  11. public function bar() {
  12. // ...
  13. }
  14. }
  15.  
  16. // app/Controller/Component/ExistingComponent.php
  17. App::uses('Component', 'Controller');
  18. class ExistingComponent extends Component {
  19.  
  20. public function foo() {
  21. // ...
  22. }
  23. }

注解

与控制器中引入的组件不同,组件中的组件不会触发回调。

组件 API

  • class Component
  • Component 基类提供了一些方法,用于通过 ComponentCollection 延迟加载其它组件,以及设置的常见处理。它也提供所有组件回调函数的原型。

  • Component::construct(ComponentCollection $collection, $settings = array())[](#Component::construct)

  • Component 基类的构造函数。所有与 $settings 中的键同名的公共属性,其值会变成 $settings 中该键对应的值。

回调方法

  • Component::initialize(Controller $controller)
  • 在控制器的 beforeFilter() 方法执行前被调用。

  • Component::startup(Controller $controller)

  • 在控制器的 beforeFilter 方法执行之后、但在控制器的当前动作执行之前被调用。

  • Component::beforeRender(Controller $controller)

  • 在控制器执行请求的动作逻辑之后、但在控制器渲染视图和布局之前被调用。

  • Component::shutdown(Controller $controller)

  • 在输出被送往浏览器之前被调用。

  • Component::beforeRedirect(Controller $controller, $url, $status=null, $exit=true)

  • 在控制器的 redirect 方法调用时、但在进行任何操作之前被调用。如果该方法返回false,则控制器就不会继续重定向该请求。$url、$status 和 $exit 参数与控制器方法中相应的参数含义相同。也可以返回一个字符串作为重定向的网址,或者返回包括键'url' 和可选的键 'status'、'exit' 的关联数组。