CakePHP Event System (Getting to grips with CakePHP's events system), based on Observer mode

Source: Internet
Author: User

Label:

This article is written about CakePHP 2.x and have been untested with CakePHP 3.x

CakePHP seems to get a slightly unfavourable reputation when compared to the likes of Symfony Orzend Framework due to its Lack of namespaces and not playing nicely with Composer out of the box. However, that'll 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 is pretty common in MVC applications is the Observer pattern, 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 Dependen TS, 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, can has 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 Stack Overflow surrounding it. CakePHP ' s implementation follows the traditional Observer pattern set-up pretty closely:

    • There is subjects, which may a model or a controller
    • Subjects raise Events
    • An observer (or listener) is ' attached ' to subjects and ' listens ' for events to be Rai Sed

So let ' s think of a scenario ...

The scenario

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

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

<?phpclass User extends Appmodel {public    function Aftersave ($created, $options = Array ()) {     if ($created) {
    $email = new Cakeemail ();            $email->to ($this->data[$this->alias][' email ']);            $email->from (' [email protected]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 ();     } }

  

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

So how 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:

<?phpapp::uses (' cakeevent ', ' Event '), class User extends Appmodel {public    function Aftersave ($created, $options = Array ()) {        if ($created) {          $event = new Cakeevent (' Model.User.created ', $this, array (' id ' = = $this->id, ' Data ' = $this->data[$this->alias]));          $this->geteventmanager ()->dispatch ($event);     } }

  

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

Also Note App::uses() The 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 the 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 are the object within that tier ( User ), an d 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 the events being raised, we need code to listen for them.

The first step is to create code to does something when a 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 on the same level as Config, Controller, Model etc. I then name my class after what it ' s doing. For this handler, I ' m going to call it and UserListener save it as userlistener.php.

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

<?phpapp::uses (' Cakeeventlistener ', ' Event '), class Userlistener implements Cakeeventlistener {public    function implementedevents () {     //TODO     }}

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 this sendActivationEmail() method we ' ve specified. This is where we would put the code to being 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 is the CakeEvent instance you raise in your User model. We set some data there (an ID and the current record's data), and that data are now going to available to us in the Instanc E passed to our listener method.

So now we know the 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 (        ' [email protected] ' = ' Your Site ')    );    $email->to ($event->data[' user ' [' email ']);    $email->subject (' Activate your account ');    $email->template (' New_user ');    $email->viewvars (        ' firstName ' + $event->data[' user ' [' first_name '],        ' activationkey ' = > $activationKey    ));    $email->emailformat (' text ');    $email->send ();}

  

The code above is doing the following:

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

And that's all there are to our listener class.

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

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

Attaching the Listener

We now have both out of three, Observer pattern set up:events is being raised, and we have the Code to act O n Raised events; We just need to hook the both together now. This is where CakePHP's documentation just leaves you completely on your own.

One approach is to does this in the app/config/bootstrap.php file. We need to create a 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 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. UserSo if the model fires an event, we class (and any other UserListener listener classes attached to it) would be listen ing for it. neat!

Conclusion

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

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

CakePHP Event Systems (Getting to grips with CakePHP's events system), based on observer mode

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

Tags Index: