M

Manual Rede das Artes

Desenvolvimento

Sistema de hooks

Extensibilidade orientada a eventos no Mapas Culturais: nomenclatura de hooks, registro, binding de contexto, prioridades e padrões comuns.

O sistema de hooks é a principal forma de estender o Mapas Culturais sem modificar arquivos do núcleo. Um hook é um evento nomeado ao qual qualquer quantidade de callbacks PHP pode ser associada. Quando a aplicação dispara esse hook, todos os callbacks registrados para aquele nome são executados na ordem de prioridade.

Registrando um hook

$app = MapasCulturais\App::i();

$app->hook('hook.name', function() {
    // callback body
});

// Com prioridade (número menor = prioridade maior; padrão é 10)
$app->hook('hook.name', function() {
    // runs earlier
}, 5);

Registre hooks dentro do método _init() de um módulo ou de um tema para que eles sejam configurados antes do processamento da primeira requisição.

Hook naming syntax

Os nomes dos hooks seguem um padrão estruturado que permite inscrições amplas e também específicas.

Hooks de entidade

entity.{action}:{timing}
entity({EntityClass}).{action}:{timing}
PatternFires when
entity.save:beforeAny entity is about to be saved (insert or update)
entity.save:afterAny entity was saved
entity(Agent).save:beforeAn Agent entity is about to be saved
entity(Agent).save:afterAn Agent entity was saved
entity.insert:beforeAny entity is about to be inserted (first save)
entity.insert:afterAny entity was inserted
entity(Space).insert:afterA Space entity was inserted
entity.update:beforeAny entity is about to be updated
entity.update:afterAny entity was updated
entity.remove:beforeAny entity is about to be deleted
entity.remove:afterAny entity was deleted

Inside entity hook callbacks, $this is bound to the entity instance:

$app->hook('entity(Agent).save:before', function() {
    // $this is the Agent being saved
    if (empty($this->shortDescription)) {
        $this->shortDescription = 'No description provided.';
    }
});

Hooks de controller

{Method}({controllerId}.{actionName}):before
{Method}({controllerId}.{actionName}):after
ALL({controllerId}.{actionName}):before
PatternFires when
GET(agent.index):beforeBefore GET /agent/index
POST(opportunity.create):afterAfter POST /opportunity/create
ALL(registration.*):beforeBefore any action on the registration controller

Hooks de view

view.render:before
view.render({templateName}):before
view.partial:before
view.partial({templateName}):after
PatternFires when
view.render:beforeBefore any template is rendered with a layout
view.render(agent/single):beforeBefore agent/single is rendered
view.partial:afterAfter any partial template is rendered

API hooks

API({EntityClass}).params

Use hooks de API para modificar parâmetros de consulta antes que a API execute uma busca:

$app->hook('API(Agent).params', function(&$params) {
    // Restrict API results to enabled agents only
    $params['status'] = 1;
});

Template hooks

template({controllerId}/{actionName})

Hooks de template permitem injetar HTML em pontos específicos de uma página renderizada:

$app->hook('template(agent/single)', function() {
    echo '<div class="my-widget">Custom content</div>';
});

Module lifecycle hooks

module({ClassName}).init:before
module({ClassName}).init:after
mapasculturais.init

O contexto de $this

Quando um hook dispara de dentro de uma operação de entidade, o callback pode ser vinculado à entidade usando applyHookBoundTo. Isso é tratado automaticamente nos hooks de entidade, então $this dentro do callback se refere à entidade que disparou o hook:

$app->hook('entity(Opportunity).insert:after', function() {
    // $this = the newly inserted Opportunity
    $this->log->info("New opportunity created: {$this->name}");
});

For hooks fired without binding, $this is not available unless you use use in the closure:

$myService = new MyService();

$app->hook('mapasculturais.init', function() use ($myService) {
    $myService->initialize();
});

Passagem de argumentos

Hooks podem passar argumentos adicionais aos callbacks. Os métodos applyHook e applyHookBoundTo aceitam um array de argumentos:

// Hook fired with extra arguments
$app->applyHook('my.hook', [$entityId, $extraData]);

// Callback receives them as parameters
$app->hook('my.hook', function($entityId, $extraData) {
    // use $entityId and $extraData
});

Prioridades

A prioridade padrão é 10. Números menores executam primeiro. Quando dois callbacks têm a mesma prioridade numérica, eles rodam na ordem em que foram registrados.

// Runs first
$app->hook('entity.save:before', function() {
    // high priority
}, 1);

// Runs after
$app->hook('entity.save:before', function() {
    // lower priority
}, 20);

Removing hooks

To remove all callbacks from a hook:

$app->hooks->clear('entity(Agent).save:before');

// Remove all hooks
$app->hooks->clear();

Comma-separated hook registration

Register a single callback on multiple hooks at once by separating names with commas:

$app->hook('entity.insert:after, entity.update:after', function() {
    // fires after both insert and update
});

Negated hooks (exclusion)

Prefix a hook name with - to remove a previously registered callback from that hook:

$app->hook('-entity(Agent).save:before', $callbackToExclude);

Using hooks in themes vs. modules

In a module

Register hooks inside _init(). The module config array is available as $this->_config. Prefer modules for hooks that add backend behavior independent of the visual layer.

class Module extends \MapasCulturais\Module
{
    function _init(): void
    {
        $app = App::i();
        $app->hook('entity(Registration).insert:after', function() {
            // send a notification
        });
    }
}

In a theme

Register hooks inside the theme's _init(). Use theme hooks for view injection, asset enqueueing, and UI-level customization.

class Theme extends \MapasCulturais\Themes\BaseV2\Theme
{
    protected function _init(): void
    {
        parent::_init();
        $app = \MapasCulturais\App::i();
        $app->hook('view.render(site/index):before', function() {
            $this->enqueueScript('app', 'home', 'js/home.js');
        });
    }
}

Complete list of entity hook events

HookTiming
entity.newAfter __construct of any entity
entity({Class}).newAfter __construct of a specific entity
entity.loadAfter any entity is loaded from DB
entity({Class}).loadAfter a specific entity class is loaded
entity.save:beforeBefore insert or update
entity.save:afterAfter insert or update
entity.insert:beforeBefore first-time save
entity.insert:afterAfter first-time save
entity.update:beforeBefore update of existing entity
entity.update:afterAfter update of existing entity
entity.remove:beforeBefore soft or hard delete
entity.remove:afterAfter soft or hard delete

To discover which hooks fire during a specific operation, enable hook logging in your development configuration:

'app.log.hook' => true,

Hook names will be written to var/logs/app.log as requests are processed.


Esse material é fruto do Programa de Difusão Nacional - Funarte Redes das Artes, realizado pelo Laboratório do Futuro (entidade vinculada à Universidade Federal do Ceará) no ano de 2025.

Felicilab
Mutirão
Lab do Futuro UFC
UFC
Rede das Artes Funarte
Funarte
MinC Governo Federal

On this page