Events in Laravel 5 and customising Model save

Laravel has always been a treat to work with and the new version not only brings a lot of added features but also gives a much structured folder structure which will help organize the code even much better. One of the great features of Laravel 4 for me is events which I have shown in my tutorial Laravel 4 how to use Events. But in Laravel 5 the code is structured even better. In this tutorial, I will show you how we can use Events in Laravel 5 and how we can customize the default mode’s save function to fire an event on every save.

Creating the Model and it’s Migration

Laravel has made a lot of changes to Artisan; so a lot of old commands which I got use to has changed. Let’s first start with creating a Model named Post. The command for this is “php artisan make:model”. So for us the command will be “php artisan make:model Post”.

In the migration file I have made some additions, first – I want the drop to first check if the tablet exist (this additional check I generally do because I have a tendency to mess up things during the development phase and so at that I don’t have to go and manually drop tables if my migrations go wrong).

Second, I added title and body fields for post with comment function adding the comment string for that particular column. I personally feel comments are very important because it helps developer know what the column is saving at a glance without going into code files or scanning through multiple tables.

public function up()
{
  Schema::dropIfExists('posts');
  
  Schema::create('posts', function(Blueprint $table)
  {
    $table->increments('id')->comment('Unique identifier');
    $table->string('title', 128)->comment('Title of the post');
    $table->text('body')->comment('The entire content of the post');
    $table->timestamps();
  });
}

You can find the code here: Post table migration

Once these two modifications are done, we are good to start with the model. Nothing much – first I added the protected $table code. It’s generally not required if the table name is plural and the model is singular. But still as a convention it’s better to add that one line of code.

After that we override the save function of Model by calling the parent::save function. The basic idea behind this override is that when save is called for Post, I will trigger an event. That event will handle the different actions that needs to be performed – like I will write a sudo function called sendMail which can send out SMTP based emails to all users notifying that a new post has been added.

public function save(array $options = array())
{
  parent::save($options);
  
  $user = $options['user'];
  $post = $options['post'];
  
  \Event::fire(new PostAdded($user, $post));
}

Creating the Event, Event Handler classes and then fire the events on Save

As per the documentation provided on Laravel’s site we need to create one class for the event in our case say PostAdded. This class will reside insidde app/events folder and a Handler class for the event.

So first command “php artisan make:event PostAdded”. This command will give us a class file PostAdded.php inside app/events folder. Now we need to notify all users about a new post, so we will need the Post object and let’s also take the User object so that we can also notify who created the post.

So, we add two attributes to the class User and Post and assign them through the constructor. Once this is done our event class should look like this:

class PostAdded extends Event {
  
  use SerializesModels;
  
  public $user;
  public $post;
  
  /**
   * Create a new event instance.
   *
   * @return void
   */
  public function __construct($user, $post)
  {
    $this->user = $user;
    $this->post = $post;
  }
  
}

You can find the code here: PostAdded event file

Now, we will create the Handler class. Command to create a handle class for us will be “php artisan handler:event HandleNewPostAdded –event=PostAdded”.

Once we run this command, we will get out handler class. In this the handle method will be called. And it gets an event object which will have everything which we have passed through the constructor of the PostAdded event class.

So, same as event in this class I create two attributes $user and $post. Inside the handle function, I initialize them. Then I can write my sudo function sendEmailNotification which will do have the logic on sending email through what ever means required.

Here is how my final code looks like:

class HandleNewPostAdded {
  
  protected $user;
  protected $post;
  
  /**
   * Create the event handler.
   *
   * @return void
   */
  public function __construct()
  {
    //
  }
  
  /**
   * Handle the event.
   *
   * @param  PostAdded  $event
   * @return void
   */
  public function handle(PostAdded $event)
  {
    $this->post = $event->post;
    $this->user = $event->user;
    \Log::info(print_r($event, true));
    $this->sendEmailNotification();
  }
  
  private function sendEmailNotification()
  {
    $postTitle = $this->post->title;
    \Log::info("An email was sent to notify user that a new post has been added post title {$postTitle}");
  }
  
}

You can find the code here: HandleNewPostAdded event handler file

Register the event

Once the classes are ready, we need to register then inside EventServiceProdvider which can be found inside app/providers. Here we need to specify which event and which Handler class will handle the event. Below is the $listen variable:

protected $listen = [
  'event.name' =$ [
    'EventListener',
  ],
  'App\Events\PostAdded' =$ [
    'App\Handlers\Events\HandleNewPostAdded',
  ],
];

Fire the event

Now that Event is taken care of, let’s write the save part and fire the event. I will do the code inside the routes file itself because right now UI is not our main concern.

So, a Route in the routes.php file (one of my fav files to get started)

Route::get('save-post', function()
{
  $post = new App\Post;
    
  $post->title = 'This is a random title';
    
  $post->body = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer convallis nisi justo, non tincidunt ex viverra vel. Ut blandit justo at facilisis dapibus. Nam fringilla bibendum diam, sit amet laoreet purus porttitor quis.';
    
  $post->save([
    'user' => Auth::user(),
    'post' => $post
    ]);
});

If you see, the save has changed a bit inside my model because I will need the user and the post object in my event handler class which I am passing them through save. Inside the model – Post, I get them as $options.

Once the save is done, I have fired an event which calls the PostAdd event class and pass the user and post object. That is fetched by the constructor inside PostAdd php file inside events folder. Those two data are then available to the Handler class.

This is how we can manage events and divide code in our application so that Model will do only things which Model should do and rest of the heavy lifting can be done through event classes and their event handlers.