Hidden Gems in Eloquent ORM — Laravel 7.x

Kenneth Sumang
7 min readMar 24, 2020
Photo by Akshay Nanavati on Unsplash

Introduction

Many of us use Eloquent when programming with Laravel. But believe it or not, there are many hidden gems in Eloquent that is waiting to be revealed. That is, many uncommon commands make Laravel programming relatively easier.

Model Hooks

At some point, we may need to do something when someone registered for our app. It may be something like, send a welcome email, or subscribe them to our newsletter. You can put it after the record insertion, like this:

Model Hooks — registerUser

It is feasible, but it violates SRP (Single Responsibility Principle) because now our function also takes care of the post-registration tasks like this. One nifty workaround for this is to use model hooks.

Eloquent fires several events, some of which are created, updated, deleted, and many more. We can use these built-in model events for our little problem here.

First, you need to create a protected function in your Model called boot. Then add your action for your selected event next to the parent::boot() method call. In this instance, we will send a welcome email to the user when they register in our app.

Model Hooks — boot

If this code looks messy to you, you can try to create an Observer class by running php artisan make:observer UserObserver — model=User.

This command will create an Observer class that looks like the following:

Model Hooks — UserObserver

Just put the action in the appropriate function here in the observer and register the observer in the boot method of your service provider like this:

Model Hooks — Bootstrap

So much cleaner, right?

Querying Big Datasets

Imagine that you are a programmer in a super popular grocery shop, where they make 10000 transactions per day, and here you are, tasked to get a summary of the transactions per week. Querying 70,000 in one go is a bit consuming, right? Our solution to that is to chunk the data first then process it. We can do that by calling the chunk() function.

Querying Big Datasets with chunk()

In this example, we are slicing the whole dataset bit by bit so that it will hog fewer resources.

Relationships

Sometimes, we have a database structure that makes use of relationships. This can be dealt with by a series of JOINS, but sometimes multiple join queries make our code unreadable. Luckily, Eloquent provides a way to implement relationships gracefully.

One to One

For example, a User has a dedicated UserDetail record in the database.

One-to-one relationship

To access a User’s UserDetail, we add this method to the User model.

One to One — userDetail()

And call it like this:

$oUserDetails = User::find(1)->userDetail;

We can also access the User model via our UserDetails. We need to add this method to our UserDetails model.

One to One — user()

And call it like this:

$oUser = UserDetail::find(1)->user;

One to Many

For example, an Order may contain multiple OrderItems.

One-to-many relationship

To have a list of the OrderItems in an Order, we add this method to the Order model.

One to Many — orderItems()

And call it like this:

$oOrderItems = Order::find(1)->orderItems;

We can also access the Order information from an OrderItem. To do this, we add this method to the OrderItem model.

One to Many — order()

And call it:

$oOrder = OrderItem::find(1)->order;

Many to Many

For example, you are doing a blogging site, wherein a tag is associated with any number of posts, and a post can have any number of tags. For this example, you may have this relationship:

Many-to-many relationship

To get the tags associated with a post, you add this method to the Post model.

Many to Many — tags()

And access it by calling this:

$oTags = Post::find(1)->tags;

To get the posts that have a certain tag, you add this method to your Tag model.

Many to Many — posts()

And access it by calling this:

$oPosts= Tag::find(1)->posts;

But there is a problem. Imagine if you fetch, let’s say 50 posts. To fetch their tags with them, you need to run 1 additional query to get them. This is a very notorious N+1 problem. For 50 posts, maybe it isn’t much, but for something as big as Medium or Wordpress, this can be a little resource expensive.

But worry no more! Eloquent has our backs covered! The solution is to use a function called with.

$oPosts = Post::with('tags')->get();

The output is a Collection of all Posts with its corresponding Tag models.

Polymorphic

