组件机制,是Yii整个体系的思想精髓,在使用Yii之前,最应该先了解其组件机制,如果不了解这个机制,那么阅读Yii源代码会非常吃力。组件机制给Yii框架赋予了无穷的灵活性和可扩展性,可以毫不夸张地说,Yii框架的基础结构就是组件。大到Capplication对象、控制器、路由管理器(urlManager),小到一些其它插件,均是以组件形式存在的。
什么是Yii组件?
Yii中几乎所有可实例化并继承自CComponent的类,均可称为组件。
组件的特点是什么?
继承自CComponent类(直接继承或间接继承),拥有事件及行为机制,可在配置文件中定义其各个属性。
如何创建一个组件?
编写自定义类,并继承自CComponent类即可。
CComponent类是所有组件的基类,这个类在Yii框架中至关重要。具体来说,这个类主要实现以下三大功能:
1.通过利用php的魔术方法__set,__get实现定义类的属性。也就是说一个组件的属性除了使用已经定义过的public成员属性,还可以利用CComponent实现的功能,通过扩展setXXX,getXXX方法扩展属性的设置和获取,对一些特殊的属性,我们可能希望在设置它之时就验证其是格式正确,此时就比较有用。classwebpageextendsCComponent{
public$title;
PRivate$_url;
publicfunctionsetUrl($value=''){
if(is_url($value)){
$this->_url=$value;
}
}
publicfunctiongetUrl(){
return$this->_url;
}
}
$page=newwebpage();
$page->title="pagetitle";
$page->url="/index.php";#call$page->seturl("/index.php");
echo$page->url#$page->geturl();
也就是说,如果一个组件定义了setXXX,getXXX,那么就可以在类外部使用普通的属性访问形式。
2.同样利用setter,getter实现事件处理接口绑定。事件机制在Yii中也是无处不大,Yii使用大量的事件机制来实现组件之间的功能调用(观察者模式)。
那么,如何给组件定义一个事件呢?Yii规定onXX形式的方法,即称为事件,如以下定义:classformextendsCComponent{
publicfunctiononSubmit($event){
$this->raiseEvent('onSubmit',$event);
}
}
使用以上固定代码,就给car组件创建了一个onStop事件。但是绑定事件究竟有何用处呢?绑定事件的作用就是当组件产生一系列事件时,将自己的事件通过事件处理器,通知到绑定的各个对象上。例如我们希望当表单提交的时候,将此事件通知给日志组件让其记录之。
$form=newform();
$form->attachEventHandler('onSubmit',array($logOjbect,"saveLog"));
$form->data=$_POST;
$form->onSubmit(newCEvent($form,array('data'=>$_POST)));#激活事件执行,并调用事件处理接口logObject::saveLog
而logObject的代码可能如下:classlogObject{
publicfunctionsaveLog($event){
$event->sender===$form;
$event->params;
}
}
显然这种方式远比传统方式调用更灵活,概念方式上也更先进一些。
另外,可以使用setter方法绑定事件:
$form->onSubmit=array($logOjbect,"saveLog");
注意:form类并未定义onSubmit成员属性。
同时,Yii实现了同一个事件绑定多个处理接口的机制,类似javaScript中的addEventListener。
事件处理器接口柳芽,以php的callback类型格式为标准,详情参阅以下内容:http://php.net/manual/en/language.types.callable.php
如Yii的CLogRouter::init()中的代码:Yii::app()->attachEventHandler('onEndRequest',array($this,'processLogs'));
当然也可以写成Yii::app()->onEndRequest=array($this,'processLogs');
而CApplication中定义了onEndRequest事件:publicfunctiononEndRequest($event){
if(!$this->_ended){
$this->_ended=true;
$this->raiseEvent('onEndRequest',$event);
}
}
定义了事件,并给事件绑定了处理器还不够,还得在合适的地方激活事件,如CApplication::run()方法中的逻辑:
publicfunctionrun(){
if($this->hasEventHandler('onBeginRequest'))
$this->onBeginRequest(newCEvent($this));
$this->processRequest();