Laravel Observers

Mohammed Osama
6 min readMar 30, 2018

--

There might be a process we regularly do such as slugifying the post titles , formatting date-time in a proper way and so on..
whenever it comes to such a routine process, you really have to give up on the regular convention that you follow which is similar to this

public function store(){    $post = new Post;
$post->title = str_slug(request()->title);
$post->user_id = auth()->id();
$post->save();
}
// or following this convention.
public function store(){ request()->merge([
'title' =>str_slug(request()->title) ,
'user_id' => auth()->id()
]);
Post::create(request()->all());
}

or this

created {{ $post->created_at->diffForHumans() }}

It starts to get messy a little bit even we don’t have many processes to do at the moment . for this, You have two ways to overcome these silly conventions. Either by Accessors & Mutators or by Observers.

  1. Accessors & Mutators

Laravel accessors and mutators are custom, user defined methods that allow you to format Eloquent attributes. Accessors are used to format attributes when you retrieve them from the database, while mutators format the attributes before saving them to the database.

1.1 Defining an Accessor

Syntax for defining an accessor is getFooAttribute() where Foo is capitalized attribute you want to access. If your attribute is first_name, and you want to be sure that first name will always be camel cased when you fetch it, you’ll need to define an accessor function within your model as follows :

// controlling the first_name attributepublic function getFirstNameAttribute($value){
// $value = alex
return ucfirst($value); // Alex}

This method now will uppercase the first character of this first name, once you just do something like that

{{ $user->first_name }} // Alex

Notice now you didn’t have to uppercase the first character of the first name, that’s just automatically rendered behind the scenes in the User model, so you don’t do this process over and over again.

Now, Let’s fix the created_at issue, we did on the start with accessors.

public function getCreatedAtAttribute($value) {   // this is only for demonstration purposes,I don't encourage you to do that unless you really don't need the carbon instance within your application. 
// $value = '2018-03-30 2:12:27'
return \Carbon\Carbon::createFromFormat(
'Y-m-d H:i:s',$value)->diffForHumans(); // 10 mins ago.

}

Now, once you call the created_at attribute, you will automatically get the created_at format in human readable format. which saves much routine process you could do.

1.2 Defining A Mutator

Syntax for defining a mutator function is setFooAttribute() where Foo is a camel cased column you want to access. So, once again, let’s use our first_name column, but this time we want to make change before saving it to the database:

public function setFirstNameAttribute($value){
// value = alex
$this->attributes['first_name'] = ucfirst($value); //Alex}

You probably notice that here we don’t return anything. We directly access the attribute and set a new value. With this approach we can be sure that first name will always be capitalized.

Following this convention, we could always get rid of routine processes such as injecting the user_id column whenever user creates a post, we could do that using Mutators. that’s really useful, Let’s try that.

public function setUserIdAttribute($value){
$this->attributes['user_id'] = auth()->id;}

Laravel is going to squawk

We actually don’t have the user_id attribute within this insertion, which causes the squawk as the accessors and mutators return you the created and retrieved version , not while retrieving nor creating version . That’s why we are encountering this issue.

Does this mean that we can’t overcome this routine issue? , Here where the Observers come into the play to help us with that.

2. Observers

Observers supply you with powerful events that literally lets you control every single process happens with database.

Here is a list of events you might use during dealing with the database.

Imagine that you are literally controlling creating,retrieving,updating,deleting process while dealing with the database, not just that but you also can control once the process is finished, what shall we do next ?, That’s just completely powerful.

Now Let’s see how to launch an observer.

Let’s create a new service provider.

php artisan make:provider EloquentEventServiceProvider

this will just create a new regular Service provider, we just created that to avoid having a messy service provider, Just doing a single service provider which is in charge of a single responsibility , That’s just way cleaner convention to follow.

Now, Let’s open this Service Provider, and observe a particular case which is while creating a post for the user.

Remember , The issue at the accessors and mutators that they deal with the created and retrieved version, which couldn’t let us inject the user_id column behind the scenes.

For accomplishing this with observers, we will have to deal with the creating event , not created one.

Let’s go to the EloquentEventServiceProvider and within the boot method, we will fire the creating event, which in essence means that once we are creating a post, don’t created it directly, but go to this event , check what’s going on there then create the post.

public function boot() {
\App\Post::creating(function ($post) {
$post->user_id = auth()->id();
});
}

Following this convention, the post is created, we avoided to inject the user_id every single time we are going to create a new post which is a silly convention to follow.

Similarly , you have the authority to fire all of the other registered events we previously mentioned.

but, things might get a little bit messy again within this service provider as you don’t have this provider only for the Post model, but for all of the models, for thus a thing, we can create a class which in charge of post events , and just mention the Post model to observe every single event that’s going to happen within this class.

so by the end of this code the service provider is going to be like this.

\App\Post::observe(\App\Observers\PostObserver::class);

unfortunately Laravel doesn’t supply us with make:observer command, and you have to create that manually.

Or you can create your own custom command to make an observer, I’ve already done that in this article

Go to the App folder, and create Observers folder, and create PostObserver.php file

The file content is going to be similar to this.

<?phpnamespace App\Observers;use App\Post;class PostObserver
{
public function creating(Post $post)
{
$post->title = str_slug($post->title);
$post->user_id = auth()->id();

}
public function retrieved(Post $post)
{

$post->title = ucfirst(str_replace('-', ' ', $post->title));

}
public function deleting(Post $post)
{
// perhaps you have comments, replies, likes related to this post.
// you might do this then, to avoid routine processes.
$post->likes()->delete();
$post->comments()->delete();
}

This file as it mentions out, we can just control there every single event we have previously mentioned, For instance, I’ll just control the creating,deleting,retrieved processes for demonstration.

  • Creating

While creating a post, get me the title, slugify it. also, inject the user_id column

  • Retrieved

whenever we get a post, by finding it or whatever.

Just revert back to the regular format of title, which is having a white space instead of the hyphen

  • deleting

while deleting the post, you can also there delete the likes, comments and so on which are relevant to this post.

Finally, you really got to think of making your code cleaner and avoid repetitive processes.

--

--