组件
组件是在多个控制器中共享的逻辑包。如果你发现自己想要在控制器间复制粘贴某些东西时,你就应该考虑将一些功能包装在一个组件中了。
CakePHP 还配备了一套漂亮的、对你有用的核心组件:
这些组件的详细信息都在各自的章节中。 现在,我们将向你展示如何建立你自己的组件。创建组件可以保持控制器代码整洁,并且允许你在多个项目中重用代码。
配置组件
一些核心组件需要配置。需要配置的组件有授权、Cookie和电子邮件组件等。对于一般的组件,通常在$components数组或者控制器的beforeFilter方法中进行配置:
1 class PostsController extends AppController {2 public $components = array(3 'Auth' => array(4 'authorize' => array('controller'),5 'loginAction' => array('controller' => 'users', 'action' => 'login')6 ),7 'Cookie' => array('name' => 'CookieMonster')8 );
这是使用$components数组配置组件的例子。所有的核心组件都允许使用这种方式进行配置。此外,你也可以在控制器的beforeFilter()方法中配置组件。这种方式通常用在你需要将一个函数的结果赋与一个组件属性的情况下。上面的例子还可以表示成:
1 public function beforeFilter() {2 $this->Auth->authorize = array('controller');3 $this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');4 5 $this->Cookie->name = 'CookieMonster';6 }
然而,也有这种可能:一个组件的特定配置选项要在控制器的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.php11 App::uses('AuthComponent', 'Controller/Component');12 class MyAuthComponent extends AuthComponent {13 // Add your code to override the core AuthComponent14 }
上例的控制器中$this->Auth的别名为MyAuthComponent。
注解
在任何用到有别名的组件时,都要使用别名,包括在其它组件内引用。
使用组件
一旦你已经在控制器中包含了一些组件,用起来是非常简单的。在控制器中每个元件都以属性的方式使用。如果你已经在控制器中加载了SessionComponent和theCookieComponent,你就可以像下面这样访问它们:
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 $this->redirect(array('action' => 'index'));8 }9 }
注解
由于以属性身份加入到控制器中的模型和组件共享相同的 ‘命名空间’,你需要确保不给组件和模型相同的命名。
运行中加载组件
你也许不需要所有的组件在每个控制器方法中都可用。这种情况下,你可以在运行时使用ComponentCollection加载一个组件。 在控制器内部,你可以按如下方式进行:
1 $this->OneTimer = $this->Components->load('OneTimer');2 $this->OneTimer->getTime();
组件回调
组件也提供一些请求生命周期回调,以允许它们延伸请求周期。有关组件提供的回调的更详细信息,请参阅组件 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数组的方式在应用程序控制器中使用它了(参见“组件” 部分)。控制器将自动提供一个用组件命名的新属性,通过这个属性我们可以访问组件的实例:
1 3 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 // the other component your component uses 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.php17 App::uses('Component', 'Controller');18 class ExistingComponent extends Component {19 20 public function foo() {21 // ...22 }23 }
组件API
- classComponent
组件基类为通过ComponentCollection延迟加载其它组件以及处理公共设置提供了几个方法。它还为所有的组件回调提供了属性。
- Component::__construct(ComponentCollection$collection,$settings =array())
组件基类构造函数。作为公共属性的所有$settings也将有与settings内设置的值匹配的值。
回调
- Component::initialize(Controller$controller)
initialize 方法在控制器的 beforeFilter 方法之前被调用。
- Component::startup(Controller$controller)
startup 方法在控制器的 beforeFilter 之后但在控制器执行当前动作处理之前被调用。
- Component::beforeRender(Controller$controller)
beforeRender 方法在执行请求动作逻辑之后,控制器渲染视图和布局之前被调用。
- Component::shutdown(Controller$controller)
shutdown 方法在输出传送给浏览器之前被调用。
- Component::beforeRedirect(Controller$controller,$url,$status=null,$exit=true)
beforeRedirect方法在控制器跳转方法被调用之后,所有其它方法调用之前被调用。如果这个方法返回假,将不再继续完成请求的转向。$url、$status 和$exit 变量对于控制器方法的意义相同。你还能返回一个字符串,作为转向的 url,或者返回带有键 ‘url’ 的关联数组,此数组的‘status’ 和 ‘exit’ 元素是可选的。