Demystifying Laravel Model Listeners and Observers for Beginners

Hey in this post on Demystifying Laravel Model Listeners and Observers for Beginners we’ll be looking deep into how we can easily listen to model changes and update those changes in respective models.
We’ll be using Post and Comments as an example and guide you within the various steps that come along the way.

Table of Contents

What are the Laravel Events?

The Laravel Eloquent Events are triggered whenever a lifecycle of a model is called.
Some of the lifecycle methods are: retrieved, creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored, replicating. These are very useful when have to manage a large number of relationships that belong to many models and will greatly reduce the code size as the logic is managed by event and listener classes.

How can you create an Event in Laravel

There are many ways of creating Event and Listeners and we’ll be discussing in-detail as per the information provided by Laravel Documentation on Events.
For this, we have considered using creating Posts as an example so the PostModel contains fields such as post_id, name, slug, description.

Whenever a new post is created we would like Laravel to automatically convert the name into a slug only when a new post gets created.

So let’s start by creating a model for the post table.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class PostModel extends Model
{
    use HasFactory;

    protected $table = 'posts'; 
    protected $primaryKey = 'post_id'; 
    protected $fillable = ['name', 'slug', 'description' ];
    protected $dates = ['deleted_at']; 
    
    protected $with = [];
    protected $appends = [ ];
    protected $morphClass = "posts";

}

And the next step is to create an Event for PostModel. So type the below command into the terminal.

php artisan make:event PostEvent

Inside PostEvent:

<?php

namespace App\Events;

use App\Models\PostModel;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class PostEvent
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    /**
     * Create a new event instance.
     *
     * @return void
     */

    public $post;

    public function __construct( PostModel $post )
    {
        $this->post = $post;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('channel-name');
    }
}

And next create a respective listner for this event i.e PostCreated listener.

php artisan make:listener PostCreated

Inside PostEvent listener:

<?php

namespace App\Listeners;

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;

class PostCreated
{ 
    /**
        * Create the event listener.
        *
        * @return void
        */
    public function __construct()
    {
        
    }

    /**
        * Handle the event.
        *
        * @param  object  $event
        * @return void
        */
    public function handle($event)
    {
        $post = $event->post;

        $post->slug = preg_replace( '/[^A-Za-z0-9]/', '-', strtolower( $post->name ) );
        $post->save();
    }
}

Mapping all the events of applications within EventServiceProvider

In EventServiceProvider all the events of the application will be mapped with their respective listeners. From here it is how Laravel gets to know which are the registered events.

So we should also add PostEvent and PostCreated listener in app\Providers\EventServiceProvider.php

protected $listen = [
    PostEvent::class => [
        PostCreated::class,
    ],
];

Registering Lifecycle methods with Event Class within the models

Now both the event and listener are ready so the next step is to associate the event with the model lifecycle hooks. Below I have shown how to do that with an example.
In Model

protected $dispatchesEvents = [
    'lifecycle-hook-name' => 'and here goes event class',
];
Caution: More of the people get it wrong they add the listener insisted on event. And you don’t do that mistake.

So we have to add dispatchesEvents property into PostModel.

<?php

namespace App\Models;

use App\Events\PostEvent;
use App\Listeners\PostCreated;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class PostModel extends Model
{
    use HasFactory;

    protected $table = 'posts'; 
    protected $primaryKey = 'post_id'; 
    protected $fillable = ['name', 'slug', 'description' ];
    protected $dates = ['deleted_at']; 
    
    protected $with = [];
    protected $appends = [ ];
    protected $morphClass = "posts";


    protected $dispatchesEvents = [
        'created' => PostEvent::class, // Event
    ];
}

The PostEvent is called after the created lifecycle is triggered. And this event, in turn, calls PostCreated listeners.

Creating a new Post

So now when we create a new post without passing slug data then the Laravel will events will trigger the PostCreated Listener which will create a slug for this post.

use App\Models\PostModel;

PostModel::create( [ 
    "name" => "Laravel Tutorial ABC", 
    "description" => "Welcome to the post of laravel tutorial", 
] );

[display-image][path=D:\contents\php_laravel[posts]\Laravel Model Listeners and Observers/Laravel Saving Data into the Post Table with Slug- 1.png ,alt=Laravel Saving Data into the Post Table with Slug]

Shortner way to achieve the same goal

You may find creating an event and then creating listeners takes too much effort. Then there is a better and shorter way to get around it by registering closures within the model static boot() method.
And here is how you will be doing it.

<?php
class PostModel extends Model
{
    protected static function booted()
    {
        static::created(function ( $post ) {
            $post->slug = preg_replace( '/[^A-Za-z0-9]/', '-', strtolower( $post->name ) );
            $post->save();
        });
    }
}

