Event Listeners for Statamic

Arlind Musliu cofounder at Lucky Media
Arlind Musliu

March 9, 2022 · 5 min read

Event Listeners for Statamic

2025 UPDATE

We have updated this blog post to include the new features available up to 2025 since its original publication in 2020.

Laravel and Statamic Event Listeners

Laravel’s event-driven architecture lets developers add custom features by tapping into the framework’s core. Statamic, a flat-file CMS built on top of Laravel, takes advantage of this by offering events that you can use to make your web app better. These events are related to all the main parts of Statamic like Assets, Blueprints, Collections, Entries, Forms, Taxonomies, Users, and more. They are triggered when you do actions like create, delete, update, upload, or find content on your Statamic site.

Understanding Statamic Event

Events in Statamic are dispatched in response to specific changes within the CMS. For example, when you add or update content, events like EntrySaved or EntrySaving are triggered. These are important for developers who want to run custom code during certain events in the content management process. By using these events, you can do things like update external databases, integrate with third-party services, or modify how the CMS works.

Implement a Fuzzy Search feature with Fuse.js

You can use Statamic events to add special search features. Statamic’s built-in search is good, but it might not work for everything, like searching for text in alphabets like Cyrillic. You can use a fuzzy-search library like Fuse.js to enable approximate string matching. This is really helpful for big projects or when you need more flexible search criteria. We explain how to use Fuse.js with Statamic to get fuzzy searching on your website. For large projects, we often suggest using Algolia with Statamic.

Create the event listener

To make it work, we need to create an event listener that will handle the event. The listener can be created with the following command and it will be found at app/Listeners.

php artisan make:listener SavePostsToJson

In our example, we want to create a JSON file that stores the title, categories, and tags of all posts. We need to create an event listener that will refresh the JSON data whenever we add a new post. When we save an entry, the event EntrySaved is triggered. Then, we can use the data from that entry to do things we need. Here’s what you get after creating the listener:

use Statamic\Events\EntrySaved;

public function handle(EntrySaved $event)
{
    $event->entry;
}

Query all posts

Let’s get back to our event listener. We mentioned that we want to query all posts and that’s why we need to include the Entry Facade. The saved data will be added to a new json file that will be located in the public directory.

namespace App\Listeners;

use Statamic\Facades\Entry;
use Statamic\Events\EntrySaved;

class SavePostsToJson
{
    public function handle(EntrySaved $event)
    {
        $data = Entry::query()
            ->where('collection', 'posts')
            ->get()
            ->transform(function ($post) {
                return [
                    'title' => $post->title,
                    'categories' => $post->categories,
                    'slug' => (string)$post->slug()
                ];
            })
            ->toArray();

        file_put_contents(public_path() . '/search/data.json', json_encode($data));
    }
}

That’s it, we can now create a new post and the JSON data will be saved with all available posts.

Using the EntrySaved $event variable

Our current code has a problem. The listener is activated not just when the user saves a post, but any entry. This means that when the user saves a page, product, or any other entry we have, it will trigger the event listener. To solve this, we will use the EntrySaved $event variable and its information.

To make it more challenging, let’s say we have a multisite setup in three different languages. We save the posts in separate JSON files based on the locale. So, we don’t want to save all JSON files if we only change one language and not the others. Let’s see how we can do this by further improving our example.

Firstly, we check if the saved entry is a post or not:

if ($event->entry->collection()->handle() == 'posts')

Then, we check to which language that post belongs:

if ($event->entry->site()->name() == 'en') {

To make it more understandable, without any code refactoring, our example will look like this:

namespace App\Listeners;

use Statamic\Facades\Entry;
use Statamic\Events\EntrySaved;

class SavePostsToJson
{
    public function handle(EntrySaved $event)
    {
        if ($event->entry->collection()->handle() == 'posts') {
            if ($event->entry->site()->name() == 'en') {
                $data = Entry::query()
                        ->where('collection', 'posts')
                        ->where('site', 'en')
                        ->where('published', true)
                        ->get()
                        ->transform(function ($post) {
                            return [
                                'title' => $post->title,
                                'categories' => $post->categories,
                                'slug' => (string)$post->slug()
                            ];
                          })
                          ->toArray();

                file_put_contents(public_path() . "/search/en/data.json", json_encode($data));
                
            } else {
                // some other language
            }
        }
    }
}

We’ll refactor the code to follow best practices. Our example includes English and Albanian as alternative languages, with a different default language.

namespace App\Listeners;

use Statamic\Facades\Entry;
use Statamic\Events\EntrySaved;

class SavePostsToJson
{
    public function handle(EntrySaved $event)
    {
        if ($event->entry->collection()->handle() == 'posts') {
            if ($event->entry->site()->name() == 'en') {
                $this->createEntry('en');
            } elseif ($event->entry->site()->name() == 'sq') {
                $this->createEntry('sq');
            } else {
                $this->createEntry('default');
            }
        }
    }

    protected function createEntry($lang) {

        $data = Entry::query()
            ->where('collection', 'posts')
            ->where('site', $lang)
            ->where('published', true)
            ->get()
            ->transform(function ($post) {
                return [
                    'title' => $post->title,
                    'categories' => $post->categories,
                    'slug' => (string)$post->slug()
                ];
            })
            ->toArray();

        file_put_contents(public_path() . "/search/{$lang}/data.json", json_encode($data));
    }
}

Debugging the data

If you want to see what other data the $event variable brings to the table, you can do so by including Laravel’s Log service:

use Illuminate\Support\Facades\Log;

and adding the following code inside the handle function:

Log::debug(print_r($event->entry, true));

You must add it inside print_r() because the log expects a string and not an object. Then simply go to storage/logs/laravel.log and scroll to the end of the file to see what output you got.

Best practices and code refactoring

When creating custom event listeners, it’s important to use best practices and keep your code clean and modular. Refactor your listeners to manage different situations effectively to make your code easier to maintain and read. Whether you work with many languages or various content types, a well-organized listener can significantly improve your Statamic website’s features.


Bring Your Ideas to Life 🚀

Kickstart Your Statamic Project with the #1 Statamic Agency

Are you planning a new Statamic project or thinking about migrating your WordPress site to Statamic? Learn more about our expertise as a renowned Statamic development agency.

Technologies

Statamic
Arlind Musliu cofounder at Lucky Media
Arlind Musliu

Cofounder and CFO of Lucky Media

Stay up-to-date

Be updated with all news, products and tips we share!

Let’s chat

We partner with a limited number of brands each quarter to ensure senior-level attention on every project.

lokman and arlind headshots
Teamwork

Related posts

September 10, 2021

Cloud - a starter kit for Statamic
Statamic