Events

Festi provides a event-driven architecture that allows different parts of the application to communicate efficiently. Events are triggered throughout the execution of a Festi application, and you can subscribe to these events to execute specific logic when they occur.

The Core primarily acts as an Event Bus, dispatching events that can be listened to and handled by plugins, DGS (Data Grid Store), themes, and other system components.

System Events

Core::EVENT_ON_AFTER_INIT

This event is triggered after Core has been initialized and all plugins have been initialized, meaning all the init.php files of the plugins have been called.

For example, if we need to use a constant declared in the init.php of another plugin in our plugin:

init.php:


assert($this instanceof Core);

$this->addEventListener(Core::EVENT_ON_AFTER_INIT, function () {
    $this->addEventListener(IRpc::EVENT_ON_TOKEN_LOGIN, function (FestiEvent &$event) {
        Core::getInstance()->getPluginInstance('GoogleApi')->onInterceptLoginByToken($event);
    });
});

IRpc is declared in another plugin, and if IRpc::EVENT_ON_TOKEN_LOGIN is called outside of the Core::EVENT_ON_AFTER_INIT event, there's a high chance you'll get a Fatal Error.

Core::EVENT_ON_CREATE_STORE

After a DGS is created, the Core::EVENT_ON_CREATE_STORE event is triggered. It can be used to modify all DGS in the system or specific ones.

Target: * store - reference to the DGS

$stores = array();

$this->core->addEventListener(Core::EVENT_ON_CREATE_STORE, function (FestiEvent &$event) use (&$stores) {
    $stores[] = &$event->getTargetValueByKey('store');
});

Core::EVENT_PLUGIN_INIT

This event is triggered during the initialization of a plugin, before the plugin's onInit method is called.

Target: * plugin - reference to the plugin

Core::EVENT_ON_RESPONSE

This event is triggered before the response is sent to the request.

Target = Response

$this->core->addEventListener(Core::EVENT_ON_RESPONSE, function (FestiEvent &$event) {
    $response = $event->getTarget();
    assert($response instanceof Response);
    $response->setOverride(true);
    $this->core->removeEventListenersByType(Core::EVENT_ON_RESPONSE);
});

ISystemPlugin::EVENT_ON_BEFORE_REQUEST_PLUGIN_METHOD

This event is triggered before a plugin method is called by reference.

Target: * params - reference to the array of parameters being passed to the method * response - reference to the Response instance * plugin - name of the plugin being called * method - name of the method being called

Writing an Interceptor for All Plugin Requests

We need to ensure that users of type Student have access only to their institution for all requested methods:

class SchoolPlugin extends DisplayPlugin
{

    public function onRequestInterceptor(FestiEvent &$event)
    {
        $params = $event->getTargetValueByKey('params');

        if (App::isStudent()) {
            if (!array_keyExists(1, $params)) {
                throw new NotFoundException("Undefined company ID");
            }

            $idRequestedCompany = $params[1];
            App::validateCompanyPermission($idRequestedCompany);
        }

        return true;
    }
}

init.php:


$this->addEventListener(
    ISystemPlugin::EVENT_ON_BEFORE_REQUEST_PLUGIN_METHOD,
    function (FestiEvent &$event) {
        Core::getInstance()->getPluginInstance('School')->onRequestInterceptor($event);
    }
);

Create Custom Event and Pass Through the Core

You can create a custom event by extending FestiEvent and pass it through the Core, allowing other parts of your project to listen for and handle the event.

Here’s an example of creating a custom event:

class NotFoundContentEvent extends FestiEvent
{
    private string $_url;
    private Response $_response;
    private bool $isFound = false;
    public function __construct(Response &$response, string $url)
    {
        parent::__construct($this::class);

        $this->_url = $url;
        $this->_response = &$response;
    }

    public function getUrl(): string
    {
        return $this->_url;
    }
    public function setFound(bool $isFound): void
    {
        $this->isFound = $isFound;
    }

    public function isFound(): bool
    {
        return $this->isFound;
    }
}
````

To dispatch the custom event through Core, use the following code:

```php
$event = new NotFoundContentEvent($response, $url);
$this->core->dispatchEvent($event);

if (!$event->isFound()) {
    throw new NotFoundException();
}

return true;

Once the event has been dispatched, you can subscribe to it from anywhere in your project, such as `init.php`` in any plugin, or from any other location:

assert($this instanceof Core);

$this->addEventListener(Core::EVENT_ON_AFTER_INIT, function () {

    Core::getInstance()->addEventListener(NotFoundContentEvent::class, function (NotFoundContentEvent &$event) {
        // Handle the custom event
    });
});

Cross-Plugin Events via Core

When you need to link one plugin's event to another, subscribe to the expected event in the plugin's init.php:

class PluginA
{
    public function onUpdate(Response &$response, ?int $idUser = null)
    {
        ...
        $event = new FestiEvent(PLUGIN_A_EVENT_UPDATE, $target);
        $this->core->dispatchEvent($event);
        ...
    }
}

PluginB init.php:

$this->addEventListener(
    ISystemPlugin::EVENT_ON_BEFORE_REQUEST_PLUGIN_METHOD,
    function (FestiEvent &$event) {
        $plugin = Core::getInstance()->getPluginInstance('PluginB');

        Core::getInstance()->addEventListener(
            PLUGIN_A_EVENT_UPDATE,
            function (FestiEvent &$event) use ($plugin) {
                $plugin->onUpdatePluginA($event);
            }
        );
    }
);

Theme Events

In the event’s $target, you will find: * link - the URL that the logo leads to * logo - the logo's src attribute * content - if specified, $target['content'] will be rendered within the logo

init.php:

<?php

$this->addEventListener(
    Core::EVENT_ON_REQUEST,
    function () {
        $companiesPlugin = Core::getInstance()->getPluginInstance('Companies');

        Core::getInstance()->getSystemPlugin()->addEventListener(
            ITheme::EVENT_THEME_ON_PREPARE_LOGO,
            array(&$companiesPlugin, 'onPrepareCompanyLogo')
        );
    }
);

CompaniesPlugin.php:

<?php

class CompaniesPlugin extends StagePlugin
{
    public function onPrepareCompanyLogo(FestiEvent &$event)
    {
        $target = &$event->getTarget();

        // ...

        if (is_null($company['logo'])) {
            return;
        }

        // ...

        $target['logo'] = $logo;
    }
}