Using Events in a Custom Module

One way to customize Craft’s behavior is by listening to events it emits, and reacting, altering, or suppressing them using your own logic.

Subscribing to events requires a small amount of PHP in a custom plugin or module. Use our Generator package (included in any new Craft project) to spin up a new module inside your project!

The process for creating a module is covered in greater depth in the extension documentation—this article will cover only the essential pieces of setting up an event listener!

Bootstrap the Module #

Not all modules need to be bootstrapped on every request—but when working with events, we want to be confident that the listeners are registered as the application initializes.

Add this line to your project’s config/app.php file:

// ...

return [
    'id' => App::env('APP_ID') ?: 'CraftCMS',
    'modules' => [
        // This may exist already:
        'my-module' => \modules\MyModule::class,
    ],
    // Add this, or append `my-module` to the array if you already have a `bootstrap` key:
    'bootstrap' => [
        'my-module'
    ],
];

Add Event Code #

Now that we’ve bootstrapped our custom module, we can add code to its init() method to subscribe to an event.

We’ll use the EVENT_DEFINE_RULES event to require all entry titles to be at least ten characters.

Here’s the new code we’ll be adding:

// At the top of the file, add these `use` statements:
use yii\base\Event;
use craft\elements\Entry;
use craft\base\Model;
use craft\events\DefineRulesEvent;

// Within your module class’s `init()` method, add this code:
Event::on(
    Entry::class,
    Model::EVENT_DEFINE_RULES,
    function(DefineRulesEvent $event) {
        // Memoize existing validation rules:
        $rules = $event->rules;

        // Push our new rule onto the array:
        $rules[] = ['title', 'string', 'min' => 10];

        // Reassign rules onto the event so the Entry model can find them:
        $event->rules = $rules;
    }
);

Let’s look at what this code is comprised of—a deeper dive into the anatomy of an event is available in the documentation.

This declares a listener for the EVENT_DEFINE_RULES event, common to all classes that inherit from craft\base\Model—including entries. Craft emits this event whenever any model is initializing its validation rules, but we’ve targeted a subset of those events so our handler is only invoked for events sent by a craft\elements\Entry instance.

$event is a DefineRulesEvent object that’s passed along when the event is triggered, and our code modifies the included rules array to append our custom 10-character minimum to the title field.

The rules array is a core part of Yii’s validation system. Check out Validating Input or jump straight to the Core Validators reference for more about how what kinds of validators and options are available.

Once added, the entire contents of modules/MyModule.php will look something like this:

namespace modules;

use yii\base\Event;
use craft\elements\Entry;
use craft\base\Model;
use craft\events\DefineRulesEvent;
use Craft;

/**
 * MyModule module
 * 
 * @method static MyModule getInstance()
 */
class MyModule extends \yii\base\Module
{
    /**
     * Initializes the module.
     */
    public function init()
    {
        Craft::setAlias('@modules', __DIR__);

        // Set the controllerNamespace based on whether this is a console or web request
        if (Craft::$app->request->isConsoleRequest) {
            $this->controllerNamespace = 'modules\\console\\controllers';
        } else {
            $this->controllerNamespace = 'modules\\controllers';
        }

        parent::init();

        // Our validation event listener:
        Event::on(
            Entry::class,
            Model::EVENT_DEFINE_RULES,
            function(DefineRulesEvent $event) {
                // Memoize existing validation rules:
                $rules = $event->rules;
        
                // Push our new rule onto the array:
                $rules[] = ['title', 'string', 'min' => 10];
        
                // Reassign rules onto the event so the Entry model can find them:
                $event->rules = $rules;
            }
        );

        // Defer most setup tasks until Craft is fully initialized
        Craft::$app->onInit(function() {
            // ...
        });
    }
}

Craft will now produce a validation error if the author attempts to save an entry with a title containing fewer than 10 characters:

Module Events: Validation Error

A validation error produced by code in a custom module, displayed on an entry edit screen in the Craft control panel.

You’re ready to take advantage of any events Craft (or a plugin) emits. Use the event browser to search for an event, or learn about how to discover them in the source!

Applies to Craft CMS 5, Craft CMS 4, and Craft CMS 3.