Since version 3.6.0 BEdita is shipped with a basic event system, along with a callback manager that enables the binding of model events simply plugging callback behaviors, without the need to edit the model configuration itself, hence leaving core code intact.

Automatic callback behaviors

By giving a special name to your behaviors, you can attach listeners to some built-in CakePHP's model events without editing the core code. Such callback system is handled by the CallbackBehavior which is automatically attached to every BEObject in "callback manager" mode.

CallbackBehavior configuration

CallbackBehavior has an optional setting callbackManager that allows you to decide if you want to attach your behaviors' methods as listeners for events handled by BeCallbackManager (see below for detailed documentation) or you prefer to use standard CakePHP behaviors system. By default, BeCallbackManager will be used.

Adding custom callbacks

Let's say you need to execute a custom piece of code every time an Event is saved. Normally, you would create a behavior and then add the newly-born behavior to the Event::$actsAs attribute, but this would involve the editing of a piece of source code.

Thanks to CallbackBehavior you can attach your behavior automatically to the Event model by naming it EventDoSomethingCallbackBehavior (where DoSomething can be replaced by anything you wish), and then enable it as an add-on, or place it within an enabled module.

This way, CallbackBehavior will take care of attaching your custom behaviors following such naming convention to the respective models, and the event listeners they implement will be correctly triggered.

Unbinding custom callbacks

If you chose to attach CallbackBehavior with callbackManager set to true, you might also decide to unbind single listeners. For example, if you had a custom callback behavior like this:

class EventTestingCallbackBehavior extends ModelBehavior {
    public function afterSave() {
        $this->log('afterSave', 'callback');
    }
    public function afterDelete() {
        $this->log('afterDelete', 'callback');
    }
}

If later on you wanted to get rid of its afterDelete() callback while doing a specific operation, you might call:

BeLib::getObject('BeCallbackManager')->unbind('Event.AfterDelete', array(
    'EventTestingCallbackBehavior', 'afterDelete'
));

This way, the EventTestingCallbackBehavior::afterDelete() method would be detached from the Event.AfterDelete event. Please, read BeCallbackManager::unbind() documentation for further details.

Adding callbacks on-the-fly

If you chose to attach CallbackBehavior with callbackManager set to true, you might also decide to add custom listeners on the fly to model events. For example, if you attached CallbackBehavior to the Event model, you would have the chance to listen to those events: Event.Beforefind, Event.AfterFind, Event.BeforeValidate, Event.BeforeSave, Event.AfterSave, Event.BeforeDelete, Event.AfterDelete.

Thus, this would work:

BeLib::getObject('BeCallbackManager')->bind('Event.BeforeValidate', function ($model) {
    /* Do some custom validation rules. */
    return true;
});

Please, refer to the documentation for BeCallbackManager::bind() for further details.

BeCallbackManager class reference

BEdita implements a basic event system. You can attach custom functions as listeners for built-in events, and you can even trigger your custom events.

function bind(string $eventName, mixed $listener)

Bind a listener to an event.

Params

  • $eventName: name of the event to be listened.
  • $listener: callable to be invoked when the event is triggered.

    This can be either an anonymous function (PHP ≥ 5.3.0) or a fully qualified name of a callable method, like a string (e.g.: 'my_function') or an array containing a class name and one of its methods (e.g.: array('MyClass', 'myPublicMethod')). Please note that CakePHP's ClassRegistry will be used in this case to obtain an instance of the given class.

Examples

Bind a closure to an event:

$eventManager = BeLib::getObject('BeCallbackManager');

$eventManager->bind('MyModel.AfterSave', function ($model, $created, $event) {
    CakeLog::write('callback', 'Saved object of type "MyModel"');
});

Bind a model method to a custom event:

$eventManager = BeLib::getObject('BeCallbackManager');

$eventManager->bind('Cart.AfterPurchase', array('PurchaseHistory', 'purchaseCompleted'));

function unbind(string $eventName = null, mixed $listener = null)

Unbinds a listener from an event. If called without any argument, all events listeners are cleared.

Params

  • $eventName: name of the event. If omitted or null, the given $listener will be removed from all events.
  • $listener: callable to be unbinded. If omitted or null, the given $eventName will be completely cleared.

Examples

Unbind a closure from an event:

$eventManager = BeLib::getObject('BeCallbackManager');

$listener = function ($model, $query, $event) { /* Do something... */ };
$eventManager->bind('MyModel.BeforeFind', $listener);
// ...
$eventManager->unbind('MyModel.BeforeFind', $listener);

Clear all listeners for an event:

$eventManager = BeLib::getObject('BeCallbackManager');

$eventManager->bind('MyModel.MyEvent', function () { /* ... */ });
$eventManager->bind('MyModel.MyEvent', function () { /* ... */ });
// ...
$eventManager->unbind('MyModel.MyEvent');

function trigger(string $eventName, array $eventData = array())

Trigger an event (possibly any event) passing custom data.

For model-related events (or class-related events in general), $eventName is usually the class name the event refers to, followed by a dot, followed by a self-explanatory event name (both model name and event name are camel cased). You are encouraged to follow this convention.

Params

  • $eventName: name of the event to be triggered.
  • $eventData: array of data to be passed as arguments to listeners. The event will be always passed as last argument.

Return values

The triggered event is returned.

Examples

Trigger a custom event:

$eventManager = BeLib::getObject('BeCallbackManager');

$eventManager->bind('MyEvent', function ($a, $b, $event) {
    CakeLog::write('callback', $event->name . '_' . ($a + $b));
    return 42;
});
$evt = $eventManager->trigger('MyEvent', array(2, 3));  // Will result in logging 'MyEvent_5' to 'callback.log'.
echo($evt->result);  // Outputs: '42'.