We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

About custom trigger events

I'm Phalcon beginner and i couldnt understand how to trigger custom events.

Example 1: When a user register successfully, trigger "register-user" event

Example 2: When a user update profile, trigger "update-user" event

Example 3: When a user delete, trigger "delete-user" event

Triggers should be trigger on Controllers or Models?

I read many times following document but i couldnt done

https://docs.phalcon.io/en/latest/reference/events.html

Can you give me sample code or idea how to achive this issue?

Thanks

Events can be triggered by any object you'd like. Below is some sample code showing how this might be accomplished. The example assumes the existance of a User class.

Generally when implementing custom events, you'll have at least 3 objects: 1) An event manager (an instance of Phalcon\Events\ManagerInterface). 2) An event "emitter" (often an instance of Phalcon\Events\EventsAwareInterface). 3) One or more event "listeners", which have methods bound to particular events.

In your case, the emitter might look something like this:

class UserController implements Phalcon\Events\EventsAwareInterface
{
    protected $_eventsManager;

    public function setEventsManager(Phalcon\Events\ManagerInterface $manager) 
    {
        $this->_eventsManager = $manager;
    }

    public function getEventsManager() 
    {
        return $this->_eventsManager;
    }

    public function register(User $user) 
    {
        // do registration...
        $this->getEventsManager()->fire("users:registerUser", $this, $user);
    }

    public function updateProfile(User $user) 
    {
        // do update...
        $this->getEventsManager()->fire("users:updateUser", $this, $user);
    }

    public function delete(User $user) 
    {
        // do delete...
        $this->getEventsManager()->fire("users:deleteUser", $this, $user);
    }
}

Your listener might look something like this:

class UserControllerListener
{
    public function registerUser(Phalcon\Events\Event $event, UserController $cntrl, User $user)
    {
        // do stuff...
    }
}

Put it all together:


// Create the event emitter.
$cntrl = new UserController();

// Set the event manager.
$cntrl->setEventsManager(new Phalcon\Events\Manager());

// Create the event listener.
$listener = new UserControllerListener();

// Attach the listener to "users" events.
$cntrl->getEventsManager()->attach("users", $listener);

In this case, if a method with the same name as the event exists, it will be invoked when the event is triggered (e.g. when the users:registerUser event is triggered, the UserControllerListener::registerUser() method is invoked).

An alternative listener implementation might look like this:

class UserControllerListener
{
    public function onUserRegistration(Phalcon\Events\Event $event, UserController $cntrl, User $user)
    {
        // do stuff...
    }
}

In this case, you'll need to directly attach the event to the callback method:


// Create the event emitter.
$cntrl = new UserController();

// Set the event manager.
$cntrl->setEventsManager(new Phalcon\Events\Manager());

// Create the event listener.
$listener = new UserControllerListener();

// Attach the listener to "users:registerUser" events.
$cntrl->getEventsManager()->attach("users:registerUser", [$listener, "onUserRegistration"]);


7.0k
edited Mar '16

I don't like the way phalcon handle events just like above. Though it works, it often baffles me. So after doing some homework, I got the solution below:

//Anywhere in code
… …
$eventmanager = \Phalcon\Di::getDefault()->get('event');
$eventmanager->trigger(new myTestEvent('test'));

myTestEvent object is just value object, containing information relating to this event, the data you want your handler to process.

class myTestEvent
{
    private $data;
    public function __construct($data)
    {
        $this->data = $data;
    }
    public function getData()
    {
        return $this->data;
    }
}

And you eventHandler should something like:

class myTestEventHandler
{
    public function myTestEvent($e,myTestEvent $event)
    {
        var_dump($event->getData());//do anything you like!
    }
}

And you should register you handler in you events.php

<?php
$eventManager = new myEventManager();

$eDomain =’my’;

$eventManager->register($eDomain,[
    myTestEventHandler::class,
//add handler here
]);

return $eventManager;

myEventManager class:

class myEventManager extends \Phalcon\Events\Manager
{

    /**以批量方式来绑定事件
     * @param $eventDomain
     * @param array $handlerClassArray
     */
    public function register($eventDomain, array $handlerClassArray)
    {
            foreach($handlerClassArray as $handler){
                $this->attach($eventDomain,new $handler);
            }
    }

    /**能够处理对象类事件
     * @param $event
     */
    public function trigger($event)
    {
        $eventName = $this->getEventName($event);
        $this->fire($eventName,$event);
    }

      private function getEventName($event)
    {
        return 'my:'.get_class($event);
    }
}

And in you service.php file, you should register this eventmanager like:

$di->set('event',function(){
    return include 'events.php';
},true);

If you like you could implement Façade pattern to make code more succinct.

EventFacade::trigger(new myTestEvent('test'));

For more info for Façade pattern, https://forum.phalcon.io/discussion/10371/borrow-facade-design-pattern-from-laravel



7.0k
edited Mar '16

The main idea is to create a globle event manager, and because event is object, you can easily trace where this event is triggered, by using any IDE.

p.s.: handler's function name should be same as event object classname. One handler class can have many eventhandling functions.



58.1k

Thank you so much for great example!

One more ask for you..

Can you update your answer with folder/files structures to better understand, please.

I couldnt not know which file is which place the folder.

Really thanks again!!

I don't like the way phalcon handle events just like above. Though it works, it often baffles me. So after doing some homework, I got the solution below:

//Anywhere in code
… …
$eventmanager = \Phalcon\Di::getDefault()->get('event');
$eventmanager->trigger(new myTestEvent('test'));

