博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
cakephp 的事件系统(Getting to grips with CakePHP’s events system), 基于观察者模式...
阅读量:6229 次
发布时间:2019-06-21

本文共 7927 字,大约阅读时间需要 26 分钟。

 This article was written about CakePHP 2.x and has been untested with CakePHP 3.x

 

 seems to get a slightly unfavourable reputation when compared to the likes of  or due to its lack of namespaces and not playing nicely with  out of the box. However, that will change in the forthcoming version 3; and CakePHP 2 still remains a pretty easy PHP framework to work with and quickly build web applications with.

A design pattern that is pretty common in MVC applications is the , colloquially known as event handlers. From the Wikipedia entry, it’s defined as:

The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.

So plainly put: when something changes in your application, you can have code somewhere else that does something too. This makes for a better separation of concerns and more modular code that’s easier to maintain.

The events system in CakePHP

CakePHP comes with a built-in events system but it’s poorly documentated, and not the most straightforward of things based on the number of questions on  surrounding it. CakePHP’s implementation follows the traditional Observer pattern set-up pretty closely:

  • There are subjects, which may be a model or a controller
  • Subjects raise events
  • An observer (or listener) is “attached” to subjects and “listens” for events to be raised

So let’s think of a scenario…

The scenario

A website that accepts user registrations. When a user registers an account is created for them, but is initially inactive. A user has to activate their account by clicking a link in an email.

One approach would be just to put the code that sends the activation email in the User model itself:

to($this->data[$this->alias]['email']); $email->from(array( 'noreply@example.com' => 'Your Site' )); $email->subject('Activate your account'); $email->format('text'); $email->template('new_user'); $email->viewVars(array( 'user' => $this->data[$this->alias] )); $email->send(); } }

  

But this is mixing concerns. We don’t want the code that sends the activation email in our User model. The model should just deal with retrieving, saving, and deleting User records.

So what can we do? We can implement the Observer pattern.

Raising events

First we can remove the email sending code from our afterSave() callback method, and instead raise an event:

$this->id, 'data' => $this->data[$this->alias] )); $this->getEventManager()->dispatch($event); } }

  

As you can see, our afterSave() method is now much leaner.

Also note the App::uses() statement added to the top of the file, to make sure the CakeEvent class is imported. We’re creating an instance of the CakeEvent event class, passing it an event name ("Model.User.created"), a subject ($this), and some data associated with this event. We want to pass the newly-created record’s ID, and the data of this record.

With event names in CakePHP, it’s recommended to use pseudo name-spacing. In the above example, the first portion is the tier (Model), the second portion is the object within that tier (User), and the third portion is a description name of the event (created). So we know from the event name that it’s when a new user record is created.

Creating a listener

Now we have events being raised, we need code to listen for them.

The first step is to create code to do something when an event is raised. This is where CakePHP’s documentation starts getting hazy. It provides sample code, but it doesn’t tell you where to actually put it. I personally created an Event directory at the same level as ConfigControllerModel etc. I then name my class after what it’s doing. For this handler, I’m going to call itUserListener and save it as UserListener.php.

Event listeners in CakePHP implement the CakeEventListener interface, and as a result need to implement one method calledimplementedEvents(). The skeleton code for the listener class then looks like this:

  The implementedEvents() method expects an associative array mapping event names to methods that should handle such events. So let’s flesh that out with the one event we’re raising:

public function implementedEvents() {    return array(        'Model.User.created' => 'sendActivationEmail'    );}

  

Simples.

So now, we need to actually create that sendActivationEmail() method we’ve specified. This is where we would put the code to be ran when a user is created.

public function sendActivationEmail(CakeEvent $event) {    // TODO}

  

The method is passed one argument: an instance of CakeEvent. In fact, this would be the CakeEvent instance you raise in yourUser model. We set some data there (an ID and the current record’s data), and that data is now going to available to us in the instance passed to our listener method.

So now we know what we’re getting, let’s flesh our listener method out some more with that email sending code:

public function sendActivationEmail(CakeEvent $event) {    $this->User = ClassRegistry::init('User');        $activationKey = Security::generateAuthKey();        $this->User->id = $event->data['id'];    $this->User->set(array(        'active' => false,        'activation_key' => $activationKey    ));    $this->User->save();        $email = new CakeEmail();    $email->from(array(        'noreply@example.com' => 'Your Site'    ));    $email->to($event->data['user']['email']);    $email->subject('Activate your account');    $email->template('new_user');    $email->viewVars(array(        'firstName' => $event->data['user']['first_name'],        'activationKey' => $activationKey    ));    $email->emailFormat('text');    $email->send();}

  

The code above is doing the following:

  • Creating an instance of the User model, as we don’t initially have it available in our listener class
  • Generating an activation key for the user
  • Setting the activation key for the user in the database, whose ID we get from the event raised
  • Sending the activation email, with our generated activation key

And that’s all there is to our listener class.

Because we’re using the CakeEmail and Security classes in CakePHP, it’s a good idea to make sure they’re loaded. At the top of the file, add these two lines:

 

App::uses('CakeEmail', 'Network/Email'); App::uses('Security', 'Utility');

 

Attaching the listener

We now have two out of three components in our Observer pattern set up: events are being raised, and we have code to act on raised events; we just need to hook the two together now. This is where CakePHP’s documentation just leaves you completely on your own.

One approach is to do this in the app/Config/bootstrap.php file. We need to create an instance of our event listener class and attach it to the User model using its event manager.

The code is simple. At the bottom of your bootstrap.php add the following code:

App::uses('ClassRegistry', 'Utility');App::uses('UserListener', 'Event');$user = ClassRegistry::init('User');$user->getEventManager()->attach(new UserListener());

  

As you can see, we’re using CakePHP’s ClassRegistry utility class to load the User model; and then using the User model’s event manager to attach our UserListener class. So now when the User model fires an event, our UserListener class (and any other listener classes attached to it) will be listening for it. Neat!

Conclusion

Hopefully you can see the merits of the Observer pattern. This is just one example; there are many other use cases where this pattern would be appropriate. Hopefully this blog post will demystify CakePHP’s implementation of this design pattern and you can find areas in your own applications where you can apply it yourself.

If you do use CakePHP’s events system in your own applications, then I’d love to see your implementations and the problems you solved using it.

转载地址:http://gyxna.baihongyu.com/

你可能感兴趣的文章
【云服务月刊】2018年第1期:阿里云客户服务部总经理张颖杰:用心聆听,服务见智...
查看>>
99%的Java程序员都不知道的Spring中的@Transactional注解的坑
查看>>
堆排序 Heap Sort
查看>>
golang map 底层部分理解
查看>>
3.22(终)
查看>>
第61节:Java中的DOM和Javascript技术
查看>>
排名前十的程序员应用软件曝光,你有用过吗?
查看>>
关于android中监控u盘插入与拔出的困惑与思考
查看>>
Golang 并发爬虫 爬取某著名游戏媒体
查看>>
java(1)
查看>>
支持向量机(Support Vector Machine)
查看>>
react native FlatList内嵌自己的Component不刷新的处理
查看>>
spring boot 加载过程分析--ConfigurationClassPostProcessor
查看>>
Python基础教程,第九讲,异常处理
查看>>
再谈MV*(MVVM MVP MVC)模式的设计原理—封装与解耦
查看>>
一看就会的 egret 入门教程
查看>>
大型互联网 b2b b2c o2o 电子商务微服务云平台
查看>>
Flutter之可滑动Widget
查看>>
富文本编辑器-CKeditor5
查看>>
前端基础22:数组迭代基本方法
查看>>