首页 > 代码库 > 清晰理解Yii的事件和行为--假如你翻了十多页百度搜索结果还是一无所获的话:)
清晰理解Yii的事件和行为--假如你翻了十多页百度搜索结果还是一无所获的话:)
编程也许真的是个技术活,每次你被概念搞的蒙头转向,无从下手的时候你也许会这么想.但这也就是一会会的事,如果你在晚上喝着茶,咬着牙,僵硬着脖子,一页一页的点百度,然后在笔记上一句话一句话的记下你看到的每一篇文章中看懂了的那一句,我想很快那一句一句的积累会把你带到一个恍然大悟的时刻.这点请你务必相信.但前提是,你翻了十多页的百度看到的相关文章大部分是不重复的.......
总结:上面一段话说了两件事,1柳暗花明是确定是可以常常有的,2百度是确定让人很恼火的.
再总结:上面都是废话,下面开始干货.
让我们先扔下Yii的事件,开始捋一捋思路,假设你自己再写自己的框架,现在想给一个类加一个事件处理程序,你该怎么做?多加个方法?No~,如果其他很多类也都用这个方法,你每个类都写一遍吗?继承?No~,那么多类都继承自一个写有你要的方法的父类?你每个类去改吗?如果别的类本来就有父类呢?require?好聪明,不管程序逻辑判断的结果,先加载进来再说,简单是简单了,可是很笨对吗?所以,还是No.如果当你程序逻辑判断需要一个处理方法的时候,再把这个方法引进来运行一下,不需要的时候就不管,其他任何地方也都可以调用这个方法,那多好是不是?Yii解决这个问题的方式就是事件.
事件应该是个动作吧?点击事件--你很快就能想到这个词,这是js里的,那Yii的事件和这个是不是一样呢?我们结合来看看.首先,js里点击要有个发起的动作吧,那Yii的点击最少也要有个代码段来表示这触发了个动作吧,这是第一.js点击后总要有个处理程序吧?Yii的事件也要有,这是二.除此而外,Yii的事件是为了方便的复用,那总的有个程序段把处理程序和发起类联系到一起吧,这个俗名叫绑定.绑定->触发->处理,很简单是不是?别且,当你昏头专向的时候常常会脱离这个线索的,我们现在要做的是紧紧抓住这根线往下捋.
绑定在Yii中怎么做的呢?先做两个简单的类,A类中有我们想要的处理程序,我们想把他绑定到B类上去
class A {} class B{ function showChange(){ echo ‘changed me‘; }
}
现在A什么都没有,怎么调用B呢?我们在这里假设了一个设定宽度的事件,用Yii的魔术方法来发起动作.
class A{
public $width; public function setWidth($width){ if($this->hasEventHandler(‘onChange‘)){ $this->raiseEvent(‘onChange‘, $event); } $this->width = $width; } }
请注意,setWidth是Yii的魔术方法,当你设定A类的width属性时候会自动调用的.$this->raiseEvent(‘onChange‘, $event);这一句就是核心,当你设定A类的width属性时候,这句话就会去调用一个名叫onChange的方法链,如果这个链上有程序,就会依次执行的.那这个名叫onChange的方法链和上面的B类中的showChange有什么关系呢,只要一个句话就可以把showChange赋给onChange,$j->onChange=array($s,‘showChange‘);,具体是这样的
$j=new A; $s=new B; $j->onChange=array($s,‘showChange‘);
就这样,把B类的showChange方法绑定到了A类的onChange方法链上去了,绑定完了.绑定完了怎么触发呢?出发很简单啊,setWidth是Yii魔术方法啊,只要width有变化就会出发这个方法,然后,raiseEvent就会去找onChange上的所有绑定了的程序,然后挨个执行吗.
$j->width=250;
现在看懂了没有?我们来总结一下,
$j->onChange=array($s,‘showChange‘);是绑定
$this->raiseEvent(‘onChange‘, $event); 是触发
function showChange{} 是执行的程序
如果上面没看懂?建议再看几遍,看到看懂为止,因为下来就要来点花哨的了,还是一样的,上面是按着绑定->触发->执行的条理捋下来的,下面呢就要按上面的内容捋下来,稍微扩展下.我拿一个网上的例子讲,但是他讲的真心没我讲的好懂
第一步先新建一个类,别被类名字唬住,望文生义,就当它是个普通类
class NewCommentEvent extends CModelEvent { public $comment; public $post;}
再建立一个model类,数据来源
class Post extends CActiveRecord { function addComment(Comment $comment){ $comment->post_id = $this->id; $event = new NewCommentEvent($this); $event->post = $this; $event->comment = $comment; $this->onNewComment($event); return $event->isValid; } public function onNewComment($event) { $this->raiseEvent(‘onNewComment‘, $event); }}
下来是处理程序类,就像上一个例子里的B类
class Notifier { function comment($event){ $text = "There was new comment from {$event->comment->author} on post {$event->post->title}"; mail(‘admin@example.com‘, ‘New comment‘, $text); }}
最后还有个类,嗯,类多了点,大概是为了模拟Yii里的真实环境吧,这个类才是主控类
class PostController extends CController{ function actionAddComment() { $post = Post::model()->findByPk(10); $notifier = new Notifier(); $post->onNewComment = array($notifier, ‘comment‘); $comment = new Comment(); $comment->author = ‘Sam Dark‘; $comment->text = ‘Yii events are amazing!‘; $post->addComment($comment); }}
好了,我们从这个主控类PostController开始捋,首先它获取了数据,然后把处理程序类 $notifier实例化了,这个类里有我们要的事件处理程序,然后它把数据来源post类里的onNewComment方法链上挂上了事件处理程序,最后调用了post的addComment方法,传入了一个新的类,名字叫Comment,这段代码没问题了,只有不知道addComment是要干嘛?我们进入到post类里看
post类里的addComment,首先接收了Comment的实例,把这个实例又传给了NewCommentEvent里的Comment属性,然后post把自己也传给了NewCommentEvent里的post属性,然后调用了自己的onNewComment方法
$event = new NewCommentEvent($this); $event->post = $this; $event->comment = $comment; $this->onNewComment($event);
自己的onNewCOmment方法就一句话,里面放了一个触发程序 $this->raiseEvent(‘onNewComment‘, $event);这句话要是放在addComment里,替换掉$this->onNewComment($event);也是完全可以的;
这里就出现了事件里最最容易被忽视然后被忽悠,然后就晕掉了的点,$this->onNewComment($event)中的onNewComment和$this->raiseEvent(‘onNewComment‘, $event)中的onNewCOmment可是两个东西哦!$this->onNewComment($event)中的onNewComment是一个方法,这个方法在post中定义了,而$this->raiseEvent(‘onNewComment‘, $event)中的onNewComment是一个方法链名称;这个方法链上现在绑定是class Notifier中的 comment方法,这个绑定是在主控类的PostController 中 actionAddComment()中做的.
$post->onNewComment = array($notifier, ‘comment‘);
那你要是问为什么onNewComment方法链的这个变量为什么没有见在定义啊,这还是魔术方法_set的效果吗.我把它称为方法链是为了让大家从概念上更好区分一些,其实它就是个变量,里面存储了你绑定的各个处理程序名字,方便以后调用.
另外, raiseEvent(‘onNewComment‘, $event)中的$event是一个类实例,这里的作用是向onNewComment传递参数,这个参数最后被comment方法调用了,其实还可以穿CEvent类及其子类的实例进去,类似于
$j->onChange(new CEvent($this, array(‘statime‘ => $statime, ‘endtime‘ => $endtime)));
//或者
$j->onChange(new CEvent($this));
到这里为止,事件的过程已经捋完了,至于在哪里绑定,在哪里触发,你可以分开来写,也可以一股脑的塞到一个类里,只要做完整这三步就没问题了,现在是不是有点懂了?那赶紧转回去再看一遍啊,懂了这些后,至于行为那就不是问题了,如果有很多类似的处理程序可以放在一个类里,继承自CBehavior,然后你在要用的时候就直接attachBehavior()就可以了,具体参数看看API就知道了,行为就是事件的升级版,可以传多个方法,还可以传属性,一次性传入了一个类,那可以做的东西就多了是吧?
清晰理解Yii的事件和行为--假如你翻了十多页百度搜索结果还是一无所获的话:)