Close
Do you want more articles like this delivered straight to your inbox?

Subscribe now to my e-mail newsletter and get my latest articles and project updates delivered directly to your inbox. Never miss an update.

Laravel Eloquent for Beginners | Full guide (2021)

Published September 3, 2021; last updated on September 10, 2021
Laravel Eloquent for Beginners | Full guide (2021)

Laravel Eloquent is one of Laravel’s flagship features and one of the most notable things that distinguishes Laravel from other PHP frameworks. The Laravel Eloquent ORM is a way to interact with your database. In this tutorial I’ll show you the basics of using Laravel Eloquent, so that you can start using Eloquent quickly.

So, what is Eloquent exactly? Or what is an Eloquent model? It effectively comes down to the following: for each table you have in your database, you create an Eloquent model. An Eloquent model is just a PHP class, that allows you to do two things:

  1. Interact with the database table as a whole. For example: $users = User::all(); gets all users.
  2. Represent a single row in the table. For example: $user = User::first() or $ralph = new User.

This gives you extreme versatility and allows you to perform almost any database operation.

So let’s just get started and show a few examples. In this article I’ll use the default Eloquent User model.

Getting started with Laravel Eloquent

In order to get started with Laravel Eloquent, the database needs to have the correct structure. E.g. the correct tables and their corresponding columns should be accessible. You define that structure in a database migration.

After installing Laravel, you have by default three migrations: one migration for the users table, one for the password_resets table and one for the failed_jobs table. (The latter is for Queues in Laravel. That is an advanced feature and not necessary to dive into that already.)

This is the migration that creates the users table, with a few comments from my side:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
            // Create a table called 'users'
        Schema::create('users', function (Blueprint $table) {

            $table->id();                             // Each user has an id
            $table->string('name');                   // Each user has a name
            $table->string('email')->unique();        // Each user has a unique email
            $table->timestamp('email_verified_at')->nullable();   // Only applicable if you need email verification
            $table->string('password');               // Each user has a password
            $table->rememberToken();                  // Each user has a remember token (to allow him or her to stay signed in
            $table->timestamps();                     // Each user has two timestamps: created_at and last_edited_time

        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');               // To undo this migration, we need to drop the table 'users'
    }
}

Now run one of the below commands in order to run the migration(s):

php artisan migrate
php artisan migrate:fresh   # Start with a fresh, empty database

As you see, the above code is enough to create a database in the correct structure. If you want to learn more about database connections and creating migrations, check out my article on Laravel databases & migrations.

Using your first Eloquent Model

So, what is Eloquent exactly? Each Eloquent model is a file in the App/Models directory. So if you check out the App/Models, directory, you’ll see one default Eloquent model already there: User.php.

How do we use Eloquent models?

use \App\Models\User;

    // Somewhere in your code, perhaps in a controller or in a Livewire component
public function save(Request $request)
{
    // Do some validation stuff here, to be sure that there's nothing malicious coming in

    // Create a new user
    $user = new User;
    $user->name = $request->input('name');
    $user->email = $request->input('email');
    $user->save();

    return redirect()->route('login');
}

All these operations can be performed with one of the simplest Eloquent model that exists:

<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable {}

Let’s see how we work with Eloquent models.

Creating and defining Eloquent models

Let’s create a new Eloquent model. And as always, there’s an artisan command for that:

php artisan make:model Post

This creates the following file in App/Models/Post.php:

<?php

namespace App\Models;

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

class Post extends Model
{
    use HasFactory;
}

If you want to automatically create a migration as well, you can add the -m flag to your artisan command:

php artisan make:model Post -m

Creating Eloquent models in a subfolder

You can also create Eloquent models in a subfolder. For each folder, add it before the name and add a slash. Like this:

php artisan make:model Blog/Post            # Creates App/Models/Blog/Post model
php artisan make:model Blog/Post/Comment    # Creates App/Models/Blog/Post/Comment model

# Import them as 'use \App\Models\Blog\Post;`.

Table names and Eloquent naming convention

The name of the Eloquent model refers usually to the table name. By default, Laravel takes your table name, ‘snake cases’ it and pluralizes it.

Model "Post" becomes tablename "posts"
Model "UserPost" becomes tablename "user_posts"
Model "EmailSubscriber" becomes tablename "email_subscribers"

This is purely convention. Remember this: the Eloquent model name is always singular and, if it consists of multiple words, the words are separated by a capital letter (UserPost). The tablename is always plural, and is “snake_cased” (user_posts). Keep this convention in mind, because it may help you later.

