Dispatcher, Action Hooks e Filters¶
O MIDDAG usa mais de um mecanismo reativo, mas eles não têm o mesmo papel.
O publish principal de ocorrências do framework acontece por dispatch(). Internamente, esse fluxo usa EventDispatcherInterface. Action hooks públicos podem ser derivados dessa mesma emissão quando houver mapping oficial. Filters permanecem separados para transformação síncrona de valores.
Relação entre os mecanismos¶
Observer central do Moodle¶
Recebe eventos da plataforma Moodle e pode encaminhar o próprio objeto para o dispatch principal como passagem de plataforma.
Regra:
catch_allé permitido;- se não houver listener, tradução ou hook canônico aplicável, o processamento deve sair em
no-oprápido; - esse caminho pode entregar o próprio objeto do Moodle a quem quiser consumi-lo;
- esse consumo herda acoplamento explícito à plataforma Moodle.
Exemplo educacional:
<?php
$observers = [
[
'eventname' => '*',
'callback' => observer::class . '::catch_all',
'internal' => false,
],
];
<?php
final class observer
{
public static function catch_all(\core\event\base $event): void
{
middag::dispatch($event);
}
}
Dispatcher principal¶
É a entrada principal para publish reativo do framework.
Regra:
- o core publica uma vez;
- listeners tipados, jobs, audit e bridges derivadas reagem a partir dessa emissão;
- o core não deve disparar manualmente
dispatch()edo_action()para o mesmo fato.
Exemplo educacional:
<?php
namespace local_middag\extensions\ecommerce\signal;
final class order_processed_signal
{
public function __construct(
public readonly int $order_id,
public readonly int $customer_id,
public readonly string $status,
) {}
}
<?php
middag::dispatch(
new \local_middag\extensions\ecommerce\signal\order_processed_signal(
order_id: 10,
customer_id: 99,
status: 'processed',
)
);
Action hook derivado¶
É a projeção pública opcional de uma ocorrência despachada.
Regra:
- só existe quando houver mapping oficial;
- só deve ser disparado automaticamente quando houver ouvinte registrado;
- cada ocorrência elegível tem no máximo um nome canônico oficial em string;
- hooks públicos devem documentar explicitamente o contrato de payload.
Hook público estável¶
Quando o objetivo for API pública estável do MIDDAG, o hook deve receber um payload próprio, documentado e estável.
<?php
namespace local_middag\extensions\ecommerce\hook_payload;
final class order_processed_payload
{
public function __construct(
public readonly int $order_id,
public readonly int $customer_id,
public readonly string $status,
) {}
public function to_array(): array
{
return [
'order_id' => $this->order_id,
'customer_id' => $this->customer_id,
'status' => $this->status,
];
}
}
<?php
middag::add_action('middag/extension/ecommerce/order_processed', function (
\local_middag\extensions\ecommerce\hook_payload\order_processed_payload $payload
): void {
debugging((string) $payload->order_id);
});
Hook pass-through de plataforma¶
Quando o objetivo for compatibilidade ou integração técnica direta com o Moodle, o hook pode receber o próprio objeto da plataforma.
<?php
middag::add_action('middag/moodle/user_created', function (
\core\event\user_created $event
): void {
debugging((string) $event->objectid);
});
Pass-through de plataforma
Esse hook continua público e útil, mas o seu payload herda acoplamento ao Moodle. O contrato documental do hook deve deixar isso explícito.
Filter¶
É o mecanismo próprio para transformação síncrona de valores em fluxo.
Regra:
- não é derivado do dispatcher;
- não representa ocorrência publicada;
- existe para receber um valor, transformá-lo e devolvê-lo no mesmo fluxo.
Exemplo educacional:
Quando usar dispatch()¶
- quando uma ocorrência precisar gerar múltiplas reações independentes;
- quando audit, jobs, observabilidade ou bridges derivadas puderem surgir sem alterar o emissor;
- quando o core precisar publicar uma única vez e deixar o restante como consequência derivada.
Exemplos curtos:
- criação de usuário no Moodle recebida pelo
catch_all; - mudança de status de matrícula;
- conclusão de processamento de pedido.
Quando usar add_action()¶
- quando houver necessidade de ergonomia pública ou compatibilidade de extensão em string;
- quando a reação for pública e síncrona, mas derivada de uma ocorrência despachada;
- quando o ponto de extensão precisar ser reconhecível por terceiros sem obrigá-los a listener tipado.
Exemplos:
middag/moodle/user_createdmiddag/extension/ecommerce/order_processedmiddag/matricula/created
Quando usar add_filter() / apply_filters()¶
- quando a necessidade principal for transformar um valor em fluxo;
- quando o valor modificado precisar seguir no mesmo caminho síncrono;
- quando não houver “fato publicado”, e sim ajuste de dado.
Exemplos:
- filtrar HTML antes de renderizar;
- normalizar payload antes de persistir;
- ajustar configuração antes de enviar a serviço externo.
Regra prática de uso¶
- aconteceu um fato ->
dispatch() - quero reagir por listener tipado -> dispatcher interno
- quero reagir por hook público ->
add_action() - quero alterar valor no fluxo ->
add_filter()/apply_filters()
Naming e convenções¶
- métodos públicos reativos usam
snake_case:add_action,do_action,add_filter,apply_filters - classes e interfaces do subsistema reativo também usam
snake_case - hooks canônicos públicos usam prefixo
middag/e segmentos emsnake_case - payload de hook público deve ser documentado explicitamente por hook
eventofica reservado preferencialmente à plataforma Moodle e ao legado- o artefato interno do framework usa a terminologia
signal
O que não fazer¶
- não disparar manualmente
dispatch()edo_action()para o mesmo fato; - não substituir
filterspelo dispatcher; - não tratar hook pass-through de plataforma como se fosse payload estável do MIDDAG;
- não usar hook para esconder dependência obrigatória do fluxo principal.