Moodle LMS is designed with extensibility and scalability in mind. One of the key reasons Moodle can support complex workflows, automation, and third-party integrations without touching core code is its event-driven architecture. Two important concepts that make this possible are the Moodle Events API and Observers.
In this article, we will first understand these two core concepts, and then explore how they can be practically used together with a real-world example: the core\event\user_created event and a custom plugin named local_welcome.
What Is
the Moodle Events API?
The Moodle
Events API is a standardized framework that tracks significant actions
happening across the LMS. Whenever something important occurs - such as a user
logging in, a course being created, or an assignment being submitted - Moodle fires
an event.
Each event
is represented as a PHP class that contains structured information
about:
- What action occurred
- Where it occurred (context)
- Who triggered it
- When it happened
These
events are fired not only by Moodle core, but also by plugins,
making the system highly modular. Once an event is fired, it can be captured
and processed by observers, which are callback functions defined inside
plugins.
This
architecture ensures that Moodle remains:
- Modular – features can be extended
without modifying core files
- Stable – core logic stays untouched
- Maintainable – custom logic lives inside
plugins
In short,
the Events API acts as a central communication layer that allows different
parts of Moodle to react to system activity in a clean and predictable way.
What Is an Observer in Moodle?
An observer
is a PHP callback that listens for a specific event and executes automatically
when that event occurs. Observers are defined inside a plugin’s db/events.php
file.
Each
observer specifies:
- The event name it
listens to
- The callback method to
execute
- Optional settings like
priority
When
Moodle fires an event, it checks all registered observers across installed
plugins. If a plugin has registered an observer for that event, Moodle calls
the corresponding handler method and passes the event object to it.
It is
important to understand that observers:
- Do not interrupt the event
- Do not modify the original action
- React after the event has
occurred
This makes
observers safe, predictable, and ideal for automation tasks such as logging,
notifications, integrations, and background processing.
Understanding Moodle Events API Using core\event\user_created
Moodle LMS
follows a powerful event-driven architecture that allows developers to react to
system activities without modifying core code. One of the most commonly used
and practical examples of this architecture is the core\event\user_created
event.
This event
is triggered whenever a new user account is created in Moodle. This
could happen through:
- Manual user creation by an
administrator
- User upload via CSV
- External authentication
methods (LDAP, OAuth, etc.)
- Web services or custom scripts
Because
user creation is such a critical action, this event provides valuable data such
as the newly created user ID, context, and timestamp. As a result, it is widely
used in real-world Moodle customizations.
Why Listen to the User Created Event?
Listening
to core\event\user_created allows developers to automate many post-registration
workflows. Some common use cases include:
- Sending a welcome email to the
user
- Logging user creation activity
for auditing
- Assigning users to default
cohorts or groups
- Triggering external CRM or ERP
systems
- Creating structured onboarding
workflows
In this
example, the local_welcome plugin is designed to respond immediately
after a user is created and perform a welcome-related action. By using an
observer, the plugin keeps its logic completely separate from Moodle core,
making it easier to maintain, upgrade, and reuse across different Moodle
versions.
How local_welcome Fits into the Events Flow
Let’s look
at how the Events API and observer come together in the local_welcome
plugin.
The
workflow is simple and reliable:
- A new user is created in
Moodle
- Moodle fires the core\event\user_created
event
- The Events API checks all
registered observers
- The observer defined in the local_welcome
plugin is triggered
- The plugin executes its custom
logic (such as preparing welcome messages)
This
approach ensures that the plugin works consistently, regardless of how
users are created. Whether an administrator adds users manually, users are
imported via CSV, or accounts are created through external authentication, the
observer will still respond in the same way.
Code Implementation (local_welcome Plugin)
Below is
the complete implementation used in the local_welcome plugin.
The code demonstrates how an observer is registered and how the event data is
handled after a user is created.
Plugin reference: https://moodle.org/plugins/local_welcome
Moodle event List: https://docs.moodle.org/dev/Events_API
local/welcome/db/event.php
<?php
defined('MOODLE_INTERNAL') || die();
$observers = array(
array(
'eventname' => '\core\event\user_created',
'callback' => '\local_welcome\observer::send_welcome',
),
);
local/welcome/classes/observer.php
<?php
namespace local_welcome;
defined('MOODLE_INTERNAL') || die();
class observer {
public static function send_welcome(\core\event\user_created $event) {
global $CFG, $SITE;
$eventdata = $event->get_data();
$user = \core_user::get_user($eventdata['objectid']);
$sender = get_admin();
// Sender can be false when unit tests are running.
if ($sender === false) {
return;
}
if (!empty($user->email)) {
$config = get_config('local_welcome');
$moderator = clone($sender);
if (!empty($config->auth_plugins)) {
$auths = explode(',', $config->auth_plugins);
if (!in_array($user->auth, $auths)) {
return '';
}
} else {
return '';
}
$moderator->email = $config->moderator_email;
$sender->email = $config->sender_email;
$sender->firstname = $config->sender_firstname;
$sender->lastname = $config->sender_lastname;
$messageuserenabled = $config->message_user_enabled;
$messageuser = $config->message_user;
$messageusersubject = $config->message_user_subject;
$messagemoderatorenabled = $config->message_moderator_enabled;
$messagemoderator = $config->message_moderator;
$messagemoderatorsubject = $config->message_moderator_subject;
$welcome = new \local_welcome\message();
$messageuser = $welcome->replace_values($user, $messageuser);
$messageusersubject = $welcome->replace_values($user, $messageusersubject);
$messagemoderator = $welcome->replace_values($user, $messagemoderator);
$messagemoderatorsubject = $welcome->replace_values($user, $messagemoderatorsubject);
if (!empty($messageuser) && !empty($sender->email) && $messageuserenabled) {
email_to_user($user, $sender, $messageusersubject, html_to_text($messageuser), $messageuser);
}
if (!empty($messagemoderator) && !empty($sender->email) && $messagemoderatorenabled) {
email_to_user($moderator, $sender, $messagemoderatorsubject,
html_to_text($messagemoderator), $messagemoderator);
}
}
}
}
When
working with core\event\user_created, keep these best practices in mind:
- Avoid heavy processing inside
observers
- Use background tasks for
time-consuming logic
- Always validate user data
before using it
- Keep observer logic focused on
one responsibility
- Do not assume the user is
fully enrolled or active
Following these guidelines ensures good performance and long-term maintainability.
Conclusion
If you found this article helpful, please like, comment, share, and subscribe to our channel/blog. Your support motivates us to create more practical Moodle tutorials, real-world examples, and easy-to-follow guides. Don’t forget to share it with your friends and colleagues who work with Moodle LMS!
#moodle
#moodleLMS
#moodlecoding
#moodleDevelopment
#lmsbestPractice
#eventsInMoodle
#ObserversInMoodle
#techgyan
#techgyans
#Moodle