Imagine, you need to add an image in the user profile and the post in a blogging site. In a sense, you may be doing two tables or migration at the moment, right? What if I said that you would only need a single migration for these two image functionality? Shocking right? How would Laravel handle the primary keys?

Polymorphic Relationship

Assuming that you already have a model and migration for your user profile and post, we only need to create a migration for the Images. You can run php artisan make:model Image -m to create a model and a migration for that model.

Now, go to your newly created migration and let me discuss the required fields.

In a polymorphic table, there are two required fields (excluding the PK), the id of the table that record was referring to, and the string name of the model that the record was referring to.

In our example, the migration most likely looks like this:

Polymorphic — Migration up()

Then, you can now migrate to the table.

Next, to access an image in your post and user, we need to set up a relationship method on the Post and User Models.

Polymorphic — image()

Note: When for example, a post can have many images, you must use morphMany function instead of morphOne in the Post model.

For the inverse of the relationship, we need to add a method on the Image model.

That’s fairly easy, right? Compared to a series of separate functions and relationships for Post and User, now, we have only one model for the images for the post and user. That minimizes some of the long and duplicated conditions and relationships.

Accessors and Mutators

Accessors and mutators can reduce code duplications for formatting data when you are setting or getting data from an Eloquent model every time.

Accessors

Accessors are methods that happen before they give you the data from a model. You can use it to format data from the database properly. For example, you can use it to format the incremental primary key to follow the format of order code.

To declare an accessor, first, you need to determine the field that you want to put an accessor on. In our example, it is the id.

Then, create a function named “get” + field name with uppercased first letter of every word of the field + “Attribute”. In our example, it will be named “getIdAttribute”.

Return the properly formatted value on the function.

Accessors — getIdAttribute()

Mutators

Mutators are methods that happen after you set data to the model. You can use it to properly format a user input before database insertion, or maybe encrypt user information before saving it.

To declare a mutator, first, you have to determine the field you want to put a mutator on. For example, we need to encrypt a user’s bank_account_number before inserting it into the database.

Then, create a function named “set” + field name with uppercased first letter of every word of the field + “Attribute”. In our example, it will be named “setBankAccountNumberAttribute”.

Set the mutated value to the attributes class variable of the model.

Mutators — setBankAccountNumber()

Attribute Casting

Since the release of Laravel 5.6, all Eloquent model classes have $casts property that provides a convenient method of converting your attributes to common data types. The supported cast types are integer, real, float, double, string, boolean, object, array, collection, date, datetime, and timestamp.

For example, in our database, we have a field in our user's table that is named is_admin. We would like is_admin attribute to be true or false when we fetch a User. You can use accessors and mutators to do this, but there is a much easier way to deal with this one.

Attribute Casting — casts array

That’s it. Super easy right?

Scopes

For example, you have a products table. Your product table has a stocks field. When you search for products, you can have this code:

$oProducts = Product::where('stocks', '>', 0)->where('active', 1)->get();

But you also use this kind of query when a customer checks out a product. You need to know if that product has available stock and is active over and over again. Looks like code duplication, right? We’re lucky that Laravel provided us a beautiful solution to this.

First, we will add a method in our Model.

Scopes — scopeAvailable()

This is how to apply local scopes.

You can also create a local scope with parameters. Here’s how.

Scopes — scopeOfSeller()

You can also define a scope wherein you can use it in different models.

First, you have to create a class that implements \Illuminate\Database\Eloquent\Scope.

Scopes — ActiveScope Class

Then, you have to register the scope to your model.

Scopes — boot()

Don’t like to create a new class? You can do it anonymously.

Scopes — boot()

These are called global scopes.

But what if you want to execute a query without a global scope? Well, that’s easy.

Scopes — Without Global Scopes

There, the duplications will be minimized this way.

Required Version

All of the features described here are working in Laravel 6.x and 7.x.

Conclusion

I hope you have learned something you can apply to your personal project, or maybe to your next company project. Let’s all code clean and organized!

--

--