Skip to content

Event Observation: Inbound Paths, Naming & Anti-Patterns

Reference for how the Moodle adapter (middag-io/middag-php-moodle) observes native Moodle events (inbound) and emits its own domain activity into Moodle's logstore (outbound). Backs MDL-015.

Inbound paths, in order of preference

PathMechanismWhen to use
Attribute-based (preferred)#[moodle_event(...)] on the signal class, auto-discovered by moodle_signal_loaderDefault choice — zero registration boilerplate. Requires the file to be suffixed _signal.php; without it, the attribute is present but discovery silently does not happen.
Bridge registrationmoodle_event_bridge::register() / register_declarative(), called from boot()Middle-ground path when the attribute convention doesn't fit.
Manual observermiddag_observer with explicit signal publicationOnly when the Moodle event needs data enrichment before becoming an internal signal — not for simple pass-through mappings.

Duplicate registration

Registering the same Moodle event class through more than one of the three paths above is a configuration error. In practice, the bridge resolves it by last-write-wins, silently — no fallback, no hard failure. This is looser than the item-type system's identifier-collision rule (a separate mechanism), which treats a collision as fatal rather than resolved by precedence.

Signal naming

Hierarchy defined by aggregate_signal_interface:

FormPatternUse
Generalmiddag/{aggregate}/{action}Default signal name.
Specificmiddag/{aggregate}/{action}/{type}Variant scoped to a type.

Outbound naming

Generated event classes are named {extension}_{entity}_{action}, under the namespace \local_middag\event\ (or \{plugin}\event\ for external plugins), extending \core\event\base. Generation itself is handled by the static-generation pipeline — see MDL-016.

External plugin extension point

{plugin}_extend_local_middag_moodle_signal_loader(array $dirs): array is the single hook an external plugin implements to expose its own signal-class directories to the loader.

Anti-patterns

Anti-patternWhy it's wrong
Registering the same event through two different pathsLast-write-wins resolves it silently — the losing registration simply never fires, with no error raised to signal the mistake.
#[moodle_event] on a class without the _signal.php suffixNot discovered — silently, since the attribute is syntactically valid but never scanned by the loader.
Manual observer for a direct, no-enrichment mappingUnnecessary boilerplate when the attribute path would do.
catch_all (wildcard) left ON in productionReal performance cost — invoked for every Moodle event site-wide.

MIDDAG © 2015-2026