Laravel Models: A Comprehensive Guide
19 May 2025 | Category: Laravel
In Laravel, Eloquent ORM (Object-Relational Mapping) models are used to interact with database tables, providing an intuitive, object-oriented way to perform CRUD (Create, Read, Update, Delete) operations. Each model represents a database table and allows you to query and manipulate data using PHP objects. This SEO-friendly, plagiarism-free guide explains how to create, configure, and use Eloquent models in Laravel, including relationships, mass assignment, and common operations. Based on Laravel 11 (as of May 19, 2025), this tutorial builds on previous discussions about migrations and is designed for beginners and intermediate developers.
What is a Laravel Model?
A Laravel model is a PHP class that extends Illuminate\Database\Eloquent\Model and maps to a database table. It provides methods to query the table, define relationships with other tables, and handle data operations. Eloquent models simplify database interactions by abstracting raw SQL into fluent, object-oriented syntax.
Key Features of Eloquent Models
- Active Record Pattern: Each model instance represents a table row, with attributes matching column values.
- Relationships: Define one-to-one, one-to-many, or many-to-many relationships.
- Mass Assignment: Safely insert or update multiple fields.
- Timestamps: Automatically manage
created_atandupdated_at. - Soft Deletes: Support soft deletion with a
deleted_atcolumn. - Query Builder: Chain methods to build complex queries.
Creating a Model
Laravel’s Artisan CLI simplifies model creation. Models are typically stored in the app/Models/ directory (since Laravel 8).
Basic Model Creation
To create a model, use the make:model command:
php artisan make:model Post
This generates app/Models/Post.php:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
//
}
Model with Migration, Factory, or Seeder
You can generate related resources with flags:
php artisan make:model Post -mfs
-m: Creates a migration file (database/migrations/2025_05_19_123456_create_posts_table.php).-f: Creates a factory (database/factories/PostFactory.php).-s: Creates a seeder (database/seeders/PostSeeder.php).
Example Migration (from previous discussions):
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title', 255)->index();
$table->string('slug')->unique();
$table->text('content');
$table->boolean('is_published')->default(false);
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->timestamps();
$table->softDeletes();
});
}
Configuring a Model
Eloquent models are highly customizable. Below are key properties and methods to configure a model.
1. Table Name
By default, Laravel assumes the table name is the plural, snake_case form of the model name (e.g., Post → posts). Override this if needed:
class Post extends Model
{
protected $table = 'blog_posts';
}
2. Primary Key
The default primary key is id (auto-incrementing). Customize it:
protected $primaryKey = 'post_id';
3. Disabling Timestamps
If the table doesn’t have created_at or updated_at:
public $timestamps = false;
4. Mass Assignment
To prevent mass-assignment vulnerabilities, specify which columns are fillable:
protected $fillable = [
'title',
'slug',
'content',
'is_published',
'user_id',
];
Alternatively, use $guarded to specify non-fillable columns:
protected $guarded = ['id'];
5. Soft Deletes
Enable soft deletes for the model if the table has a deleted_at column:
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
use SoftDeletes;
protected $fillable = ['title', 'slug', 'content', 'is_published', 'user_id'];
}
- Records are marked as deleted (with
deleted_at) instead of being removed. - Use
withTrashed()oronlyTrashed()to query soft-deleted records.
6. Date Fields
Specify columns that should be treated as dates (automatically cast to Carbon instances):
protected $dates = ['published_at', 'deleted_at'];
7. Attribute Casting
Cast column values to specific types:
protected $casts = [
'is_published' => 'boolean',
'published_at' => 'datetime',
'metadata' => 'array', // For JSON columns
];
Example Model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
use SoftDeletes;
protected $fillable = [
'title',
'slug',
'content',
'is_published',
'user_id',
];
protected $casts = [
'is_published' => 'boolean',
'published_at' => 'datetime',
];
protected $dates = ['published_at', 'deleted_at'];
}
Basic CRUD Operations
Eloquent models make CRUD operations straightforward.
1. Creating Records
Use create() for mass assignment or save() for individual attributes:
// Mass assignment
$post = Post::create([
'title' => 'My First Post',
'slug' => 'my-first-post',
'content' => 'This is the content.',
'is_published' => true,
'user_id' => 1,
]);
// Individual attributes
$post = new Post;
$post->title = 'Another Post';
$post->slug = 'another-post';
$post->content = 'More content.';
$post->is_published = false;
$post->user_id = 1;
$post->save();
2. Reading Records
Retrieve records using query methods:
// Get all posts
$posts = Post::all();
// Find by ID
$post = Post::find(1);
// Find or fail (throws ModelNotFoundException if not found)
$post = Post::findOrFail(1);
// Query with conditions
$publishedPosts = Post::where('is_published', true)->get();
// First matching record
$firstPost = Post::where('user_id', 1)->first();
3. Updating Records
Update existing records:
// Update by ID
$post = Post::find(1);
$post->title = 'Updated Title';
$post->save();
// Mass update
Post::where('user_id', 1)->update(['is_published' => false]);
4. Deleting Records
Delete records (or soft delete if enabled):
// Delete a single record
$post = Post::find(1);
$post->delete();
// Delete by query
Post::where('is_published', false)->delete();
// Restore soft-deleted record
Post::withTrashed()->find(1)->restore();
// Permanently delete
Post::withTrashed()->find(1)->forceDelete();
Defining Relationships
Eloquent supports relationships to connect models (e.g., a Post belongs to a User). Define relationships as methods in the model.
1. One-to-One
A Post has one Meta (e.g., metadata stored in a separate table).
class Post extends Model
{
public function meta()
{
return $this->hasOne(Meta::class);
}
}
Usage:
$meta = Post::find(1)->meta;
2. One-to-Many
A User has many Posts.
class User extends Model
{
public function posts()
{
return $this->hasMany(Post::class);
}
}
Inverse (a Post belongs to a User):
class Post extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
Usage:
$posts = User::find(1)->posts;
$user = Post::find(1)->user;
3. Many-to-Many
A Post can have many Tags, and a Tag can belong to many Posts (via a pivot table, e.g., post_tag).
class Post extends Model
{
public function tags()
{
return $this->belongsToMany(Tag::class);
}
}
Usage:
$tags = Post::find(1)->tags;
Post::find(1)->tags()->attach([1, 2]); // Attach tags
Post::find(1)->tags()->detach(1); // Detach a tag
Querying with Eloquent
Eloquent provides a fluent query builder for advanced queries.
Examples
// Published posts
$posts = Post::where('is_published', true)->orderBy('title')->get();
// Posts by user
$posts = Post::where('user_id', 1)->paginate(10);
// Eager load relationships
$posts = Post::with('user')->get();
// Count published posts
$count = Post::where('is_published', true)->count();
// Soft-deleted posts
$trashed = Post::onlyTrashed()->get();
Best Practices for Models
- Use Fillable or Guarded:
- Protect against mass-assignment vulnerabilities with
$fillableor$guarded.
- Define Relationships:
- Use methods like
hasManyorbelongsTofor clear relationships.
- Leverage Soft Deletes:
- Enable soft deletes for recoverable data.
- Cast Attributes:
- Use
$castsfor proper type handling (e.g., booleans, dates).
- Keep Models Focused:
- Avoid complex logic in models; use repositories or services for business logic.
- Eager Load Relationships:
- Use
with()to prevent N+1 query issues:php $posts = Post::with('user')->get();
- Use Factories for Testing:
- Generate test data with factories (e.g.,
Post::factory()->create()).
- Follow Naming Conventions:
- Model: Singular, PascalCase (e.g.,
Post). - Table: Plural, snake_case (e.g.,
posts).
- Document Relationships:
- Add PHPDoc comments for clarity: “`php /**
- Get the user who created the post.
*/
public function user()
{
return $this->belongsTo(User::class);
}
“`
- Get the user who created the post.
Debugging Models
- Check Table Name:
- Verify
$tableor default naming (pluralized model name). - Mass Assignment Errors:
- Ensure columns are in
$fillableor$guardedis configured correctly. - Relationship Issues:
- Confirm foreign key columns exist (e.g.,
user_idforbelongsTo). - Use
with()to debug eager loading. - Query Debugging:
- Use
toSql()to inspect queries:php echo Post::where('is_published', true)->toSql(); - Enable query logging:
php DB::enableQueryLog(); Post::all(); dd(DB::getQueryLog()); - Logs:
- Check
storage/logs/laravel.logfor errors.
Example: Complete Model Workflow
Step 1: Create Model and Migration
php artisan make:model Post -m
Migration (database/migrations/2025_05_19_123456_create_posts_table.php):
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title', 255)->index();
$table->string('slug')->unique();
$table->text('content');
$table->boolean('is_published')->default(false);
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->timestamps();
$table->softDeletes();
});
}
Model (app/Models/Post.php):
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
use SoftDeletes;
protected $fillable = [
'title',
'slug',
'content',
'is_published',
'user_id',
];
protected $casts = [
'is_published' => 'boolean',
];
/**
* Get the user who created the post.
*/
public function user()
{
return $this->belongsTo(User::class);
}
}
Step 2: Run Migration
php artisan migrate
Step 3: CRUD Operations
Controller (app/Http/Controllers/PostController.php):
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
class PostController extends Controller
{
public function index()
{
$posts = Post::with('user')->where('is_published', true)->get();
return view('posts.index', compact('posts'));
}
public function store(Request $request)
{
$post = Post::create($request->validate([
'title' => 'required|string|max:255',
'slug' => 'required|string|unique:posts',
'content' => 'required|string',
'is_published' => 'boolean',
'user_id' => 'required|exists:users,id',
]));
return redirect()->route('posts.index');
}
public function show(Post $post)
{
return view('posts.show', compact('post'));
}
public function update(Request $request, Post $post)
{
$post->update($request->validate([
'title' => 'required|string|max:255',
'slug' => 'required|string|unique:posts,slug,' . $post->id,
'content' => 'required|string',
'is_published' => 'boolean',
]));
return redirect()->route('posts.index');
}
public function destroy(Post $post)
{
$post->delete();
return redirect()->route('posts.index');
}
}
Step 4: Blade View
View (resources/views/posts/index.blade.php):
@extends('layouts.app')
@section('content')
<h1>Posts</h1>
@forelse ($posts as $post)
<article>
<h2>{{ $post->title }}</h2>
<p>By {{ $post->user->name }}</p>
<p>{{ $post->content }}</p>
</article>
@empty
<p>No posts found.</p>
@endforelse
@endsection
Conclusion
Laravel’s Eloquent models provide a powerful and elegant way to interact with your database, abstracting complex SQL into simple PHP methods. By configuring models with $fillable, relationships, and casts, and using them for CRUD operations, you can build robust applications efficiently. Combining models with migrations, controllers, and Blade views creates a seamless workflow for data management.
Next Steps:
- Create a model with a migration:
php artisan make:model ModelName -m. - Define relationships and perform CRUD operations.
- Experiment with factories and seeders for test data.
For deeper insights, explore Laravel’s official documentation or connect with the Laravel community on platforms like X. Start building with Eloquent models today!
Laravel Model Example
This artifact provides a practical example of an Eloquent model for a posts table, including migration, model, controller, and Blade view.
Migration (database/migrations/2025_05_19_123456_create_posts_table.php)
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title', 255)->index();
$table->string('slug')->unique();
$table->text('content');
$table->boolean('is_published')->default(false);
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->timestamps();
$table->softDeletes();
});
}
public function down(): void
{
Schema::dropIfExists('posts');
}
};
Model (app/Models/Post.php)
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
use SoftDeletes;
protected $fillable = [
'title',
'slug',
'content',
'is_published',
'user_id',
];
protected $casts = [
'is_published' => 'boolean',
];
/**
* Get the user who created the post.
*/
public function user()
{
return $this->belongsTo(User::class);
}
}
Controller (app/Http/Controllers/PostController.php)
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
class PostController extends Controller
{
public function index()
{
$posts = Post::with('user')->where('is_published', true)->get();
return view('posts.index', compact('posts'));
}
public function store(Request $request)
{
$post = Post::create($request->validate([
'title' => 'required|string|max:255',
'slug' => 'required|string|unique:posts',
'content' => 'required|string',
'is_published' => 'boolean',
'user_id' => 'required|exists:users,id',
]));
return redirect()->route('posts.index');
}
}
Blade View (resources/views/posts/index.blade.php)
@extends('layouts.app')
@section('content')
<h1>Posts</h1>
@forelse ($posts as $post)
<article>
<h2>{{ $post->title }}</h2>
<p>By {{ $post->user->name }}</p>
<p>{{ $post->content }}</p>
</article>
@empty
<p>No posts found.</p>
@endforelse
@endsection
Usage
- Ensure your
.envfile is configured and theuserstable exists. - Save the migration, model, controller, and Blade view.
- Run the migration:
php artisan migrate
- Define routes in
routes/web.php:
use App\Http\Controllers\PostController;
Route::get('/posts', [PostController::class, 'index'])->name('posts.index');
Route::post('/posts', [PostController::class, 'store'])->name('posts.store');
- Test by accessing
/postsor creating a post via a form.
This example demonstrates an Eloquent model with mass assignment, soft deletes, a relationship, and basic CRUD operations, integrated with a controller and Blade view.