Build a CRUD App with Laravel 11 🚀

A Beginner's Guide to Creating a Simple Task Manager from Start to Finish.

Step 0: Prerequisites & Goal 🎯
Project Goal

To create a web application where a user can view a list of tasks, add new tasks, edit existing ones, and delete them.

What You'll Need
  • PHP (version 8.2 or newer).
  • Composer for managing PHP dependencies.
  • A local development environment (e.g., Laragon, Laravel Herd, XAMPP).
Good News! Laravel 11 is configured to use an SQLite database by default. This means you don't need to install a separate database server like MySQL or PostgreSQL. Laravel will handle creating the database file for you!
Step 1: Setting Up Your Laravel 11 Project 🏗️

First, we'll use Composer to create a brand new Laravel project.

1. Create the Project

Open your terminal and run the following command:

composer create-project laravel/laravel task-manager

This will download Laravel and all its dependencies into a new folder called `task-manager`.

2. Set up the Database

Navigate into your new project's directory:

cd task-manager

Now, run the initial migration command. This will automatically create the `database/database.sqlite` file and set up the default tables (like for users).

php artisan migrate
Step 2: The Model and Migration (The Data Structure) 🏛️

We need a `Task` model to represent a task in our application and a migration to create the `tasks` table in our database.

Laravel's command-line tool, **Artisan**, can create both for us with a single command:

php artisan make:model Task -m

The `-m` flag tells Artisan to also create a database migration file for the `Task` model.

1. Define the Table Structure (Migration)

Open the new migration file located at `database/migrations/...._create_tasks_table.php`. Let's add columns for a `title` and a `completed` status.

// ... inside the up() method
Schema::create('tasks', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->boolean('completed')->default(false);
    $table->timestamps();
});
2. Run the Migration

Now, apply this new migration to create the `tasks` table in your database:

php artisan migrate
3. Configure the Model

Open the model file at `app/Models/Task.php`. We need to specify which fields are "fillable" to prevent mass-assignment vulnerabilities.

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Task extends Model
{
    use HasFactory;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['title', 'completed'];
}
Step 3: The Controller (The Business Logic) ⚙️

A **Controller** handles the application logic. We'll create one to manage our tasks. Using the `--resource` flag will pre-fill the controller with methods for each CRUD action.

php artisan make:controller TaskController --resource

Now, open `app/Http/Controllers/TaskController.php` and fill in the logic.

namespace App\Http\Controllers;

use App\Models\Task;
use Illuminate\Http\Request;

class TaskController extends Controller
{
    // Display a listing of the tasks.
    public function index()
    {
        $tasks = Task::latest()->get();
        return view('tasks.index', compact('tasks'));
    }

    // Show the form for creating a new task.
    public function create()
    {
        return view('tasks.create');
    }

    // Store a newly created task in storage.
    public function store(Request $request)
    {
        $request->validate(['title' => 'required|max:255']);
        Task::create($request->all());
        return redirect()->route('tasks.index')->with('success', 'Task created successfully.');
    }

    // Show the form for editing the specified task.
    public function edit(Task $task)
    {
        return view('tasks.edit', compact('task'));
    }

    // Update the specified task in storage.
    public function update(Request $request, Task $task)
    {
        $request->validate(['title' => 'required|max:255']);
        $task->update($request->all());
        return redirect()->route('tasks.index')->with('success', 'Task updated successfully.');
    }

    // Remove the specified task from storage.
    public function destroy(Task $task)
    {
        $task->delete();
        return redirect()->route('tasks.index')->with('success', 'Task deleted successfully.');
    }
}
Key Concept: Route-Model Binding. Notice how in `edit(Task $task)`, Laravel automatically finds the Task from the database with the ID from the URL. This is a powerful feature called Route-Model Binding that makes your code cleaner.
Step 4: The Routes (The URL Endpoints) 🔌

A **Route** connects a URL to a controller action. Instead of defining each CRUD route manually, Laravel provides a convenient "resource" route that does it all in one line.

Open `routes/web.php` and add the following code:

