Ralph J. Smit Laravel Software Engineer
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:
-
Interact with the database table as a whole. For example:
$users = User::all();
gets all users. -
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 migratephp 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 componentpublic function save(Request $request): RedirectResponse{ // 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 modelphp 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'; $item->save();});
(I know that you could achieve the above example in a faster and better way by using ->update()
. I just used it here for demonstration purposes.)
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:
-
The cursor executes one database query.
-
The cursor loads the first Eloquent model in memory.
-
Perform an action on the first Eloquent model.
-
The cursor deletes the first Eloquent model from memory (NB: not from the database!)
-
The cursor loads the second Eloquent model in memory.
-
Repeat.
The downside of this is that you can't eager-load relationships between models (e.g. a User
that has many Post
s). 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:
-
Let Laravel catch and handle the exception automatically.
-
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 throws 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:
-
manually updating each property; and
-
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_idprotected $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 columnsuser_id
andpost_id
. (Each comment belongs to one post and is written by one author.) -
The
categories
table doesn't have any extraxxx_id
columns, it only has its ownid
column. -
The
roles
table doesn't have any extraxxx_id
columns as well. -
The
users
table has a columnrole_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:
-
One-to-one relationships: a
User
has oneRole
. -
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(): HasOne{ return $this->hasOne(Membership::class); // This returns a single item}
And this is for the Membership
model:
public function user(): BelongsTo{ 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 modelpublic function posts(): HasMany{ return $this->hasMany(Post::class); // This is an Eloquent collection}
And this is for the Post
model:
public function user(): BelongsTo{ 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.
Published by Ralph J. Smit on in Laravel . Last updated on 11 March 2022 .