Laravel 11: Een complete applicatie bouwen vanaf nul
Uitgebreide handleiding voor Laravel 11: installatie, Eloquent-modellen, authenticatie met Breeze, controllers, routes, autorisatie met policies, REST API met Sanctum, testen met Pest en productie-deployment.

Laravel 11 introduceert een gestroomlijnde projectstructuur, native health checks en een vernieuwde configuratie op basis van bootstrap/app.php. Deze handleiding doorloopt het volledige traject: van installatie tot productie-deployment van een taakbeheer-applicatie.
Laravel 11 verwijdert talloze boilerplate-bestanden (Http Kernel, Console Kernel, Middleware-directory) en introduceert een gecentraliseerde configuratie in bootstrap/app.php. Health-check routes, geleidelijke encryptiesleutel-rotatie en een vernieuwd starter kit-ecosysteem behoren tot de belangrijkste wijzigingen.
Installatie en projectconfiguratie
De Laravel-installer genereert het projectskelet in enkele seconden. Vereisten: PHP 8.2+ en Composer.
# Install Laravel installer globally
composer global require laravel/installer
# Create a new Laravel 11 project
laravel new taskmanager
# Navigate to the project
cd taskmanagerHet .env-bestand configureert de databaseverbinding. Laravel 11 gebruikt standaard SQLite; voor productieomgevingen verdient PostgreSQL of MySQL de voorkeur.
# .env
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=taskmanager
DB_USERNAME=postgres
DB_PASSWORD=secretNa de configuratie worden de initiele migraties uitgevoerd en de ontwikkelserver gestart.
# Run initial migrations
php artisan migrate
# Start the development server
php artisan serveDe applicatie is nu bereikbaar op http://localhost:8000.
Modellen en migraties aanmaken
Eloquent-modellen vormen de kern van elke Laravel-applicatie. Het volgende commando maakt gelijktijdig een model, migratie, factory en seeder aan.
# Create model with migration, factory, and seeder
php artisan make:model Task -mfsDe migratie definieert de tabelstructuur. Belangrijk: softDeletes() maakt logisch verwijderen mogelijk zonder gegevensverlies.
public function up(): void
{
Schema::create('tasks', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('title');
$table->text('description')->nullable();
$table->enum('status', ['pending', 'in_progress', 'completed'])->default('pending');
$table->enum('priority', ['low', 'medium', 'high'])->default('medium');
$table->date('due_date')->nullable();
$table->timestamps();
$table->softDeletes();
});
}Het Eloquent-model declareert de fillable velden, casts en de relatie met User.
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Task extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'title',
'description',
'status',
'priority',
'due_date',
'user_id',
];
protected $casts = [
'due_date' => 'date',
];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}Soft Deletes markeren records als verwijderd zonder ze fysiek uit de database te verwijderen. Dit is bijzonder nuttig voor audit trails en gegevensherstel. Met Task::withTrashed() kunnen ook verwijderde records worden opgevraagd.
Authenticatie instellen met Breeze
Laravel Breeze biedt een lichtgewicht authenticatieoplossing met login, registratie, wachtwoord-reset en e-mailverificatie.
# Install Breeze
composer require laravel/breeze --dev
# Scaffold with Blade stack
php artisan breeze:install blade
# Install frontend dependencies and build
npm install && npm run build
# Run migrations for auth tables
php artisan migrateHet User-model wordt uitgebreid met de hasMany-relatie naar Tasks.
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',
];
}
public function tasks(): HasMany
{
return $this->hasMany(Task::class);
}
}Klaar om je Laravel gesprekken te halen?
Oefen met onze interactieve simulatoren, flashcards en technische tests.
Controllers en routes aanmaken
Een resourceful controller implementeert alle CRUD-operaties. Laravel 11 maakt gebruik van de vereenvoudigde route-declaratie in routes/web.php.
namespace App\Http\Controllers;
use App\Models\Task;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\View\View;
use Illuminate\Http\RedirectResponse;
class TaskController extends Controller
{
public function index(): View
{
$tasks = Auth::user()->tasks()
->orderBy('due_date')
->paginate(10);
return view('tasks.index', compact('tasks'));
}
public function create(): View
{
return view('tasks.create');
}
public function store(Request $request): RedirectResponse
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'description' => 'nullable|string',
'status' => 'in:pending,in_progress,completed',
'priority' => 'in:low,medium,high',
'due_date' => 'nullable|date|after:today',
]);
Auth::user()->tasks()->create($validated);
return redirect()->route('tasks.index')
->with('success', 'Task created successfully.');
}
public function edit(Task $task): View
{
$this->authorize('update', $task);
return view('tasks.edit', compact('task'));
}
public function update(Request $request, Task $task): RedirectResponse
{
$this->authorize('update', $task);
$validated = $request->validate([
'title' => 'required|string|max:255',
'description' => 'nullable|string',
'status' => 'in:pending,in_progress,completed',
'priority' => 'in:low,medium,high',
'due_date' => 'nullable|date',
]);
$task->update($validated);
return redirect()->route('tasks.index')
->with('success', 'Task updated successfully.');
}
public function destroy(Task $task): RedirectResponse
{
$this->authorize('delete', $task);
$task->delete();
return redirect()->route('tasks.index')
->with('success', 'Task deleted successfully.');
}
}De routes worden geregistreerd als resource route, beschermd door de auth-middleware.
use App\Http\Controllers\TaskController;
Route::middleware(['auth', 'verified'])->group(function () {
Route::resource('tasks', TaskController::class)
->except(['show']);
});Autorisatie met policies instellen
Policies bundelen de autorisatielogica en zorgen ervoor dat gebruikers alleen hun eigen taken kunnen bewerken.
# Generate a policy for the Task model
php artisan make:policy TaskPolicy --model=TaskDe policy controleert of de geauthenticeerde gebruiker de eigenaar van de taak is.
namespace App\Policies;
use App\Models\Task;
use App\Models\User;
class TaskPolicy
{
public function update(User $user, Task $task): bool
{
return $user->id === $task->user_id;
}
public function delete(User $user, Task $task): bool
{
return $user->id === $task->user_id;
}
}Policies moeten worden aangeroepen in elke controller-methode die gegevens wijzigt. Zonder autorisatiecontrole kunnen gebruikers via URL-manipulatie records van anderen bewerken of verwijderen. De methode $this->authorize() gooit automatisch een 403-exceptie wanneer de controle faalt.
Een REST API bouwen met Sanctum
Laravel Sanctum biedt lichtgewicht authenticatie voor SPA's en mobiele apps. Vanaf Laravel 11 is de API-configuratie met een enkel commando geregeld.
# Install API support (includes Sanctum)
php artisan install:apiDe API-controller retourneert JSON-responses via API Resources, die de datastructuur bepalen.
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Resources\TaskResource;
use App\Models\Task;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
class TaskController extends Controller
{
public function index(Request $request): AnonymousResourceCollection
{
$tasks = $request->user()->tasks()
->orderBy('created_at', 'desc')
->paginate(15);
return TaskResource::collection($tasks);
}
public function store(Request $request): TaskResource
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'description' => 'nullable|string',
'status' => 'in:pending,in_progress,completed',
'priority' => 'in:low,medium,high',
'due_date' => 'nullable|date|after:today',
]);
$task = $request->user()->tasks()->create($validated);
return new TaskResource($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',
'status' => 'in:pending,in_progress,completed',
'priority' => 'in:low,medium,high',
'due_date' => 'nullable|date',
]);
$task->update($validated);
return new TaskResource($task);
}
public function destroy(Task $task)
{
$this->authorize('delete', $task);
$task->delete();
return response()->json(['message' => 'Task deleted'], 200);
}
}De API Resource bepaalt welke velden in de JSON-response worden opgenomen.
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'),
'created_at' => $this->created_at->toISOString(),
'updated_at' => $this->updated_at->toISOString(),
];
}
}De API-routes worden beschermd door Sanctums auth:sanctum-middleware.
use App\Http\Controllers\Api\TaskController;
Route::middleware('auth:sanctum')->group(function () {
Route::apiResource('tasks', TaskController::class);
});Geautomatiseerd testen met Pest
Pest biedt een elegante syntax voor PHP-tests. De volgende tests dekken de CRUD-operaties en autorisatie.
use App\Models\Task;
use App\Models\User;
beforeEach(function () {
$this->user = User::factory()->create();
});
test('user can create a task', function () {
$response = $this->actingAs($this->user)->post('/tasks', [
'title' => 'Test Task',
'description' => 'Test description',
'priority' => 'high',
]);
$response->assertRedirect('/tasks');
$this->assertDatabaseHas('tasks', [
'title' => 'Test Task',
'user_id' => $this->user->id,
]);
});
test('user can update own task', function () {
$task = Task::factory()->create(['user_id' => $this->user->id]);
$response = $this->actingAs($this->user)->put("/tasks/{$task->id}", [
'title' => 'Updated Task',
'status' => 'completed',
'priority' => 'low',
]);
$response->assertRedirect('/tasks');
expect($task->fresh()->title)->toBe('Updated Task');
});
test('user cannot update another users task', function () {
$otherUser = User::factory()->create();
$task = Task::factory()->create(['user_id' => $otherUser->id]);
$response = $this->actingAs($this->user)->put("/tasks/{$task->id}", [
'title' => 'Hacked Task',
]);
$response->assertForbidden();
});
test('user can delete own task', function () {
$task = Task::factory()->create(['user_id' => $this->user->id]);
$response = $this->actingAs($this->user)->delete("/tasks/{$task->id}");
$response->assertRedirect('/tasks');
$this->assertSoftDeleted('tasks', ['id' => $task->id]);
});
test('api returns paginated tasks', function () {
Task::factory(20)->create(['user_id' => $this->user->id]);
$response = $this->actingAs($this->user)->getJson('/api/tasks');
$response->assertOk()
->assertJsonCount(15, 'data')
->assertJsonStructure([
'data' => [['id', 'title', 'status', 'priority']],
'meta' => ['current_page', 'last_page'],
]);
});Het uitvoeren van de tests gebeurt met een enkel commando.
# Run all tests
php artisan test
# Run with coverage
php artisan test --coverageProductie-optimalisaties
Voor deployment verbeteren de volgende cache-commando's de prestaties aanzienlijk.
# Cache configuration, routes, and views
php artisan config:cache
php artisan route:cache
php artisan view:cache
# Optimize autoloader
composer install --optimize-autoloader --no-devHet .env.production-bestand bevat productiespecifieke instellingen.
# .env.production
APP_ENV=production
APP_DEBUG=false
APP_URL=https://taskmanager.example.com
LOG_CHANNEL=stack
LOG_LEVEL=error
CACHE_STORE=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redisHet .env-bestand mag nooit in versiebeheer worden opgenomen. APP_KEY, databasewachtwoorden en API-sleutels dienen via beveiligde omgevingsvariabelen van de hosting provider te worden geconfigureerd. Het commando php artisan env:encrypt versleutelt het bestand voor veilige deployments.
Conclusie
Deze handleiding heeft de essentiiele bouwstenen van een Laravel 11-applicatie behandeld:
- Projectconfiguratie: Laravel installer, omgevingsconfiguratie en initiele migraties
- Eloquent-modellen: relaties, casts, soft deletes en factories voor gestructureerd gegevensbeheer
- Authenticatie: Breeze levert login, registratie en wachtwoord-reset in enkele minuten
- Controllers en routes: resourceful controllers en middleware-beschermde routegroepen
- Autorisatie: policies garanderen dat gebruikers alleen hun eigen resources beheren
- REST API: Sanctum-tokens en API Resources voor mobiele en SPA-clients
- Tests: Pest-tests dekken CRUD-operaties en autorisatieregels
- Productie: caching, autoloader-optimalisatie en beveiligde omgevingsconfiguratie
Begin met oefenen!
Test je kennis met onze gespreksimulatoren en technische tests.
Zie jij de bug in Laravel?
Een echt codefragment, een verborgen bug, één poging per dag. Zonder account uit te proberen.

Geschreven door
Anthony Fillion-MailletOprichter van SharpSkill
Al meer dan 10 jaar fullstack-ontwikkelaar. Hij leidt SharpSkill en staat in voor alles wat hier verschijnt.
Bijgewerkt op 8 april 2026
Tags
Delen
Gerelateerde artikelen

Eloquent ORM: patronen en optimalisaties voor Laravel
Beheers Eloquent ORM met geavanceerde patronen en optimalisatietechnieken. Eager loading, query scopes, accessors, mutators en performance voor Laravel-applicaties.

Laravel en PHP sollicitatievragen: de Top 25 in 2026
De 25 meest gestelde Laravel- en PHP-sollicitatievragen. Eloquent ORM, middleware, Artisan, queues, tests en architectuur met uitgebreide antwoorden en codevoorbeelden.

Laravel 12 in 2026: Nieuwe Features, Starter Kits en Sollicitatievragen
Laravel 12 introduceert volledig vernieuwde Starter Kits met React 19, Vue 3, Livewire 4 en WorkOS AuthKit. Een complete gids over nieuwe features, het upgradepad en belangrijke sollicitatievragen voor 2026.