use App\Http\Controllers\TaskController;
use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return redirect()->route('tasks.index');
});

Route::resource('tasks', TaskController::class);

This single `Route::resource` line creates all the necessary routes for viewing, creating, editing, and deleting tasks. It's incredibly efficient!

Step 5: The Blade Views (The User Interface) 🖥️

**Blade** is Laravel's powerful templating engine. We'll create a few files in the `resources/views/tasks` directory (you'll need to create the `tasks` folder).

1. `layout.blade.php` (The Main Template)

Create a `layout.blade.php` file in `resources/views`. This will be our main site template.

<!DOCTYPE html>
<html>
<head>
    <title>Task Manager</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container mt-5">
        @yield('content')
    </div>
</body>
</html>
2. `tasks/index.blade.php` (The Task List)

This view will display all the tasks.

@extends('layout')

@section('content')
    <h1>Task List</h1>
    <a href="{{ route('tasks.create') }}" class="btn btn-primary mb-3">Add New Task</a>

    @if ($message = Session::get('success'))
        <div class="alert alert-success">{{ $message }}</div>
    @endif

    <table class="table table-bordered">
        <tr>
            <th>ID</th>
            <th>Title</th>
            <th width="280px">Action</th>
        </tr>
        @foreach ($tasks as $task)
        <tr>
            <td>{{ $task->id }}</td>
            <td>{{ $task->title }}</td>
            <td>
                <form action="{{ route('tasks.destroy',$task->id) }}" method="POST">
                    <a class="btn btn-info" href="{{ route('tasks.edit',$task->id) }}">Edit</a>
                    @csrf
                    @method('DELETE')
                    <button type="submit" class="btn btn-danger">Delete</button>
                </form>
            </td>
        </tr>
        @endforeach
    </table>
@endsection
3. `tasks/create.blade.php` (Create Task Form)
@extends('layout')

@section('content')
    <h1>Add New Task</h1>

    @if ($errors->any())
        <div class="alert alert-danger">
            <strong>Whoops!</strong> There were some problems with your input.<br><br>
            <ul>
                @foreach ($errors->all() as $error)
                    <li>{{ $error }}</li>
                @endforeach
            </ul>
        </div>
    @endif

    <form action="{{ route('tasks.store') }}" method="POST">
        @csrf
        <div class="mb-3">
            <label for="title" class="form-label">Title:</label>
            <input type="text" name="title" class="form-control" placeholder="Title">
        </div>
        <button type="submit" class="btn btn-primary">Submit</button>
        <a class="btn btn-secondary" href="{{ route('tasks.index') }}"> Back</a>
    </form>
@endsection
4. `tasks/edit.blade.php` (Edit Task Form)
@extends('layout')

@section('content')
    <h1>Edit Task</h1>

    @if ($errors->any())
        <div class="alert alert-danger">
            <strong>Whoops!</strong> There were some problems with your input.<br><br>
            <ul>
                @foreach ($errors->all() as $error)
                    <li>{{ $error }}</li>
                @endforeach
            </ul>
        </div>
    @endif

    <form action="{{ route('tasks.update',$task->id) }}" method="POST">
        @csrf
        @method('PUT')
        <div class="mb-3">
            <label for="title" class="form-label">Title:</label>
            <input type="text" name="title" value="{{ $task->title }}" class="form-control" placeholder="Title">
        </div>
        <button type="submit" class="btn btn-primary">Submit</button>
        <a class="btn btn-secondary" href="{{ route('tasks.index') }}"> Back</a>
    </form>
@endsection
Step 6: Run The Application! 🏁

Everything is now ready! Go to your terminal (in the `task-manager` directory) and start the Laravel development server:

php artisan serve

Now, open your web browser and navigate to the address provided (usually http://127.0.0.1:8000).

http://127.0.0.1:8000/tasks

You should see your task list application. You can now add, edit, and delete tasks!

Congratulations! You've Built a Laravel 11 CRUD Application! 🎉

You have successfully created a complete web application from scratch using Laravel. This project covers the essential workflow of modern web development.

You learned how to:


This is a solid foundation for building more complex and feature-rich a