Laravel Eloquent Performance Patterns - Part 2
Hello everyone, today I will describe our Laravel eloquent performance part - 2. It's an advanced level topic so I recommend you know the basics of Laravel. How to create a Model, Controller, how it works, and how to call this in view blade. You can find so many tutorials about this so just check that and keep learning. Let's begin our series ๐.
Here we talk about the memory usage of a Laravel application. How much memory it take when you called a view. How much data it send and how much memory it takes. Here we create a post related blog application. Here we have two models 1) User, 2) Post. Let's create this and also create PostController.php where you have a public function index(). We pass our all data throw this function.
Here, posts table migration file ๐
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('author_id')->constrained('users');
$table->string('title');
$table->string('slug');
$table->longText('body');
$table->dateTime('published_at');
$table->timestamps();
});
๐ In the posts table you see there is published_at
colomun whis was dateTime formate. Our database has 20 users & 100 posts. Each post has a user which means the author. Each post has a published date. So we group the all post by year.
So, let's create a relation between User & Post Models. Check the code ๐
public function post()
{
return $this->hasMany(Post::class, 'auhtor_id');
}
public function author()
{
return $this->belongsTo(User::class, 'author_id');
}
Okay, we done our relation part of the model. Now let's jump to the controller where we fetch all the data of post. So, go to PostController.php file and create index() function ๐
public function index()
{
$years = Post::query()
->with('author')
->latest('published_at')
->get()
->groupBy(fn ($post) => $post->published_at->year);
return view('elequent.part-2.index', compact('years'));
}
Now, let's jump to our blade file. Where we actually show the data group by year. Like each post categories by year.
@extends('layouts.app')
@section('content')
<div class="container">
@foreach($years as $year => $posts)
<div class="row justify-content-center">
<div class="col-md-8 mt-2">
<h2>{{ $year }}</h2>
<hr>
<div class="row">
@foreach ($posts as $post)
<div class="col-sm-6 mb-2">
<div class="card">
<div class="card-body">
<blockquote class="blockquote mb-0">
<p><a href="#">{{ $post->slug }}</a></p>
<footer class="blockquote-footer">Posted {{ $post->published_at->toFormattedDateString() }} by {{ $post->author->name}} <cite title="Source Title">Source Title</cite></footer>
</blockquote>
</div>
</div>
</div>
@endforeach
</div>
</div>
</div>
@endforeach
</div>
@endsection
In foreach loop, we loop this by year so we can easily fetch the data by each year. But here you found a problem let's see the controller we group this post by published_at so this is not assigned with casts variable if you hit fresh in our browser we get an error. Let's set this ๐
class Post extends Model
{
use HasFactory;
protected $casts = [
'published_at' => 'datetime'
];
}
Okay, we did this let's refresh our browser and see if everything is loaded. But if you looked at the debugbar, you see the takes more memory which is not needed. It takes a little bit more memory. If you press the query tab in debugbar you see the SQL. We see it takes all the posts column which has contain the body of the post. So that is the reason to take a little bit more memory here. On this page, we just show the post title with also the slug so we modify our controller and add a select option that optimizes the data. Let's modify ๐
public function index()
{
$years = Post::query()
->select('id', 'title', 'slug', 'published_at', 'author_id')
->with('author')
->latest('published_at')
->get()
->groupBy(fn ($post) => $post->published_at->year);
return view('elequent.part-2.index', compact('years'));
}
We select the id, which is needed for use eloquent. Eloquent expects the primary key to be there so that's why we select id. We need title, slug, and published_at for the group by year and need author_id for a relationship with the author.
Let's more one tip, we can use the same technic with the relationship. We can fetch the name, bio, social media link what ever we need. We can use a callback function to fetch necessary data. let's see ๐
public function index()
{
$years = Post::query()
->select('id', 'title', 'slug', 'published_at', 'author_id')
->with(['author' => function($query) {
$query->select('id', 'name');
}])
->latest('published_at')
->get()
->groupBy(fn ($post) => $post->published_at->year);
return view('elequent.part-2.index', compact('years'));
}
You can hit refresh and see the query tab and see we fetch only the id and name from the author. Actually, Laravel also gives us a shortcut for this so let's jump to the controller and modify this ๐
public function index()
{
$years = Post::query()
->select('id', 'title', 'slug', 'published_at', 'author_id')
->with('author:id,name') // this is the short form
->latest('published_at')
->get()
->groupBy(fn ($post) => $post->published_at->year);
return view('elequent.part-2.index', compact('years'));
}
So this is the nice little shorthand avoid the callback function. At this point are you using this thing all your entire application. I think most probably not. You just this in index page or where you just need to show only title or slug in huge data. Like dashboard or show list data something like that.
That's all today. I hope you will know a little bit from my article. Please stay with us for the 3rd part of this series. So, if you want to check part 1 just click the link below ๐ and check out. see you in part 3.
Laravel Eloquent Performance Patterns - Part 1
I am also a big fan of Tailwindcss, so you check out our website for Tailwindcss template and components ๐
tailkitpro.com