Laravel will also provide you with the ability to queue the event in the background and then process logic behind the curtain to long and heavy processes.

use function Illuminate\Events\queueable;

static::created(queueable(function ( $post ) {
    $post->slug = preg_replace( '/[^A-Za-z0-9]/', '-', strtolower( $post->name ) );
    $post->save();
}));

And if you don’t like to do this then there is another way around where you specify the above methods within a custom provider. And to do that I have created a custom provider called CustomNotificationProvider within app\Providers\CustomNotificationProvider.php.
And registered that provider in config\app.php.

    'providers' => [

        '...',
        '...',
        '...',
        App\Providers\CustomNotificationProvider::class,
    ]

And inside app\Providers\CustomNotificationProvider.php.

<?php

namespace App\Providers;

use App\Models\PostModel;
use Illuminate\Support\ServiceProvider;

class CustomNotificationProvider extends ServiceProvider
{
    /**
        * Register services.
        *
        * @return void
        */
    public function register()
    {
        
    }
    /**
        * Bootstrap services.
        *
        * @return void
        */
    public function boot()
    {

        PostModel::created(function( $post ){
            $post->slug = preg_replace( '/[^A-Za-z0-9]/', '-', strtolower( $post->name ) );
            $post->save();
        });
    }
}    

What are Laravel Observers?

The Laravel Observers are used to centralize the management of the lifecycle within a single observer class. Unlike Events which in most cases are related to other application events. But the observers are specifically for model events any lifecycle event triggers the observer calls the related methods and we can put our code inside that method.

How can you create a Model Observer in Laravel

To create a model observer use the below command.

php artisan make:observer PostObserver --model=Models/PostModel

You’ll see the prefeed data in the PostObserver class.

<?php

namespace App\Observers;

use App\Models\PostModel;

class PostObserver
{
    /**
        * Handle the post model "created" event.
        *
        * @param  \App\Models\Models\PostModel  $postModel
        * @return void
        */
    public function created(PostModel $postModel)
    {
        $postModel->slug = preg_replace( '/[^A-Za-z0-9]/', '-', strtolower( $postModel->name ) );
        $postModel->save();
    }

    /**
        * Handle the post model "updated" event.
        *
        * @param  \App\Models\Models\PostModel  $postModel
        * @return void
        */
    public function updated(PostModel $postModel)
    {
        //
    }

    /**
        * Handle the post model "deleted" event.
        *
        * @param  \App\Models\Models\PostModel  $postModel
        * @return void
        */
    public function deleted(PostModel $postModel)
    {
        //
    }

    /**
        * Handle the post model "restored" event.
        *
        * @param  \App\Models\Models\PostModel  $postModel
        * @return void
        */
    public function restored(PostModel $postModel)
    {
        //
    }

    /**
        * Handle the post model "force deleted" event.
        *
        * @param  \App\Models\Models\PostModel  $postModel
        * @return void
        */
    public function forceDeleted(PostModel $postModel)
    {
        //
    }
}    

Next register the PostObserver within AppServiceProvider.

public function boot()
{
    PostModel::observe(PostObserver::class);
}

Skipping or Ignoring Laravel Model Events

For some reason, you can also skip or ignore events in Laravel that use the static method withoutEvents which takes a closure function as a parameter and inside that function there will be database operations happening.

$post = PostModel::withoutEvents(function () use () {
    $post = PostModel::create( [ 
        "name" => "Laravel Tutorial ABC", 
        "description" => "Welcome to the post of laravel tutorial", 
    ] );

    return $post;
});

And if you want to ignore an event for a particular object then use saveQuietly() method.

$post = new PostModel();

$post->fill( [ 
    "name" => "Laravel Tutorial ABC", 
    "description" => "Welcome to the post of laravel tutorial", 
] );

#post->saveQuietly();

Difference between Laravel Events and Observers

Events and ListenersObservers
Events and Listeners are multi-purpose and can be used for various purposes other than listening to only database lifecycle events.The model observers on the other hand can only be used to listen to model events.
The approach is lengthy as you need to create an Event and then listenersThe approach is short and straight forward as you need to create only one observer class and register it into AppServiceProvider
Events and listeners are a bit complex to understand for beginners as it is hard for them to really understand their actually working and use caseModel Observers are fairly simple and easier to understand.

Conclusion

We have reached the end of this post on Demystifying Laravel Model Listeners and Observers for Beginners. If you have any doubts or questions kindly comment below.

Related Posts

Summary
Review Date
Reviewed Item
Demystifying Laravel Model Listeners and Observers for Beginners
Author Rating
51star
1star1star1star1star
Software Name
Laravel Framework
Software Name
Windows Os, Mac Os, Ubuntu Os
Software Category
Web Development