Laravel

Laravel is a powerful and popular PHP framework that simplifies and accelerates web application development. Its elegant syntax, robust features like Eloquent ORM, Blade templating, and built-in security tools help developers create efficient and scalable apps. With strong community support and extensive documentation, Laravel is an ideal choice for both novice and experienced developers

Laravel Models: A Comprehensive Guide

19 May 2025 | Category:

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_at and updated_at.
  • Soft Deletes: Support soft deletion with a deleted_at column.
  • 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., Postposts). 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() or onlyTrashed() 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

  1. Use Fillable or Guarded:
  • Protect against mass-assignment vulnerabilities with $fillable or $guarded.
  1. Define Relationships:
  • Use methods like hasMany or belongsTo for clear relationships.
  1. Leverage Soft Deletes:
  • Enable soft deletes for recoverable data.
  1. Cast Attributes:
  • Use $casts for proper type handling (e.g., booleans, dates).
  1. Keep Models Focused:
  • Avoid complex logic in models; use repositories or services for business logic.
  1. Eager Load Relationships:
  • Use with() to prevent N+1 query issues:
    php $posts = Post::with('user')->get();
  1. Use Factories for Testing:
  • Generate test data with factories (e.g., Post::factory()->create()).
  1. Follow Naming Conventions:
  • Model: Singular, PascalCase (e.g., Post).
  • Table: Plural, snake_case (e.g., posts).
  1. Document Relationships:
  • Add PHPDoc comments for clarity: “`php /**
    • Get the user who created the post.
      */
      public function user()
      {
      return $this->belongsTo(User::class);
      }
      “`

Debugging Models

  • Check Table Name:
  • Verify $table or default naming (pluralized model name).
  • Mass Assignment Errors:
  • Ensure columns are in $fillable or $guarded is configured correctly.
  • Relationship Issues:
  • Confirm foreign key columns exist (e.g., user_id for belongsTo).
  • 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.log for 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

  1. Ensure your .env file is configured and the users table exists.
  2. Save the migration, model, controller, and Blade view.
  3. Run the migration:
   php artisan migrate
  1. 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');
  1. Test by accessing /posts or 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.