Build Your First CRUD App in Laravel

Summary: Step-by-step CRUD application in Laravel using controllers and Blade.


Laravel, a powerful PHP framework, offers an elegant syntax and robust features to kickstart web applications. One of the first steps for any developer is to master CRUD: Create, Read, Update, Delete. In this tutorial, we'll build a simple CRUD app in Laravel using controllers and Blade views from scratch. Let’s get started!


Prerequisites

Before you begin, ensure you have:

  • PHP (8.x recommended)
  • Composer
  • Laravel CLI
  • A database system (MySQL, Postgres, etc.)

1. Setting Up a New Laravel Project

Open your terminal and run:

composer create-project laravel/laravel laravel-crud-app
cd laravel-crud-app

2. Set Up the Database

Edit your .env file with your database configuration:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=crud_app
DB_USERNAME=root
DB_PASSWORD=your-password

Create the database in your DBMS, for example:

CREATE DATABASE crud_app;

3. Create a Model, Migration, and Controller

Suppose we want to manage Posts. Generate the required files:

php artisan make:model Post -mcr

This does three things:

  • -m creates a migration file.
  • -c creates a controller (app/Http/Controllers/PostController.php).
  • -r makes the controller resourceful.

4. Update the Migration

Open database/migrations/xxxx_xx_xx_create_posts_table.php and update:

public function up()
{
    Schema::create('posts', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->text('content');
        $table->timestamps();
    });
}

Run the migration:

php artisan migrate

5. Define Resource Routes

Open routes/web.php and add:

use App\Http\Controllers\PostController;

Route::resource('posts', PostController::class);

This generates all CRUD routes for posts.


6. Building the Controller Logic

Open app/Http/Controllers/PostController.php and add CRUD methods:

use App\Models\Post;

public function index()
{
    $posts = Post::latest()->get();
    return view('posts.index', compact('posts'));
}

public function create()
{
    return view('posts.create');
}

public function store(Request $request)
{
    $validated = $request->validate([
        'title' => 'required|max:255',
        'content' => 'required',
    ]);

    Post::create($validated);
    return redirect()->route('posts.index')->with('success', 'Post created successfully!');
}

public function show(Post $post)
{
    return view('posts.show', compact('post'));
}

public function edit(Post $post)
{
    return view('posts.edit', compact('post'));
}

public function update(Request $request, Post $post)
{
    $validated = $request->validate([
        'title' => 'required|max:255',
        'content' => 'required',
    ]);

    $post->update($validated);
    return redirect()->route('posts.index')->with('success', 'Post updated successfully!');
}

public function destroy(Post $post)
{
    $post->delete();
    return redirect()->route('posts.index')->with('success', 'Post deleted successfully!');
}

7. Enable Mass Assignment

Open app/Models/Post.php and add:

protected $fillable = ['title', 'content'];

8. Creating Blade Views

Create a new folder: resources/views/posts.

index.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <h1>Posts</h1>
    <a href="{{ route('posts.create') }}" class="btn btn-primary mb-2">New Post</a>
    @if(session('success'))
        <div class="alert alert-success">{{ session('success') }}</div>
    @endif
    <table class="table">
        <thead>
            <tr>
                <th>Title</th>
                <th>Content</th>
                <th colspan="2"></th>
            </tr>
        </thead>
        <tbody>
            @forelse($posts as $post)
                <tr>
                    <td><a href="{{ route('posts.show', $post) }}">{{ $post->title }}</a></td>
                    <td>{{ Str::limit($post->content, 50) }}</td>
                    <td><a href="{{ route('posts.edit', $post) }}" class="btn btn-sm btn-secondary">Edit</a></td>
                    <td>
                        <form action="{{ route('posts.destroy', $post) }}" method="POST">
                            @csrf
                            @method('DELETE')
                            <button class="btn btn-sm btn-danger" onclick="return confirm('Delete this post?')">Delete</button>
                        </form>
                    </td>
                </tr>
            @empty
                <tr><td colspan="4">No posts found.</td></tr>
            @endforelse
        </tbody>
    </table>
</div>
@endsection

create.blade.php and edit.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <h1>{{ isset($post) ? 'Edit Post' : 'Create Post' }}</h1>
    <form method="POST" action="{{ isset($post) ? route('posts.update', $post) : route('posts.store') }}">
        @csrf
        @if(isset($post))
            @method('PUT')
        @endif

        <div class="mb-3">
            <label class="form-label">Title</label>
            <input type="text" name="title" class="form-control" value="{{ old('title', $post->title ?? '') }}" required>
        </div>
        <div class="mb-3">
            <label class="form-label">Content</label>
            <textarea name="content" class="form-control" rows="5" required>{{ old('content', $post->content ?? '') }}</textarea>
        </div>
        <button class="btn btn-success">{{ isset($post) ? 'Update' : 'Create' }}</button>
        <a href="{{ route('posts.index') }}" class="btn btn-secondary">Back</a>
    </form>
</div>
@endsection

show.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <h1>{{ $post->title }}</h1>
    <p>{{ $post->content }}</p>
    <a class="btn btn-secondary" href="{{ route('posts.index') }}">Back</a>
</div>
@endsection

9. Layout (Optional Bootstrap Setup)

For quick styling, generate a base layout: resources/views/layouts/app.blade.php:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Laravel CRUD App</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    @yield('content')
</body>
</html>

10. Test Your Application

Run your app locally:

php artisan serve

Visit http://127.0.0.1:8000/posts in your browser.


Conclusion

Congratulations! You’ve built a full-featured CRUD application in Laravel using resource controllers and Blade views. From setting up the database to creating, updating, and deleting posts, you have touched on essential Laravel fundamentals.

Happy coding! 🚀


Further Reading:


If you enjoyed this guide or have questions, leave a comment below!