Event Management

From Cloudrexx Development Wiki
Jump to: navigation, search

Cloudrexx provides an event system which allows the execution of code in the event of predefined state changes. Every component is permitted to register any number of events to which any component is permitted to listen to and run code on.

Note: This article covers service side events (PHP). For client-side events seee JavaScript Framework Events.


Introduction

The event system is managed by a single instance of \Cx\Core\Event\Controller\EventManager (one per instance of \Cx\Core\Core\Controller\Cx) and provides the following functionality:

List of Events

Passive Events

Listen to those events to inject custom code.

Component Event Event Data Notes Known Triggers
Search SearchFindContent
[$search]
$search is an instance of \Cx\Core_Modules\Search\Controller\Search Implement Full-Text search
MediaBrowser MediaBrowser.Plugin:initialize - - Add view to MediaBrowser
MailTemplate MailTemplate.Mail:addAddress
[
    'kind' => <to/cc/bcc>,
    'address' => <e-mail>,
    'name' => <recipient_name>,
    'mail' => $mail,
]
$mail is the instance of \Cx\Core\MailTemplate\Model\Entity\Mail on which the recipient is being added Catch recipient before email is dispatched.
DataSource preDistantEntityLoad
[
    'targetEntityClassName' => <entity>,
    'targetId' => <id>,
]
- Allows to interfere the loading process of associated entities when fetching over the RESTful API.
MediaSource mediasource.load
[$mediaSourceManager]
$mediaSourceManager is the instance of \Cx\Core\MediaSource\Model\Entity\MediaSourceManager that triggered the event Add a MediaSource
Html Html.ViewGenerator:initialize
['options' => &$options]
$options are the ViewGenerator options on initialization Allows for customizing of Autogenerated Views.
View View.Sigma:loadContentDeprecated
[
    'content' => &$string,
    'template' => $sigma,
]
$sigma is the instance of \Cx\Core\Html\Sigma that triggered the event. Allows to modify the content of a Template Block before being processed.
View View.Sigma:setVariableDeprecated
[
    'content' => &$val,
    'template' => $sigma,
]
$sigma is the instance of \Cx\Core\Html\Sigma that triggered the event. Allows to modify the value of a Template Placeholder on assignement.
View View.Sigma:loadFileDeprecated
['filepath' => &$filepath]
$filepath is the path to the template file to be loaded. Allows to modify the path to a Template File before being loaded.
Core preComponent
[
    'componentName' => <name>,
    'component' => <component>,
    'hook' => <hook>,
]
<hook> will be one of the available Component Hooks. Use to inject code before the executing of other components' hooks.
postComponent Use to inject code after the executing of other components' hooks.

Active Events

Trigger the following events to perform certain operations.

Component Event Argument Definition Purpose
SysLog SysLog/Add
[
    'severity' => '<type>',
    'message' => '<log message>',
    'data' => <data>,
]
Use to add a System Log entry.
Cache clearEsiCache
[
    'Widget',
    [ <widget>, ...],
]
Use to flush the cache of one or multiple ESI-Widget at once.
Event model/prePersist See Model Events Use to manually trigger a model event on a model.
model/postPersist
model/preUpdate
model/postUpdate
model/preRemove
model/postRemove
model/onFlush
model/postFlush

Internal Events

The following events are internal and do therefore not provide any external purpose.

Component Event
ContentManager wysiwygCssReload

Model Events

The following pre-defined events are being triggered (in the listed order, from top to bottom) on all entities that are instances of \Cx\Model\Base\EntityBase in the event of flushing the entity manager[1]:

# Event Point in time Listener Invocations DB Transaction State
model/prePersist When $this->cx->getDb()->getEntityManager()->persist($entity) is being called to persist a newly created entity.
Note: This event is also being triggered on entities that are subject to a cascade persist operation.
Once for every newly added entity. Fetch the newly added entity in the event listener through $eventArgs[0]->getEntity(). None
model/preRemove When $this->cx->getDb()->getEntityManager()->remove($entity) is being called to delete the entity.
Note: This event is also being triggered on entities that are subject to a cascade remove operation.
Once for every deleted entity. Fetch the deleted entity in the event listener through $eventArgs[0]->getEntity().
model/onFlush Only once per model. If multiple entities of a certain model are being flushed, then the registered event listeners are only being called once.
BEGIN
model/postPersist After the newly added entity has been added in the database. Once for every newly added entity. Fetch the newly added entity in the event listener through $eventArgs[0]->getEntity(). Active
model/preUpdate Before the changes of the modified entity are being sent to the database. Once for every updated entity. Fetch the updated entity in the event listener through $eventArgs[0]->getEntity().
model/postUpdate After the changes of the modified entity have been sent to the database. Once for every updated entity. Fetch the updated entity in the event listener through $eventArgs[0]->getEntity().
model/postRemove After the deleted entity has been removed from the database. Once for every deleted entity. Fetch the deleted entity in the event listener through $eventArgs[0]->getEntity().
COMMIT
model/postFlush After the database transaction has been committed successfully.
Note: This event is never being triggered in case of an issue/exception that happens during the active DB-transaction state. Instead a ROLLBACK command will be sent to the database.
Only once per model. If multiple entities of a certain model have been flushed, then the registered event listeners are only being called once. None
Note: The model event system of Cloudrexx differs in the behaviour from the underlying event system of Doctrine in such way, that the above listed model events do only trigger the registered event listeners in case of an actual change on the target model. Whereas a listener that has been registered directly on a Doctrine event[2] will always be called, independently of the target model.
Note: The Doctrine model event model/preFlush is not supported/implemented as a Cloudrexx model event as it can not be bound to a specific model. There are currently no intentions to implement it. However it is possible to listen to the Doctrine event directly[3]
Footnotes
  1. The entity manager can be flushed as follows:
    $this->cx->getDb()->getEntityManager()->flush();
    
  2. Listening directly on a Doctrine event is not advised. However it can be done as follows:
    $this->cx->getDb()->getEntityManager()->getEventManager()->addEventListener(
        $event, // see lib/doctrine/Doctrine/ORM/Events.php for possible values
        new \Cx\Modules\MyComponent\Model\Event\MyEventListener()
    );
    
  3. Listening directly to the Doctrine event model/preFlush is not advised. However it can be done as follows:
    $this->cx->getDb()->getEntityManager()->getEventManager()->addEventListener(
        \Doctrine\ORM\Events::preFlush,
        new \Cx\Modules\MyComponent\Model\Event\MyEventListener()
    );
    