A few examples, the users table has a User model. The categories table has a Catagory model. The user_categories table has a UserCategory model.

Anyway, if your table name is different than what the convention assumes, you can add the following in your class:

protected $table = 'my_posts';

Primary key

By default, Eloquent assumes that every table has a primary autoincrementing integer named id. If you migration contains this (by default):

$table->id();

Then you’re fine. If not, you can set an other key as primary key:

protected $primaryKey = 'post_id';

And you can even set it as an non-incrementing key (advanced use case).

public $incrementing = false;

And you can also change the Eloquent primary key type, if it isn’t an integer (also advanced):

protected $keyType = 'string';

Timestamps

By default Eloquent assumes that every table has a created_at and updated_at column. If your migration contains this (by default):

$table->timestamps();

You’re good to go. If not, tell Eloquent that you’re not using timestamps:

public $timestamps = false;

Getting data from an Eloquent model

Getting multiple rows from Eloquent

Let’s us now see how we use Eloquent and get data from it. First, let’s get all the items. There are two ways to do that:

use App\Models\User;

$users = User::all();
$users = User::get(); // Just as valid, this means exactly the same

foreach ($users as $user) {
    echo $user->name;
}

Should I use all() or get()?

Many people use the all() method, just because it exists. Personally I’d recommend always using the get() method, because you use the get() call also on other queries. For example, you do User::where('column_name', 'value')->get() and not User::where('column_name', 'value')->all().

Importing Eloquent models with use App\Models\ModelName;

In the above example, I imported the User model with use App\Models\User. This allows you to make calls directly on User::get() instead of \App\Models\User::get().

In the following examples I’ll assume you already imported the correct classname.

Filter results

Let’s filter the results a bit with a where() clause:

$admins = User::where('admin', true)->get(); 

foreach ($admins as $admin) {
   echo $admin->email;
}

In the above example, we say that the column admin should match the value of true. We can also add operators in between by adding a new parameter:

    // Users created more than seven days ago. 
$admins = User::where('created_at', '<', now()->subDays(7))->get(); 

Checkout this Stack Overflow question for all the operators you can use and an explanation of them

Eloquent collections

As you’ve seen, the results from ->get() and ->all() don’t just return a plain PHP array. They return a so-called Eloquent collection. A collection in Laravel is a kind of array with superpowers. This allows advanced operations like:

$admins = User::where('admin', true)->get(); 

$admins->each(function ($item) {
    $item->role = 'administrator';
});

There are more methods you can use, please take a look at the documentation about Eloquent collections and regular collections.

Chunking queries

If you have tens of thousands of records, getting them and storing them all in memory might not be the best for your server. To solve that, Eloquent offers a few ways to ‘chunk‘ queries and reduce memory load.

The first way is the Model::chunk() method. Use it like this:

Post::where('column_name', 'matches_this')->chunk(150, function ($posts) {
    foreach ($posts as $post) {
        // Do something with each post
    }
});

This gets 150 records at a time and every time it stores those 150 records in a variable $posts and then you can do a foreach on those 150 records to get individual records.

A better – or syntactically cleaner – way is to use Model::lazy() instead of Model::chunk(). Due to the Eloquent magic, you can just input Model::lazy() in one foreach loop.

foreach (Post::where('column_name', 'matches_this')->lazy() as $post) {
    //
}

Eloquent cursors

Another alternative to chunking is using cursors. A cursor executes only one query and it keeps only one model in memory at a time. This is roughly what happens:

  1. The cursor executes one database query.
  2. The cursor loads the first Eloquent model in memory.
  3. Perform an action on the first Eloquent model.
  4. The cursor deletes the first Eloquent model from memory (NB: not from the database!)
  5. The cursor loads the second Eloquent model in memory.
  6. Repeat.

The downside of this is that you can’t eager-load relationships between models (e.g. a User that has many Posts). We’ll cover relations later.

Another downside is that cursors will still eventually run out of memory, because of caching somewhere. Model::lazy() doesn’t have this problem.

Cursors would be the best way if you’re actively looking to ultra-optimize your application and can handle the downsides. If not, I’d advise using Model::lazy().

foreach (Post::where('column_name', 'matches_this')->cursor() as $post) {
    //
}

There’s some more advanced magic you can do with cursors, like filtering, but which you absolutely don’t need as a beginner (because you can do filtering as well with a where() clause). Check out the whole section on Eloquent cursors in the documentation.

