Laravel Project: Build a Task Manager

This Laravel project ties the whole tutorial together. You will build a simple task manager where logged-in users create, complete and delete their own tasks. It uses migrations, Eloquent relationships, a resource controller, validation, Blade and authentication.

Step 1: Create the Project

laravel new tasks
cd tasks
composer require laravel/breeze --dev
php artisan breeze:install blade
php artisan make:model Task -mcr
INFO  Model [app/Models/Task.php] created successfully.
INFO  Migration [database/migrations/..._create_tasks_table.php] created successfully.
INFO  Controller [app/Http/Controllers/TaskController.php] created successfully.

Step 2: Migration and Model

<?php
// migration
Schema::create('tasks', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->cascadeOnDelete();
    $table->string('title');
    $table->date('due_on')->nullable();
    $table->boolean('done')->default(false);
    $table->timestamps();
});

// app/Models/Task.php
class Task extends Model
{
    protected $fillable = ['title', 'due_on', 'done'];
    protected function casts(): array { return ['due_on' => 'date', 'done' => 'boolean']; }
    public function user(): BelongsTo { return $this->belongsTo(User::class); }
}

// app/Models/User.php
public function tasks(): HasMany { return $this->hasMany(Task::class); }
$ php artisan migrate
2026_10_31_140000_create_tasks_table ........ 15ms DONE

Step 3: Routes and Controller

<?php
// routes/web.php
Route::middleware('auth')->group(function () {
    Route::resource('tasks', TaskController::class)->only(['index', 'store', 'update', 'destroy']);
});

// app/Http/Controllers/TaskController.php
class TaskController extends Controller
{
    public function index(Request $request)
    {
        $tasks = $request->user()->tasks()->orderBy('done')->orderBy('due_on')->get();
        return view('tasks.index', compact('tasks'));
    }

    public function store(Request $request)
    {
        $data = $request->validate([
            'title' => 'required|string|max:150',
            'due_on' => 'nullable|date|after_or_equal:today',
        ]);
        $request->user()->tasks()->create($data);
        return back()->with('status', 'Task added');
    }

    public function update(Request $request, Task $task)
    {
        abort_unless($task->user_id === $request->user()->id, 403);
        $task->update(['done' => ! $task->done]);
        return back();
    }

    public function destroy(Request $request, Task $task)
    {
        abort_unless($task->user_id === $request->user()->id, 403);
        $task->delete();
        return back()->with('status', 'Task deleted');
    }
}
GET    /tasks         tasks.index
POST   /tasks         tasks.store
PATCH  /tasks/{task}  tasks.update
DELETE /tasks/{task}  tasks.destroy

Step 4: The Blade View

{{-- resources/views/tasks/index.blade.php --}}
<x-app-layout>
    @if (session('status')) <p>{{ session('status') }}</p> @endif

    <form method="POST" action="{{ route('tasks.store') }}">
        @csrf
        <input name="title" value="{{ old('title') }}" placeholder="New task">
        <input type="date" name="due_on">
        <button>Add</button>
        @error('title') <p>{{ $message }}</p> @enderror
    </form>

    <ul>
    @forelse ($tasks as $task)
        <li>
            <form method="POST" action="{{ route('tasks.update', $task) }}" style="display:inline">
                @csrf @method('PATCH')
                <button>{{ $task->done ? 'Undo' : 'Done' }}</button>
            </form>
            <span @class(['line-through' => $task->done])>{{ $task->title }}</span>
            @if ($task->due_on) <small>due {{ $task->due_on->format('M j') }}</small> @endif
            <form method="POST" action="{{ route('tasks.destroy', $task) }}" style="display:inline">
                @csrf @method('DELETE')
                <button>Delete</button>
            </form>
        </li>
    @empty
        <li>No tasks yet. Add your first one above.</li>
    @endforelse
    </ul>
</x-app-layout>
Task added
[Done] Write Laravel tutorial   due Nov 3   [Delete]
[Undo] Install Breeze (crossed out)          [Delete]

Ideas to Extend the Project

  • Move the ownership check into a TaskPolicy and use Gate::authorize
  • Add a Form Request class for validation
  • Add categories with a belongsToMany relationship
  • Send a queued reminder email for tasks due tomorrow using the scheduler
  • Expose the tasks as a JSON API protected by Sanctum
  • Write feature tests with php artisan test

Frequently asked questions

What is a good first Laravel project?

A CRUD app such as a task manager, blog or notes app. It covers routing, controllers, Eloquent, validation and Blade in one small project.

How do I deploy a Laravel project?

Use a server with PHP and a database, point the web root to the public folder, set .env, run composer install --no-dev, php artisan migrate --force and php artisan optimize. Laravel Forge or Laravel Cloud can automate this.

How do I test my Laravel app?

Write feature tests in tests/Feature using Pest or PHPUnit and run php artisan test. Laravel provides helpers like actingAs() and assertRedirect().