Skip to content

Event Management

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.
NetManager NetManager:getSenderDomains
[
'usages' => bool $usages,
]
If $usages is set to true, then the event listener must return usages according to the format specific to the sender domain validation. Collect used email sender domains.
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.
Core postComponent
[
'componentName' => <name>,
'component' => <component>,
'hook' => <hook>,
]
<hook> will be one of the available Component Hooks. Use to inject code after the executing of other components' hooks.
News News::rssFeedWritten
[
'langId' => <langId>,
'newsList' => <array>,
]
The event is triggered for each locale separately. <newsList> will be a list of news entries. These are simple lists of properties. Use to push a news feed to a third-party.
Cache Cache.Cachefile:outdateIndex
[
'component' => <component>,
'cacheFile' => <cacheFile>,
'index' => <index>,
]
The event is triggered for each expired entry when fetching data from the Data cache. <cacheFile> is an instance of Cx\Core_Modules\Listing\Model\Entity\DataSet holding the whole Data cache the expired entry belongs to. <index> is a string representation of the identifier of the expired entry. Use to extend expired Data cache entries by returning a new DateTime object (that points to a future date) to prevent removal of the expired entry. Returned structure must be as folows:
[
'validUntil' => new \DateTime(),
]

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
model/postPersist
model/preUpdate
model/postUpdate
model/preRemove
model/postRemove
model/onFlush
model/postFlush
See Model Events Use to manually trigger a model event on a model.

Internal Events

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

Component Event
ContentManager wysiwygCssReload

Model Events

In the event of flushing the entity manager1, the following pre-defined events are being triggered (in the listed order) on all affected entities that are instances of \Cx\Model\Base\EntityBase:

Model Events

Event Point in time Listener Invocations2 Event Arguments
model/prePersist On new entities that have been persisted using $this->cx->getDb()->getEntityManager()->persist($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->getEntity(). \Doctrine\ORM\Event\LifecycleEventArgs
model/preRemove On entities that have been removed using $this->cx->getDb()->getEntityManager()->remove($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->getEntity(). \Doctrine\ORM\Event\LifecycleEventArgs
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. \Doctrine\ORM\Event\OnFlushEventArgs
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->getEntity(). \Doctrine\ORM\Event\LifecycleEventArgs
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->getEntity(). \Doctrine\ORM\Event\PreUpdateEventArgs
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->getEntity(). \Doctrine\ORM\Event\LifecycleEventArgs
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->getEntity(). \Doctrine\ORM\Event\LifecycleEventArgs
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. \Doctrine\ORM\Event\PostFlushEventArgs

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 event3 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 directly4

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:

1
2
3
4
5
6
/**
 * @{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:

1
2
3
4
5
6
7
8
<?php
namespace \Cx\Modules\MyComponent\Model\Event;

class MyComponentEventListener extends \Cx\Core\Event\Model\Entity\DefaultEventListener {
    public function myComponentMyEvent($eventArgs) {
        // 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
    • Method must accept one single argument
    • Set method name according to Listener method naming

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

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.

Listener method naming

Listener method names are derived from their associated event names. The conversion occurs from the event name scheme (<component>[.<entity>]:<action>) the method name <lcfirst:component>[<ucfirst:entity>]<ucfirst:action>. Examples:

Event name Corresponding method name
MyComponent:myEvent myComponentMyEvent()
MyComponent.MyModel:myEvent myComponentMyModelMyEvent()

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',
        \Cx\Core\ContentManager\Model\Entity\Page::class,
        $myEventListener
    );
}

Note

See Model Events for a list of available model events.

From Scratch

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:

1
2
3
public function onEvent($eventName, array $eventArgs) {
    // insert your code here
}

Warning

The actual event data is accesible through $eventArgs[0].

Trigger Event

To trigger an event, simply do as follows:

1
2
3
4
$this->cx->getEvents()->triggerEvent(
    'MyComponent:myEvent',
    [$myEventArguments]
);

Warning

$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.


  1. The entity manager can be flushed as follows:

    $this->cx->getDb()->getEntityManager()->flush();
    
     

  2. This only applies when extending \Cx\Core\Event\Model\Entity\DefaultEventListener. When implementing a listener from scratch then this depends on your implementation of the onEvent() method. 

  3. Listening directly on a Doctrine event is not advised. However it can be done as follows:

    1
    2
    3
    4
    $this->cx->getDb()->getEntityManager()->getEventManager()->addEventListener(
        $event, // see lib/doctrine/Doctrine/ORM/Events.php for possible values
        new \Cx\Modules\MyComponent\Model\Event\MyEventListener()
    );
    
     

  4. Listening directly to the Doctrine event model/preFlush is not advised. However it can be done as follows:

    1
    2
    3
    4
    $this->cx->getDb()->getEntityManager()->getEventManager()->addEventListener(
        \Doctrine\ORM\Events::preFlush,
        new \Cx\Modules\MyComponent\Model\Event\MyEventListener()
    );