Getting a single row from Eloquent

Now that we’ve seen how to get multiple records and how to process them, let’s take a look at how to get a single record from the database.

The easiest way to get a single record is to look it up by its id. Like this:

$user = User::find(2); // Get user with an id of 2

Another handy way it to use the first() method. This effectively means that you can write queries that potentially give multiple results, but you only get the first one of that.

$user = User::where('admin', true)->first(); // Get the first admin user

And you can also apply some filtering, so that you can get some records in a certain order and only get the first. E.g. you want to get the oldest comment or the latest blog post:

$oldest_comment = Comment::orderBy('created_at', 'asc')->first;
$newest_pos .= Post::latest()->first();

Eloquent firstOrFail() and findOrFail() methods

In the above example, you’ve seen how we can use find() to find a record and first() to get the first record that matches the requirements.

But what happens when no record is found?

In that case, you’ll get null back. That might be good in some simple (or very advanced) situations, as you could just check with is_null(), but it’s more likely that you’ll need to better and more professionally handle situations when there is no record found.

To solve that, Eloquent has two methods for that: findOrFail() and firstOrFail().

$user = User::findOrFail(2); 
$user = User::where('admin', true)->firstOrFail(); 

In this case, when Eloquent did not find an object, it throws an exception. That sounds scary – I mean, why would you want to manually throw exceptions, if you’re only working to fix them? But it isn’t scary at all.

If an exception is thrown, then you need to catch it. Sounds logical, right? If someone throws a ball, someone needs to catch it. Else it falls to the ground and the game (‘app’) grinds to a halt.

In this situation, when we’re talking about not finding Eloquent records from the database, you have two options:

  1. Let Laravel catch and handle the exception automatically.
  2. Catch and handle the exception yourself.

In the case of findOrFail() and firstOrFail(), Laravel throws an Illuminate\Database\Eloquent\ModelNotFoundException when it does not find a model.

If you choose for option 1, you don’t need to do anything, except just appending OrFail() to your methods. Laravel will automatically catch the exception and returns a 404 Not found page.

This is ideal for situations with urls like /posts/edit/5, where 5 represents the post id. If the post is not found, it is perfectly fine and common to return a 404.

But you can also choose to handle it yourself. In that case, wrap the code that potentially returns an error into a try {} catch() {} block:

