# Laravel 11: Budowa kompletnej aplikacji od podstaw > Kompleksowy przewodnik po budowie aplikacji w Laravel 11 z uwierzytelnianiem, REST API, Eloquent ORM i wdrożeniem produkcyjnym. Praktyczny poradnik dla początkujących i średnio zaawansowanych programistów. - Published: 2026-01-14 - Updated: 2026-04-08 - Author: SharpSkill - Tags: laravel, php, rest api, eloquent, tutorial - Reading time: 15 min --- Laravel 11 redefiniuje nowoczesne programowanie w PHP dzięki uproszczonej architekturze i zoptymalizowanej wydajności. Ta główna wersja usuwa wiele domyślnych plików konfiguracyjnych, wprowadza lżejszą strukturę projektu i znacząco poprawia komfort pracy programisty. Niniejszy przewodnik prowadzi przez budowę kompletnej aplikacji do zarządzania zadaniami — od instalacji po wdrożenie produkcyjne. > **Najważniejsze zmiany w Laravel 11** > > Laravel 11 przyjmuje minimalistyczną strukturę: brak plików Kernel, skonsolidowana konfiguracja w bootstrap/app.php i czystszy katalog app/. Framework wymaga minimalnie PHP 8.2 i natywnie integruje SQLite do szybkiego startu. ## Instalacja i konfiguracja projektu Laravel 11 instaluje się przez Composer lub dedykowany instalator Laravel. Nowa struktura projektu jest lżejsza, z mniejszą liczbą plików konfiguracyjnych do zarządzania od samego początku. ```bash # 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-manager ``` Instalator oferuje kilka interaktywnych opcji: wybór starter kitu (Breeze, Jetstream), frameworka testowego (Pest, PHPUnit) i bazy danych. ```bash # terminal # Start the development server php artisan serve # Server starts at http://localhost:8000 ``` ### Konfiguracja bazy danych Laravel 11 domyślnie używa SQLite, co jest idealne do pracy deweloperskiej. W przypadku aplikacji produkcyjnych konfiguracja MySQL lub PostgreSQL odbywa się w pliku `.env`. ```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=secret ``` Weryfikacja połączenia z bazą danych odbywa się za pomocą komendy Artisan. ```bash # terminal # Verify database connection php artisan db:show # Run default migrations php artisan migrate ``` ## Tworzenie modeli i migracji Eloquent ORM upraszcza manipulację danymi dzięki ekspresyjnym modelom. Komenda `make:model` generuje model, migrację, kontroler i fabrykę za jednym razem. ```bash # terminal # Generate Task model with migration, factory, seeder, and controller php artisan make:model Task -mfsc # -m : migration # -f : factory # -s : seeder # -c : controller ``` Ta komenda tworzy cztery niezbędne pliki do kompletnego zarządzania zadaniami. ```php 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'); } }; ``` Migracja definiuje strukturę tabeli z relacjami, indeksami i soft delete, aby zapobiec przypadkowej utracie danych. ```php '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(), ]); } } ``` Model Task wykorzystuje Eloquent scopes do czytelnych i wielokrotnie używalnych zapytań. Stałe centralizują dozwolone wartości statusów i priorytetów. > **Soft Deletes** > > Trait SoftDeletes dodaje kolumnę deleted_at. Usunięte zadania nie znikają z bazy danych, lecz zostają oznaczone jako usunięte. Metoda withTrashed() pozwala uwzględnić je w zapytaniach. ## Konfiguracja uwierzytelniania z Breeze Laravel Breeze zapewnia kompletne uwierzytelnianie z widokami Blade lub API. Instalacja zajmuje kilka minut i generuje wszystko, co potrzebne: trasy, kontrolery, widoki i testy. ```bash # 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 migrate ``` Breeze generuje trasy uwierzytelniania, kontrolery i widoki do rejestracji, logowania, resetowania hasła i weryfikacji e-mail. ```php '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(); } } ``` ## Tworzenie kontrolerów i tras Kontroler orkiestruje logikę biznesową pomiędzy żądaniami HTTP a modelem. Laravel 11 zachęca do stosowania kontrolerów jednoakcyjnych lub zasobów RESTful. ```php 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!'); } } ``` Kontroler wykorzystuje Gates do autoryzacji, wbudowaną walidację i route model binding, co zapewnia zwięzły i bezpieczny kod. ### Definiowanie tras Trasy definiują adresy URL i przypisują je do akcji kontrolera. Laravel 11 centralizuje trasy w pliku `routes/web.php`. ```php 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'; ``` Metoda `Route::resource()` automatycznie generuje siedem standardowych tras RESTful: index, create, store, show, edit, update i destroy. ## Konfiguracja polityk autoryzacji Polityki centralizują logikę autoryzacji dla każdego modelu. Określają, kto może wykonywać jakie operacje na zasobach. ```bash # terminal # Generate the policy for Task php artisan make:policy TaskPolicy --model=Task ``` ```php 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; } } ``` Laravel automatycznie wykrywa polityki dzięki konwencjom nazewniczym. `TaskPolicy` stosuje się do modelu `Task`. > **Bezpieczeństwo polityk** > > Polityki są kluczowe dla bezpieczeństwa. Bez nich użytkownik mógłby manipulować adresami URL, aby uzyskać dostęp do zadań innych użytkowników. Zawsze należy weryfikować własność zasobów. ## Budowa REST API Laravel upraszcza tworzenie RESTful API dzięki dedykowanym trasom, zasobom JSON i Sanctum do uwierzytelniania. ```bash # 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 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.', ]); } } ``` ### Zasoby API do transformacji danych Zasoby API przekształcają modele Eloquent w ustrukturyzowane odpowiedzi JSON, precyzyjnie kontrolując eksponowane dane. ```php $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, ]), ]; } } ``` ### Trasy API z uwierzytelnianiem Sanctum ```php 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(), ]); }); }); ``` ## Testy automatyczne z Pest Laravel 11 domyślnie integruje Pest PHP — elegancki i ekspresyjny framework testowy. Testy zapewniają poprawne działanie aplikacji po każdej modyfikacji. ```php 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', ]); }); ``` Uruchamianie testow z Pest. ```bash # 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 --parallel ``` ## Optymalizacje produkcyjne Przed wdrożeniem kilka optymalizacji poprawia wydajność aplikacji Laravel. ```bash # 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 build ``` ### Konfiguracja pliku .env dla produkcji ```env # .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=tls ``` > **Zmienne wrażliwe** > > Nigdy nie commituj pliku .env do repozytorium Git. Plik .env.example służy jako szablon, a zmienne wrażliwe konfiguruje się bezpośrednio na serwerze produkcyjnym lub przez menedżery sekretów. ## Podsumowanie Laravel 11 znacząco upraszcza nowoczesne programowanie w PHP dzięki uproszczonej strukturze i inteligentnym konwencjom. Niniejszy przewodnik obejmuje podstawy budowy kompletnej aplikacji: modele Eloquent, uwierzytelnianie, kontrolery RESTful, polityki autoryzacji, API z Sanctum i testy automatyczne. ### Lista kontrolna dla wysokiej jakości aplikacji Laravel - Migracje do wszystkich zmian w schemacie bazy danych - Polityki zabezpieczające dostęp do zasobów - Walidacja wszystkich danych wejściowych w kontrolerach - Zasoby API do kontrolowania eksponowanych danych - Testy dla krytycznych funkcjonalności - Konfiguracja cache i optymalizacji przed wdrożeniem - Zabezpieczenie wrażliwych zmiennych środowiskowych Ekosystem Laravel kontynuuje rozwój dzięki pakietom takim jak Livewire do reaktywnych interfejsów, Horizon do zarządzania kolejkami i Octane do wyjątkowej wydajności. Opanowanie tych podstaw otwiera drzwi do solidnych i łatwych w utrzymaniu aplikacji PHP. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/pl/blog/laravel/laravel-11-building-complete-application