Laravel 11: Construindo uma Aplicacao Completa do Zero
Guia completo para construir uma aplicacao com Laravel 11: autenticacao, API REST, Eloquent ORM e deploy em producao. Tutorial pratico para desenvolvedores iniciantes e intermediarios.

Laravel 11 redefine o desenvolvimento moderno em PHP com uma arquitetura simplificada e desempenho otimizado. Esta versao principal remove arquivos de configuracao desnecessarios, apresenta uma estrutura mais enxuta e melhora significativamente a experiencia do desenvolvedor. Este guia percorre a construcao de uma aplicacao completa de gerenciamento de tarefas, da instalacao ao deploy.
Laravel 11 adota uma estrutura minimalista: sem arquivos Kernel, configuracao consolidada em bootstrap/app.php e um diretorio app/ mais limpo. O framework exige PHP 8.2 como minimo e integra SQLite nativamente para um inicio rapido.
Instalacao e Configuracao do Projeto
Laravel 11 pode ser instalado via Composer ou pelo instalador do Laravel. A nova estrutura do projeto e mais enxuta, com menos arquivos de configuracao para gerenciar desde o inicio.
# terminal
# Install the Laravel installer (recommended)
composer global require laravel/installer
# Create a new Laravel 11 project
laravel new task-manager
# Or directly with Composer
composer create-project laravel/laravel task-manager
# Navigate to the project
cd task-managerO instalador oferece diversas opcoes interativas: escolha de starter kit (Breeze, Jetstream), framework de testes (Pest, PHPUnit) e banco de dados.
# terminal
# Start the development server
php artisan serve
# Server starts at http://localhost:8000Configuracao do Banco de Dados
Laravel 11 utiliza SQLite por padrao, ideal para desenvolvimento. Para aplicacoes em producao, a configuracao do MySQL ou PostgreSQL e feita no arquivo .env.
# .env
# MySQL configuration
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=task_manager
DB_USERNAME=root
DB_PASSWORD=secret
# PostgreSQL configuration (alternative)
# DB_CONNECTION=pgsql
# DB_HOST=127.0.0.1
# DB_PORT=5432
# DB_DATABASE=task_manager
# DB_USERNAME=postgres
# DB_PASSWORD=secretA verificacao da conexao com o banco de dados e feita por meio de um comando Artisan.
# terminal
# Verify database connection
php artisan db:show
# Run default migrations
php artisan migrateCriacao de Models e Migrations
O Eloquent ORM simplifica a manipulacao de dados com models expressivos. O comando make:model gera o model, a migration, o controller e o factory de uma so vez.
# terminal
# Generate Task model with migration, factory, seeder, and controller
php artisan make:model Task -mfsc
# -m : migration
# -f : factory
# -s : seeder
# -c : controllerEsse comando cria quatro arquivos essenciais para o gerenciamento completo de tarefas.
<?php
// database/migrations/2026_01_14_000000_create_tasks_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
// Migration to create the tasks table
return new class extends Migration
{
public function up(): void
{
Schema::create('tasks', function (Blueprint $table) {
// Auto-incrementing primary key
$table->id();
// Relationship with the owner user
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
// Task title (required)
$table->string('title');
// Detailed description (optional)
$table->text('description')->nullable();
// Status: pending, in_progress, completed
$table->string('status')->default('pending');
// Priority: low, medium, high
$table->string('priority')->default('medium');
// Due date (optional)
$table->date('due_date')->nullable();
// Completion marker
$table->timestamp('completed_at')->nullable();
// Automatic timestamps (created_at, updated_at)
$table->timestamps();
// Soft deletes for trash functionality
$table->softDeletes();
// Indexes to optimize frequent queries
$table->index(['user_id', 'status']);
$table->index('due_date');
});
}
public function down(): void
{
Schema::dropIfExists('tasks');
}
};A migration define a estrutura da tabela com relacionamentos, indices e soft delete para prevenir a perda acidental de dados.
<?php
// app/Models/Task.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Builder;
class Task extends Model
{
// Traits for advanced functionality
use HasFactory, SoftDeletes;
// Mass-assignable attributes
protected $fillable = [
'user_id',
'title',
'description',
'status',
'priority',
'due_date',
'completed_at',
];
// Automatic attribute casting
protected function casts(): array
{
return [
'due_date' => 'date',
'completed_at' => 'datetime',
];
}
// Constants for valid statuses
public const STATUS_PENDING = 'pending';
public const STATUS_IN_PROGRESS = 'in_progress';
public const STATUS_COMPLETED = 'completed';
// Constants for priorities
public const PRIORITY_LOW = 'low';
public const PRIORITY_MEDIUM = 'medium';
public const PRIORITY_HIGH = 'high';
// Relationship: a task belongs to a user
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
// Scope: pending tasks
public function scopePending(Builder $query): Builder
{
return $query->where('status', self::STATUS_PENDING);
}
// Scope: in-progress tasks
public function scopeInProgress(Builder $query): Builder
{
return $query->where('status', self::STATUS_IN_PROGRESS);
}
// Scope: completed tasks
public function scopeCompleted(Builder $query): Builder
{
return $query->where('status', self::STATUS_COMPLETED);
}
// Scope: overdue tasks
public function scopeOverdue(Builder $query): Builder
{
return $query->where('due_date', '<', now())
->whereNull('completed_at');
}
// Scope: tasks for a specific user
public function scopeForUser(Builder $query, int $userId): Builder
{
return $query->where('user_id', $userId);
}
// Accessor: check if task is overdue
public function getIsOverdueAttribute(): bool
{
return $this->due_date
&& $this->due_date->isPast()
&& !$this->completed_at;
}
// Method: mark as completed
public function markAsCompleted(): void
{
$this->update([
'status' => self::STATUS_COMPLETED,
'completed_at' => now(),
]);
}
}O model Task utiliza Eloquent scopes para consultas legíveis e reutilizaveis. As constantes centralizam os valores validos para status e prioridades.
O trait SoftDeletes adiciona uma coluna deleted_at. As tarefas excluidas nao desaparecem do banco de dados, apenas ficam marcadas como excluidas. Utilize withTrashed() para inclui-las nas consultas.
Configuracao de Autenticacao com Breeze
Laravel Breeze oferece autenticacao completa com views Blade ou uma API. A instalacao leva poucos minutos e gera tudo o necessario: rotas, controllers, views e testes.
# terminal
# Install Laravel Breeze
composer require laravel/breeze --dev
# Install the Blade stack (traditional)
php artisan breeze:install blade
# Or for API only (SPA/mobile)
php artisan breeze:install api
# Compile assets
npm install && npm run build
# Run migrations (users, sessions tables, etc.)
php artisan migrateO Breeze gera rotas de autenticacao, controllers e views para registro, login, redefinicao de senha e verificacao de e-mail.
<?php
// app/Models/User.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use HasFactory, Notifiable;
protected $fillable = [
'name',
'email',
'password',
];
protected $hidden = [
'password',
'remember_token',
];
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
// Relationship: a user owns multiple tasks
public function tasks(): HasMany
{
return $this->hasMany(Task::class);
}
// Shortcut: user's pending tasks
public function pendingTasks(): HasMany
{
return $this->tasks()->pending();
}
// Shortcut: user's overdue tasks
public function overdueTasks(): HasMany
{
return $this->tasks()->overdue();
}
}Pronto para mandar bem nas entrevistas de Laravel?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
Criacao de Controllers e Rotas
O controller orquestra a logica de negocio entre as requisicoes HTTP e o model. Laravel 11 incentiva controllers de acao unica ou recursos RESTful.
<?php
// app/Http/Controllers/TaskController.php
namespace App\Http\Controllers;
use App\Models\Task;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
use Illuminate\Support\Facades\Gate;
class TaskController extends Controller
{
// List tasks for the authenticated user
public function index(Request $request): View
{
// Retrieve tasks with optional filtering
$tasks = $request->user()
->tasks()
->when($request->status, fn($q, $status) => $q->where('status', $status))
->when($request->priority, fn($q, $priority) => $q->where('priority', $priority))
->orderBy('due_date')
->orderBy('priority', 'desc')
->paginate(15);
return view('tasks.index', compact('tasks'));
}
// Creation form
public function create(): View
{
return view('tasks.create');
}
// Store a new task
public function store(Request $request): RedirectResponse
{
// Validate incoming data
$validated = $request->validate([
'title' => 'required|string|max:255',
'description' => 'nullable|string|max:5000',
'priority' => 'required|in:low,medium,high',
'due_date' => 'nullable|date|after_or_equal:today',
]);
// Create the task linked to the user
$request->user()->tasks()->create($validated);
return redirect()
->route('tasks.index')
->with('success', 'Task created successfully.');
}
// Display a task
public function show(Task $task): View
{
// Verify user ownership
Gate::authorize('view', $task);
return view('tasks.show', compact('task'));
}
// Edit form
public function edit(Task $task): View
{
Gate::authorize('update', $task);
return view('tasks.edit', compact('task'));
}
// Update a task
public function update(Request $request, Task $task): RedirectResponse
{
Gate::authorize('update', $task);
$validated = $request->validate([
'title' => 'required|string|max:255',
'description' => 'nullable|string|max:5000',
'status' => 'required|in:pending,in_progress,completed',
'priority' => 'required|in:low,medium,high',
'due_date' => 'nullable|date',
]);
// Update completed_at if status = completed
if ($validated['status'] === Task::STATUS_COMPLETED && !$task->completed_at) {
$validated['completed_at'] = now();
}
$task->update($validated);
return redirect()
->route('tasks.index')
->with('success', 'Task updated.');
}
// Delete a task
public function destroy(Task $task): RedirectResponse
{
Gate::authorize('delete', $task);
$task->delete();
return redirect()
->route('tasks.index')
->with('success', 'Task deleted.');
}
// Mark as completed (quick action)
public function complete(Task $task): RedirectResponse
{
Gate::authorize('update', $task);
$task->markAsCompleted();
return back()->with('success', 'Task completed!');
}
}O controller utiliza Gates para autorizacao, validacao integrada e route model binding para um codigo conciso e seguro.
Definicao de Rotas
As rotas definem URLs e as associam a acoes do controller. Laravel 11 centraliza as rotas em routes/web.php.
<?php
// routes/web.php
use App\Http\Controllers\TaskController;
use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;
// Public homepage
Route::get('/', function () {
return view('welcome');
});
// Routes protected by authentication
Route::middleware(['auth', 'verified'])->group(function () {
// Dashboard with statistics
Route::get('/dashboard', function () {
$user = auth()->user();
return view('dashboard', [
'totalTasks' => $user->tasks()->count(),
'pendingTasks' => $user->tasks()->pending()->count(),
'completedTasks' => $user->tasks()->completed()->count(),
'overdueTasks' => $user->tasks()->overdue()->count(),
]);
})->name('dashboard');
// RESTful routes for tasks
Route::resource('tasks', TaskController::class);
// Quick action to complete a task
Route::patch('/tasks/{task}/complete', [TaskController::class, 'complete'])
->name('tasks.complete');
// Profile routes (generated by Breeze)
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});
// Authentication routes (generated by Breeze)
require __DIR__.'/auth.php';O metodo Route::resource() gera automaticamente as sete rotas RESTful padrao: index, create, store, show, edit, update e destroy.
Configuracao de Policies de Autorizacao
As policies centralizam a logica de autorizacao para cada model. Elas determinam quem pode executar quais acoes sobre os recursos.
# terminal
# Generate the policy for Task
php artisan make:policy TaskPolicy --model=Task<?php
// app/Policies/TaskPolicy.php
namespace App\Policies;
use App\Models\Task;
use App\Models\User;
class TaskPolicy
{
// Check before all methods
// Returning true bypasses all checks (admin)
public function before(User $user, string $ability): ?bool
{
if ($user->is_admin) {
return true;
}
return null; // Continue to specific methods
}
// Can view the list of tasks
public function viewAny(User $user): bool
{
return true; // Any authenticated user
}
// Can view a specific task
public function view(User $user, Task $task): bool
{
return $user->id === $task->user_id;
}
// Can create a task
public function create(User $user): bool
{
return true;
}
// Can update a task
public function update(User $user, Task $task): bool
{
return $user->id === $task->user_id;
}
// Can delete a task
public function delete(User $user, Task $task): bool
{
return $user->id === $task->user_id;
}
// Can restore a soft-deleted task
public function restore(User $user, Task $task): bool
{
return $user->id === $task->user_id;
}
// Can permanently delete
public function forceDelete(User $user, Task $task): bool
{
return $user->id === $task->user_id;
}
}O Laravel descobre automaticamente as policies por convencao de nomes. A TaskPolicy se aplica ao model Task.
As policies sao essenciais para a seguranca. Sem elas, um usuario poderia manipular URLs para acessar tarefas de outros usuarios. A verificacao de propriedade dos recursos deve ser feita sempre.
Construcao de uma API REST
Laravel simplifica a criacao de APIs RESTful com rotas dedicadas, recursos JSON e Sanctum para autenticacao.
# terminal
# Install Sanctum (included by default since Laravel 11)
php artisan install:api
# This command:
# - Publishes Sanctum migrations
# - Adds the HasApiTokens trait to the User model
# - Configures API routes<?php
// app/Http/Controllers/Api/TaskController.php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Resources\TaskResource;
use App\Http\Resources\TaskCollection;
use App\Models\Task;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
class TaskController extends Controller
{
// Paginated list of tasks
public function index(Request $request): AnonymousResourceCollection
{
$tasks = $request->user()
->tasks()
->when($request->status, fn($q, $s) => $q->where('status', $s))
->when($request->priority, fn($q, $p) => $q->where('priority', $p))
->when($request->boolean('overdue'), fn($q) => $q->overdue())
->latest()
->paginate($request->input('per_page', 15));
return TaskResource::collection($tasks);
}
// Create a task
public function store(Request $request): JsonResponse
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'description' => 'nullable|string|max:5000',
'priority' => 'required|in:low,medium,high',
'due_date' => 'nullable|date|after_or_equal:today',
]);
$task = $request->user()->tasks()->create($validated);
return response()->json([
'message' => 'Task created successfully.',
'data' => new TaskResource($task),
], 201);
}
// Display a task
public function show(Task $task): TaskResource
{
$this->authorize('view', $task);
return new TaskResource($task);
}
// Update a task
public function update(Request $request, Task $task): TaskResource
{
$this->authorize('update', $task);
$validated = $request->validate([
'title' => 'sometimes|required|string|max:255',
'description' => 'nullable|string|max:5000',
'status' => 'sometimes|required|in:pending,in_progress,completed',
'priority' => 'sometimes|required|in:low,medium,high',
'due_date' => 'nullable|date',
]);
if (isset($validated['status'])
&& $validated['status'] === Task::STATUS_COMPLETED
&& !$task->completed_at) {
$validated['completed_at'] = now();
}
$task->update($validated);
return new TaskResource($task->fresh());
}
// Delete a task
public function destroy(Task $task): JsonResponse
{
$this->authorize('delete', $task);
$task->delete();
return response()->json([
'message' => 'Task deleted successfully.',
]);
}
}API Resources para Transformacao de Dados
Os API Resources transformam models Eloquent em respostas JSON estruturadas, controlando com precisao os dados expostos.
<?php
// app/Http/Resources/TaskResource.php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class TaskResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'description' => $this->description,
'status' => $this->status,
'priority' => $this->priority,
'due_date' => $this->due_date?->format('Y-m-d'),
'is_overdue' => $this->is_overdue,
'completed_at' => $this->completed_at?->toISOString(),
'created_at' => $this->created_at->toISOString(),
'updated_at' => $this->updated_at->toISOString(),
// Conditional inclusion of the user
'user' => $this->whenLoaded('user', fn() => [
'id' => $this->user->id,
'name' => $this->user->name,
]),
];
}
}Rotas da API com Autenticacao Sanctum
<?php
// routes/api.php
use App\Http\Controllers\Api\TaskController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
// Route to retrieve the authenticated user
Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:sanctum');
// API routes protected by Sanctum
Route::middleware('auth:sanctum')->group(function () {
Route::apiResource('tasks', TaskController::class);
// Dashboard statistics
Route::get('/dashboard/stats', function (Request $request) {
$user = $request->user();
return response()->json([
'total' => $user->tasks()->count(),
'pending' => $user->tasks()->pending()->count(),
'in_progress' => $user->tasks()->inProgress()->count(),
'completed' => $user->tasks()->completed()->count(),
'overdue' => $user->tasks()->overdue()->count(),
]);
});
});Testes Automatizados com Pest
Laravel 11 integra o Pest PHP por padrao, um framework de testes elegante e expressivo. Os testes garantem que a aplicacao funcione corretamente apos cada modificacao.
<?php
// tests/Feature/TaskTest.php
use App\Models\Task;
use App\Models\User;
// Test: a user can view their tasks
test('user can view their tasks', function () {
$user = User::factory()->create();
$tasks = Task::factory()->count(5)->for($user)->create();
$response = $this->actingAs($user)->get('/tasks');
$response->assertOk();
$response->assertViewHas('tasks');
});
// Test: a user can create a task
test('user can create a task', function () {
$user = User::factory()->create();
$response = $this->actingAs($user)->post('/tasks', [
'title' => 'New task',
'description' => 'Task description',
'priority' => 'high',
'due_date' => now()->addDays(7)->format('Y-m-d'),
]);
$response->assertRedirect('/tasks');
$this->assertDatabaseHas('tasks', [
'user_id' => $user->id,
'title' => 'New task',
'priority' => 'high',
]);
});
// Test: title validation required
test('task title is required', function () {
$user = User::factory()->create();
$response = $this->actingAs($user)->post('/tasks', [
'title' => '',
'priority' => 'medium',
]);
$response->assertSessionHasErrors('title');
});
// Test: a user cannot view another user's task
test('user cannot view another users task', function () {
$user = User::factory()->create();
$otherUser = User::factory()->create();
$task = Task::factory()->for($otherUser)->create();
$response = $this->actingAs($user)->get("/tasks/{$task->id}");
$response->assertForbidden();
});
// Test: mark a task as completed
test('user can complete a task', function () {
$user = User::factory()->create();
$task = Task::factory()->for($user)->create(['status' => 'pending']);
$response = $this->actingAs($user)->patch("/tasks/{$task->id}/complete");
$response->assertRedirect();
$task->refresh();
expect($task->status)->toBe('completed');
expect($task->completed_at)->not->toBeNull();
});
// API test: retrieve tasks
test('api returns paginated tasks', function () {
$user = User::factory()->create();
Task::factory()->count(20)->for($user)->create();
$response = $this->actingAs($user, 'sanctum')
->getJson('/api/tasks?per_page=10');
$response->assertOk()
->assertJsonCount(10, 'data')
->assertJsonStructure([
'data' => [
'*' => ['id', 'title', 'status', 'priority', 'due_date'],
],
'links',
'meta',
]);
});Execucao dos testes com Pest.
# terminal
# Run all tests
php artisan test
# Tests with code coverage
php artisan test --coverage
# Tests for a specific file
php artisan test tests/Feature/TaskTest.php
# Parallel tests (faster)
php artisan test --parallelOtimizacoes para Producao
Antes do deploy, diversas otimizacoes melhoram o desempenho da aplicacao Laravel.
# terminal
# Cache configuration
php artisan config:cache
# Cache routes
php artisan route:cache
# Cache views
php artisan view:cache
# Optimize Composer autoloader
composer install --optimize-autoloader --no-dev
# Generate asset manifest
npm run buildConfiguracao do Arquivo .env para Producao
# .env.production
APP_NAME="Task Manager"
APP_ENV=production
APP_DEBUG=false
APP_URL=https://taskmanager.example.com
# Cache and session via Redis (recommended)
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
# Database
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_DATABASE=task_manager_prod
DB_USERNAME=app_user
DB_PASSWORD=strong_password_here
# Mail
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailgun.org
MAIL_PORT=587
MAIL_USERNAME=postmaster@taskmanager.example.com
MAIL_PASSWORD=secret
MAIL_ENCRYPTION=tlsO arquivo .env nunca deve ser commitado no repositorio Git. Recomenda-se usar o .env.example como template e configurar as variaveis sensiveis diretamente no servidor de producao ou por meio de gerenciadores de segredos.
Conclusao
Laravel 11 simplifica significativamente o desenvolvimento moderno em PHP com sua estrutura otimizada e convencoes inteligentes. Este guia cobriu os fundamentos para construir uma aplicacao completa: models Eloquent, autenticacao, controllers RESTful, policies de autorizacao, API com Sanctum e testes automatizados.
Checklist para Aplicacoes Laravel de Qualidade
- ✅ Usar migrations para todas as alteracoes de schema
- ✅ Implementar policies para proteger o acesso aos recursos
- ✅ Validar toda entrada do usuario nos controllers
- ✅ Usar API Resources para controlar os dados expostos
- ✅ Escrever testes para as funcionalidades criticas
- ✅ Configurar cache e otimizacoes antes do deploy
- ✅ Proteger as variaveis de ambiente sensiveis
Comece a praticar!
Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.
O ecossistema do Laravel continua evoluindo com pacotes como Livewire para interfaces reativas, Horizon para gerenciamento de filas e Octane para desempenho excepcional. Dominar esses fundamentos abre caminho para aplicacoes PHP robustas e manteniveís.
Tags
Compartilhar
Artigos relacionados

Symfony 7: API Platform e Boas Praticas
Guia completo para criar APIs REST profissionais com Symfony 7 e API Platform 4. State Providers, Processors, validacao e serializacao explicados com exemplos praticos.

NestJS: Construindo uma API REST Completa
Tutorial completo para construir uma API REST profissional com NestJS. Controllers, Services, Modules, validacao com class-validator e tratamento centralizado de erros.