try {

   // Code that potentially returns an Exception

   $user = User::findOrFail(2); 

   // or
   $user = User::where('admin', true)->firstOrFail(); 
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {

   // Custom error handling *if* there was an exception
   return redirect('/');
}

Updating rows in Eloquent

Now that we’ve seen how to retrieve collections of multiple rows and singel rows, let’s see how we can update values in the database.

How to create a new Eloquent record

There are roughly two ways of creating new Eloquent objects and inserting them (or not) in the database. For this, it is important to first understand the difference between creating and making an Eloquent model.

If you create an Eloquent model, the object is directly saved to the database. If you make an Eloquent model, the object is not directly saved to the database, until you manually call ->save().

The first way to create an Eloquent object is just to create a new instance and edit the properties.

$post = new Post;
$post->title = 'My title';
$post->content = 'Article content here';
$post->author_id = auth()->user()->id; // This assumes that the currently logged in user is the author

// Until here you mainly 'made' this object. At this point, it doesn't exist yet in the database.

$post->save();

// From here on you 'created' the object, meaning that the database now contains this post.

There’s also an other way, one that I like more. The above method code could also be written like this, which is cleaner in my opinion:

$post = Post::make([
                'title' => 'My title,
                'content' => 'Article content here',
                'author_id' => auth()->user()->id,
        ]);

// Until here you mainly 'made' this object. At this point, it doesn't exist yet in the database.

$post->save();
$post = Post::create([
                'title' => 'My title,
                'content' => 'Article content here',
                'author_id' => auth()->user()->id,
        ]);

Making properties fillable in Eloquent

As you see, the difference in those two ways is the difference between:

  1. manually updating each property; and
  2. updating multiple properties with an array.

I prefer the second approach, because it allows for writing less code. There’s one thing though to take note of. Consider the following scenario:

$post = Post::update($request->all());

In this case, we’d be plainly updating all the fields that come with the request data. But what if a nefarious user sends along fields like id? And those fields would be updated as well?

To prevent that scenario, we need to set certain properties to allow mass assignment. Mass assignment purely means that properties are updated from an array. What you’re effectively doing is adding a $guarded or $fillable property to your Eloquent model, with which you’re saying: these and these properties can be changed with an array.

protected $fillable = [ 'title', 'content' ];

Or you can also allow everything and guard only a few specific properties:

   // Allow updating with arrays for every property except author_id
protected $guarded = [ 'author_id' ];

And if you want to allow mass assignment for every field, set $guarded to be an empty array.

protected $guarded = [];

Laravel Eloquent relationships

The last part of Laravel Eloquent is about relationships. As those are very important for how you work with and connect data in Laravel, I’ll dedicate an additional article for advanced use cases, but here I’ll show you the basics to get you up and running quickly.

First, what are relationships exactly? Relationships mean that certain records belong to each other. Consider a user that has many posts. Each post belongs to a category. Each post has many comments. Each comment belongs to another user. Each user has a certain role. Those are all examples of relationships.

What are relations exactly on the most core database level?

The core idea behind relationships is that you have different sorts of data, therefore stored in different tables, and you connect those two. This is done by adding columns like user_id, post_id, category_id and so on to the tables.

In the above example, the posts table has the columns user_id and category_id.

  • The comments table has the columns user_id and post_id. (Each comment belongs to one post and is written by one author.)
  • The categories table doesn’t have any extra xxx_id columns, it only has its own id column.
  • The roles table doesn’t have any extra xxx_id columns as well.
  • The users table has a column role_id.

Creating relationship columns

You can create the required xxx_id columns in your database by using the foreignId() or foreignIdFor() helper in your database migration.

$table->foreignId('user_id');

// or

$table->foreignIdFor(User::class);

Creating relationships

There are a few types of relationships. The most common ones are:

  1. One-to-one relationships: a User has one Role.
  2. One-to-many relationships: a User has many posts. Even if the user only has one post, the user has the potential to have more posts (when it creates one).

Creating an ‘One-to-one’ or ‘has one’ relationship

Creating an Eloquent relationship is as simple as adding a new method to our Eloquent model. (NB: not the migration.)

Imagine a User that has one, unique Membership. That means that the memberships table has a user_id column.

Add this to the User model:

public function membership()
{
    return $this->hasOne(Membership::class);
    // This returns a single item
}

And this is for the Membership model:

public function user()
{
    return $this->belongsTo(User::class);
    // This returns a single item
}

Use it like this:

$user = User::find(1);

$membership = $user->membership = 'Unique membership title';
$membership->save();
$user = Membership::find(1)->user;

$name = $user->name;

Creating an ‘One-to-many’ or ‘has many’ relationship

Imagine the common example that a user has many posts, or many items. In this situation, add a xxx_id column to the database table of which there are many items. In this situation, there is always one user and multiple posts. You can’t easily add all the post_id‘s to the user table, but you can add a user_id to the posts table.

   // User model
public function posts()
{
    return $this->hasMany(Post::class);
    // This is an Eloquent collection
}

And this is for the Post model:

public function user()
{
    return $this->belongsTo(User::class);
    // This returns a single item
}

Other Eloquent relationships

There are also relationships like Many-to-Many that allows a user to has multiple posts and a post to belong to multiple users (multiple authors for a single post). This is solved by creating a sort of ‘intermediary’ pivot table between the users and the posts.

Those are all advanced variations of Eloquent relationships, so it’s certainly not necessary to know them right now, but it can be handy to know that they exist.

In a next I’ll explain Eloquent relationships more in depth and show you all the advanced use cases. Sign up for my newsletter if you’d like me to send it to you.

Conclusion

As we’ve seen, Eloquent is not difficult to get started with and allows a lot of versatility. It increases the readability of your code and will save you a lot of time. As such, I’d highly recommend learning how to use. Even if you’re learning only one new method every day, you’ll soon be an Eloquent pro.

Thank you for reading! Feel free to leave a comment with any questions or additions or subscribe to my newsletter if you want more stuff like this.

Stay up to date with all things Laravel, Tailwind, WordPress & PHP

Subscribe now to my e-mail newsletter and get my latest articles and project updates delivered directly to your inbox. Never miss an update.

Image Ralph J. Smit
Ralph is a designer gone developer. He happily lives in the Netherlands. His passion for good design drove him towards development, because he felt that no-code tools were too limiting. On this blog, Ralph writes the articles he would've wanted to have during his continual developer journey. → Follow on Twitter

Comments

Leave a reply

Your email address will not be published. Required fields are marked *