myTestEvent object is just value object, containing information relating to this event, the data you want your handler to process.

class myTestEvent
{
   private $data;
   public function __construct($data)
   {
       $this->data = $data;
   }
   public function getData()
   {
       return $this->data;
   }
}

And you eventHandler should something like:

class myTestEventHandler
{
   public function myTestEvent($e,myTestEvent $event)
   {
       var_dump($event->getData());//do anything you like!
   }
}

And you should register you handler in you events.php

<?php
$eventManager = new myEventManager();

$eDomain =’my’;

$eventManager->register($eDomain,[
   myTestEventHandler::class,
//add handler here
]);

return $eventManager;

myEventManager class:

class myEventManager extends \Phalcon\Events\Manager
{

   /**以批量方式来绑定事件
    * @param $eventDomain
    * @param array $handlerClassArray
    */
   public function register($eventDomain, array $handlerClassArray)
   {
           foreach($handlerClassArray as $handler){
               $this->attach($eventDomain,new $handler);
           }
   }

   /**能够处理对象类事件
    * @param $event
    */
   public function trigger($event)
   {
       $eventName = $this->getEventName($event);
       $this->fire($eventName,$event);
   }

    private function getEventName($event)
   {
       return 'my:'.get_class($event);
   }
}

And in you service.php file, you should register this eventmanager like:

$di->set('event',function(){
   return include 'events.php';
},true);

If you like you could implement Façade pattern to make code more succinct.

EventFacade::trigger(new myTestEvent('test'));

For more info for Façade pattern, https://forum.phalcon.io/discussion/10371/borrow-facade-design-pattern-from-laravel

There are globally eventsManager in di, you can set eventsManager for class or in di and use it.



7.0k
edited Mar '16

Thanks. Just confirmed, there is a event Manager in Di, https://docs.phalcon.io/en/latest/reference/di.html#factory-default-di.

Service Name Description Default Shared
eventsManager Events Management Service Phalcon\Events\Manager Yes

And this would make it a little duplicate. So maybe I should change the code in services.php as below:

$di->setShared('eventsManager',function(){
    return include 'events.php';
});

Do not if this would cause any other problem??

edited Mar '16

Thanks. Just confirmed, there is a event Manager in Di, https://docs.phalcon.io/en/latest/reference/di.html#factory-default-di.

Service Name Description Default Shared
eventsManager Events Management Service Phalcon\Events\Manager Yes

And this would make it a little duplicate. So maybe I should change the code in services.php as below:

$di->setShared('eventsManager',function(){
   return include 'events.php';
});

Do not if this would cause any other problem?? No, you just can make:

$di->setShared('eventsManager',function(){
    $eventsManager=new Manager();
    $eventsManager->attach('what:event',new ListenerClass());
    return $eventsManager;
});

And somewhere in code:

Di::getDefault()->get('eventsManager')->fire('what:event');

And that's it, you only need to create listenerlcass and that's it, no need to create new classes etc etc.

If you need to pass data with fire then you can pass it as third parameter in fire method, and access it in listener in third parameter.



7.0k

Thanks, for your quick reply. Your code is better and succinct. However, if you have many events and eventsHandler, and fire tens of times, then you would find the code is little ugly. Hard to trace how many time a event is fired. I learned the event class method from Jeffrey.https://laracasts.com/series/commands-and-domain-events. Though the video is for laravel, it should apply to phalcon.

Thanks. Just confirmed, there is a event Manager in Di, https://docs.phalcon.io/en/latest/reference/di.html#factory-default-di.

Service Name Description Default Shared
eventsManager Events Management Service Phalcon\Events\Manager Yes

And this would make it a little duplicate. So maybe I should change the code in services.php as below:

$di->setShared('eventsManager',function(){
   return include 'events.php';
});

Do not if this would cause any other problem?? No, you just can make:

$di->setShared('eventsManager',function(){
  $eventsManager=new Manager();
  $eventsManager->attach('what:event',new ListenerClass());
  return $eventsManager;
});

And somewhere in code:

Di::getDefault()->get('eventsManager')->fire('what:event');

And that's it, you only need to create listenerlcass and that's it, no need to create new classes etc etc.

If you need to pass data with fire then you can pass it as third parameter in fire method, and access it in listener in third parameter.

What you mean about tracing ? Like in code how many times it's used to fire ? Or something else ?



7.0k

What you mean about tracing ? Like in code how many times it's used to fire ? Or something else ?

Sorry for my vague expression, as not a native English speaker, It’s hard to avoid.

As to “event tracing”, I mean: using event object can help me:

1, easily count how many custom events I have in the app, where these events are defined, and where fired.

2,event object ,as a value object, can encapsulate what event handler needs, other parameters should not be needed when event is fired. This make me think more clear.

3,register method makes event binding better, easily to change.

<?php
$eventManager = new myEventManager();

$eDomain = \Phalcon\Di::getDefault()->get('config')->application->eventPrefix;

$eventManager->register($eDomain,[
    notificationHandler::class,
    searchLoghandler::class,
    authEventsHandler::class,
    cacheEventsHandler::class,
]);

return $eventManager;
edited Apr '16

Oh, so you mean add ability to fire to pass just array of listeners ? Or what ?

Which events are prompted that you need to prevent. Components, Plugins, and Controllers may be occasion listeners through creating methods in their elegance for blog article. To save you those events from being listened to, clearly removed that technique.