Laravel 12 Eloquent Tips and Tricks for Beginners
1 Understanding Eloquent Relationships
One-to-Many Relationship Example
In this example, we will define a Post and Comment relationship where each post can have many comments. Post Model:
class Post extends Model
{
public function comments()
{
return $this->hasMany(Comment::class);
}
}
Comment Model:
class Comment extends Model
{
public function post()
{
return $this->belongsTo(Post::class);
}
}
Fetching Comments for a Post:
$post = Post::find(1);
$comments = $post->comments;
Laravel supports several other relationship types including one-to-one, many-to-many, has-many-through, and polymorphic relations. These are essential when building real-world applications like REST APIs in Laravel 12.

2 Use Attribute Casting for Automatic Data Transformation
// app/Models/User.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Casts\Attribute;
class User extends Model
{
protected $casts = [
'is_admin' => 'boolean',
'settings' => 'array',
'born_at' => 'datetime:Y-m-d',
'data' => 'json', // Laravel 12+ improved json handling
];
// Custom accessor (getter)
protected function fullName(): Attribute
{
return Attribute::make(
get: fn ($value, $attributes) => $attributes['first_name'] . ' ' . $attributes['last_name'],
);
}
}
Usage:
$user = User::find(1);
echo $user->is_admin; // true/false instead of 1/0
echo $user->full_name; // "John Doe"
$user->settings = ['theme' => 'dark']; // auto JSON encoded
Eloquent ORM is one of the most powerful features in Laravel 12. It provides an elegant, ActiveRecord-style interface for working with your database — turning complex SQL operations into simple, expressive PHP code. Whether you are just starting with Laravel or looking to level up your skills, mastering Eloquent is essential for building efficient, maintainable applications.In this comprehensive guide, we will walk you through 11 must-know Eloquent tips and tricks for Laravel 12. Each tip includes practical code examples that you can apply immediately in your projects. If you are new to Laravel 12, you may also want to check out our guide on How to Build a REST API in Laravel 12 or learn about Laravel 12 Routing and Middleware to strengthen your foundation before diving in.
3 Avoid N+1 Query Problem with Eager Loading
// Bad - N+1 queries
$posts = Post::all();
foreach ($posts as $post) {
echo $post->user->name; // extra query per post!
}
// Good - one query for users
$posts = Post::with('user')->get();
foreach ($posts as $post) {
echo $post->user->name; // no extra queries
}
// Nested eager loading
$posts = Post::with('user.profile', 'comments.user')->get();
Use withCount() for counting without loading full models:
$posts = Post::withCount('comments')->get();
echo $posts->first()->comments_count;
4 Local Scopes – Reusable Query Logic
// app/Models/Post.php
class Post extends Model
{
public function scopePublished($query)
{
return $query->where('published_at', '<=', now())
->where('is_draft', false);
}
public function scopePopular($query, $minViews = 1000)
{
return $query->where('views', '>=', $minViews);
}
}
Usage:
$published = Post::published()->get();
$popularPublished = Post::published()->popular(5000)->get();
5 Soft Deletes with Eloquent
Soft Deletes Example
In the Post model, add the SoftDeletes trait:
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
use SoftDeletes;
}
Make sure your migration includes the deleted_at column:
$table->softDeletes(); // adds nullable deleted_at column
Now, when you delete a record, it is marked as deleted in the database but not actually removed:
$post = Post::find(1);
$post->delete(); // sets deleted_at timestamp
To retrieve soft-deleted records:
$posts = Post::withTrashed()->get(); // includes soft-deleted
$post->restore(); // restore a soft-deleted record
$post->forceDelete(); // permanently remove
6 Accessors & Mutators – Clean Data Handling
protected function firstName(): Attribute
{
return Attribute::make(
get: fn ($value) => ucfirst($value),
set: fn ($value) => strtolower($value),
);
}
Now $user->first_name is always capitalized, and stored lowercase. 7 Chunk Large Datasets to Avoid Memory Issues
User::where('active', true)->chunk(200, function ($users) {
foreach ($users as $user) {
// Process user (e.g. send email)
$user->notify(new WelcomeEmail());
}
});
Laravel 12 also supports lazy() collections for even more memory-efficient processing:
User::where('active', true)->lazy()->each(function ($user) {
$user->notify(new WelcomeEmail());
});
For background processing of large jobs, consider using Laravel 12 Queues and Jobs to offload heavy operations. 8 Advanced Relationships Tricks
// Users who have published posts in last 30 days
$activeUsers = User::whereHas('posts', function ($query) {
$query->where('published_at', '>=', now()->subDays(30));
})->get();
// Has many through with constraint
class Country extends Model
{
public function posts()
{
return $this->hasManyThrough(
Post::class, User::class,
'country_id', 'user_id', 'id', 'id'
)->where('published', true);
}
}
9 Custom Collections for Model-Specific Logic
// app/Collections/PostCollection.php
namespace App\Collections;
use Illuminate\Database\Eloquent\Collection;
class PostCollection extends Collection
{
public function published()
{
return $this->filter(fn($post) => $post->published_at?->isPast());
}
public function summaries()
{
return $this->map->only('title', 'excerpt');
}
}
// In model:
class Post extends Model
{
public function newCollection(array $models = [])
{
return new PostCollection($models);
}
}
Usage: $posts = Post::all()->published(); 10 Model Factories with Mass Insert (Laravel 12+)
// database/factories/PostFactory.php
Post::factory()->count(100)->make()->toArray(); // prepare data without saving
// Bulk insert (faster than create() for large datasets)
Post::insert(
Post::factory()->count(500)->make()->toArray()
);
Note: insert() does not trigger Eloquent events or set timestamps automatically. If you need timestamps, add them in your factory definition or use now() manually. Table Of Content
11 Debugging Queries & Performance Tips
DB::enableQueryLog();
$users = User::with('posts')->get();
dd(DB::getQueryLog());
Other performance tips: - Use select() to load only needed columns: User::select('id', 'name')->get();
- Prefer exists() over count() > 0 for checking record existence
- Use database indexes on columns frequently used in where() clauses
- Use toSql() to preview the generated SQL: Post::where('active', true)->toSql();
12 Conclusion
Experiment with these tips in a fresh Laravel 12 project (composer create-project laravel/laravel laravel12-tips). Your future self (and your users) will thank you for faster, cleaner applications.
Continue Learning Laravel 12:
- How to Build a REST API in Laravel 12 — Apply Eloquent skills to real API development
- Laravel 12 Routing and Middleware Guide — Master request handling alongside Eloquent
- Laravel 12 Performance Optimization Tips — Take your app speed to the next level
- Laravel 12 Queue and Jobs Tutorial — Offload heavy Eloquent operations to the background
- Common Laravel 12 Errors and Fixes — Troubleshoot common pitfalls quickly
- Laravel 12 Authentication with WorkOS AuthKit — Secure your Eloquent-powered app
Related Laravel 12 Guides
Written by Revathi M
PHP Developer & Technical Writer · 10+ years building web applications with CodeIgniter and Laravel
Revathi specializes in PHP backend development, authentication systems, and REST API design. She writes practical, production-tested tutorials at Get Sample Code to help developers build secure applications faster.
Frequently Asked Questions
Laravel Eloquent is an ORM (Object-Relational Mapping) included with the Laravel framework. It provides an elegant ActiveRecord implementation for working with your database, allowing developers to interact with database tables using PHP model classes instead of writing raw SQL queries. Each model corresponds to a table, and you can define relationships, scopes, accessors, and mutators within your models.
The N+1 query problem occurs when your code executes one query to fetch a collection of records, and then runs an additional query for each record to fetch related data — resulting in N+1 total queries. For example, fetching 100 posts and then accessing each post's author triggers 101 queries. The solution is eager loading using the with() method, which reduces this to just 2 queries.
To use soft deletes in Laravel, add the SoftDeletes trait to your Eloquent model and ensure your database table has a deleted_at timestamp column (use $table->softDeletes() in your migration). When you call delete() on a model, Laravel sets the deleted_at timestamp instead of removing the row. You can retrieve soft-deleted records with withTrashed(), restore them with restore(), or permanently delete with forceDelete().
Query scopes in Laravel are reusable query constraints defined as methods in your Eloquent model. Local scopes are prefixed with scope (e.g., scopePublished) and can be chained fluently like Post::published()->get(). Global scopes automatically apply to all queries for a model. Scopes help keep your controllers clean by encapsulating common filtering logic inside the model itself.
Eager loading is a technique in Laravel where related models are loaded upfront in a single query, rather than being lazily loaded one at a time. By using Post::with('comments')->get(), Laravel fetches all posts and their comments in just two queries instead of one query per post. This avoids the N+1 query problem and can dramatically improve application performance, especially when dealing with large datasets.
