Nate Iler
Craft CMS allows you to do a lot of customization by hooking into Events via a Plugin or Module. You can see the basics of how Events and Hooks work in the documentation.
Here's a full list of Events provided by Craft that you can use in your plugins or modules:
Here's an example Module named `Demo` that runs code after an Element has saved:
<?php
namespace flipbox\demo;
use yii\base\Module;
class Demo extends Module
{
public function init()
{
Event::on(
Elements::class,
Elements::EVENT_AFTER_SAVE_ELEMENT,
function (ElementEvent $event) {
// do stuff
}
);
}
}
As a Craft CMS project's size or complexity grows, you'll most likely end up adding a lot of custom business logic to either a Module or Plugin. This may require hooking into more and more of Craft's events. This will inevitably lead to your init() function getting very large and hard to maintain.
Here's an example Plugin named `Demo` that has grown very large (logic removed):
<?php # Demo.php
namespace flipbox\demo;
use craft\base\Plugin;
...
class Demo extends Plugin
{
public function init()
{
Event::on(
Elements::class,
Elements::EVENT_AFTER_SAVE_ELEMENT,
function (ElementEvent $event) {
// do stuff
}
);
Event::on(
View::class,
View::EVENT_BEFORE_RENDER_PAGE_TEMPLATE,
function (TemplateEvent $event) {
// do stuff
}
);
Event::on(
SystemMessages::class,
SystemMessages::EVENT_REGISTER_MESSAGES,
function (RegisterEmailMessagesEvent $event) {
// do stuff
}
);
Event::on(
Fields::class,
Fields::EVENT_REGISTER_FIELD_TYPES,
function (RegisterComponentTypesEvent $event) {
// do stuff
}
);
... x20
}
}
Group your events in separate functions and call those from init(). This is a simple first step.
<?php # Demo.php
namespace flipbox\demo;
use craft\base\Plugin;
...
class Demo extends Plugin
{
public function init()
{
self::registerViewHandlers();
self::registerUserHandlers();
self::registerHubspotHandlers();
self::registerSalesforceHandlers();
}
protected static function registerViewHandlers()
{
// set up event handling
}
protected static function registerUserHandlers()
{
// set up event handling
}
protected static function registerHubspotHandlers()
{
// set up event handling
}
protected static function registerSalesforceHandlers()
{
// set up event handling
}
}
Create an Events.php (or Handlers.php) file in the root of your plugin or module and call that from init().
This cleanly separates your Event setup from the rest of your plugin/module code.
<?php # Demo.php
namespace flipbox\demo;
use craft\base\Plugin;
class Demo extends Plugin
{
public function init()
{
Craft::$app->getPlugins()->on(
Plugins::EVENT_AFTER_LOAD_PLUGINS,
function () {
Events::register();
}
);
}
}
<?php # Events.php
namespace flipbox\demo;
class Events
{
public static function register()
{
// register event handlers here however you prefer
}
}
Create a directory in your plugin/module and create separate PHP classes for each event you're hooking into.
Here are some ideas of how you could structure this:
It's up to you since there is currently no Craft CMS recommendation to follow.
We've started converting our plugins and modules to use these invokable classes. It organizes the business logic in descriptively-named classes, it cleans up event/handler registration in our plugins/modules, and you can add private functions as needed. Occasionally, we won't move an event handler to a class if there are only a few lines of logic.
Here are two ways you can set this up via an invokable class or a static function.
<?php # Events.php
namespace flipbox\demo;
use flipbox\demo\events\ElementSaved;
use flipbox\demo\events\RenderingPageTemplate;
class Events
{
public function init()
{
// Invokable class
Event::on(
Elements::class,
Elements::EVENT_AFTER_SAVE_ELEMENT,
new ElementSaved
);
// Static function
Event::on(
View::class,
View::EVENT_BEFORE_RENDER_PAGE_TEMPLATE,
[
RenderingPageTemplate::class,
'handle'
]
);
}
}
<?php # /handlers/ElementSaved.php
namespace flipbox\demo\handlers;
use craft\events\ElementEvent;
class ElementSaved
{
public function __invoke(ElementEvent $event)
{
// do stuff
$this->convertTimezones();
}
private function convertTimezones()
{
// why me?!
}
}
<?php # /handlers/RenderingPageTemplate.php
namespace flipbox\demo\handlers;
use craft\events\TemplateEvent;
class RenderingPageTemplate
{
public static function handle(TemplateEvent $event)
{
// do stuff
$this->generatePdf();
}
private function generatePdf()
{
// please help!!
}
}
Yii & Craft can register and unregister events. Something to consider with invokable Event Handler classes is you aren't able to easily unregister a single Event Handler using this method because you don't have a link to the original Event Handler when it's turned on.
Calling the code below will turn off the Event and therefore anything that hooks into it, not just a single Event Handler.
$elements = Craft::$app->elements;
$elements->off(
\craft\services\Elements::EVENT_AFTER_SAVE_ELEMENT
);
If you're working on a simple Craft CMS plugin or module, you may not need to worry about keeping Events organized as you may only have a few. (We'd still suggest Solution #1 or #2 so you'll be ready for the future.) As your plugin or module grows, we'd highly recommend considering Solution #3 so that your Event business logic is separate from Event registration. We'd also recommend using Services to keep your complex logic out of your new classes.
Thanks for reading and I hope this was helpful. You can reach out with questions or feedback at @dstjohn, @flipboxdigital, or the Contact page.