Entity Modification During Flush

At some degree it is possible to perform entity operations within the execution of a model event. For restrictions and implementation please refer to the Doctrine ORM Event documentation.

Integration

The instance of the EventManager can be fetched as follows:

$eventManager = $this->cx->getEvents();

Register Event

In order to be able to trigger and register a listener to an event, the event first has to be registered as follows:

$this->cx->getEvents()->addEvent('MyComponent:myEvent');
Note: Please refer to the Naming Conventions on how to properly name your events.

According to the guidelines all events should get registered in the component hook registerEvents. Therefore, do extend your ComponentController as follows:

/**
 * @{inheritdoc}
 */
public function registerEvents() {
    $this->cx->getEvents()->addEvent('MyComponent:myEvent');
}

Register Listener

In order to listen to an event, you first need to implement a new class that will act as your event listener as follows:

<?php
namespace \Cx\Modules\MyComponent\Model\Event;

class MyComponentEventListener extends \Cx\Core\Event\Model\Entity\DefaultEventListener {
    public function myComponentMyEvent($args) {
        // add your code here
    }
}

Implementation details:

  • The class must extend \Cx\Core\Event\Model\Entity\DefaultEventListener
  • If you use a separate file for your new class, then it must be located in the folder /Event of your component
  • For each event you want to listen to, you need to implement a method as follows:
    • Set method scope to protected
    • Set method name based on the event to listen to. The scheme of an event (<component>[.<entity>]:<action>) gets converted to its corresponding method name as follows: <lcfirst:component>[<ucfirst:entity>]<ucfirst:action> Examples:
      Event name Corresponding method name
      MyComponent:myEvent myComponentMyEvent()
      MyComponent.MyModel:myEvent myComponentMyModelMyEvent()
    • Method must accept one single argument
  • Example:
    protected function myComponentMyEvent($eventArgs) {
        // add your code here
    }
    

When an event (your event listener is listening to) is triggered, the associated method (in our case myComponentMyEvent()) will be called and the argument from the triggerEvent-call (in our case $myEventArguments) will be passed along.

By extending from \Cx\Core\Event\Model\Entity\DefaultEventListener your event listener automatically also provides the following handy shortcuts:

Shortcut Notes
$this->getComponent($name) Fetch any component by $name
$this->cx Fetch current instance of \Cx\Core\Core\Controller\Cx
Note: Alternatively, you could also write your event listener from scratch by implementing the interface \Cx\Core\Event\Model\Entity\EventListener instead of extending from \Cx\Core\Event\Model\Entity\DefaultEventListener. In such case you'll have to implement a public method onEvent() that accepts two arguments as follows:
public function onEvent($eventName, array $eventArgs) {
    // insert your code here
}

Finally, your event listener must be registered as such. According to the guidelines this should be done in the component hook registerEventListeners. Therefore, do extend your ComponentController as follows:

/**
 * @{inheritdoc}
 */
public function registerEventListeners() {
    $myEventListener = new \Cx\Modules\MyComponent\Model\Event\MyComponentEventListener($this->cx);
    $this->cx->getEvents()->addEventListener(
        'MyComponent:myEvent',
        $myEventListener
    );
}
Note: One and the same instance of an Event Listener can be used to listen to multiple events.

Model Listeners

In order to listen to a model state change event, add an event listener as follows:

/**
 * @{inheritdoc}
 */
public function registerEventListeners() {
    $myEventListener = new \Cx\Modules\MyComponent\Model\Event\MyComponentEventListener($this->cx);
    $this->cx->getEvents()->addModelListener(
        // One of the available model events
        'model/prePersist',
        // FQCN of an EntityBase model with escaped backslashes
        'Cx\\Core\\ContentManager\\Model\\Entity\\Page',
        $myEventListener
    );
}
Note: See Model Events for a list of available model events.

Trigger Event

To trigger an event, simply do as follows:

$this->cx->getEvents()->triggerEvent(
    'MyComponent:myEvent',
    [$myEventArguments]
);
Important: $myEventArguments must be enclosed as an array element. If $myEventArguments is already an array, it must still be enclosed as an array element.
Note: Model Events are being triggered automatically.