Laravel 11: Membangun Aplikasi Lengkap dari Awal

Panduan lengkap membangun aplikasi Laravel 11 dengan autentikasi, REST API, Eloquent ORM, dan deployment. Tutorial praktis untuk developer pemula hingga menengah.

Tutorial Laravel 11 untuk membangun aplikasi lengkap

Laravel 11 menghadirkan perubahan besar dalam pengembangan PHP modern dengan arsitektur yang lebih ramping dan performa yang dioptimalkan. Rilis utama ini menghapus banyak file konfigurasi bawaan, memperkenalkan struktur yang lebih ringkas, dan meningkatkan pengalaman developer secara signifikan. Panduan ini membahas proses membangun aplikasi manajemen tugas secara lengkap, mulai dari instalasi hingga deployment.

Fitur Unggulan Laravel 11

Laravel 11 mengusung struktur minimalis: tidak ada lagi file Kernel, konfigurasi terkonsolidasi di bootstrap/app.php, dan direktori app/ yang lebih bersih. Framework ini membutuhkan minimal PHP 8.2 dan mengintegrasikan SQLite secara native untuk startup cepat.

Instalasi dan Pengaturan Proyek

Laravel 11 dapat diinstal melalui Composer atau Laravel installer. Struktur proyek baru lebih ringkas, dengan lebih sedikit file konfigurasi yang perlu dikelola sejak awal.

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

Installer menyediakan beberapa opsi interaktif: pilihan starter kit (Breeze, Jetstream), framework testing (Pest, PHPUnit), dan database.

bash
# terminal
# Start the development server
php artisan serve

# Server starts at http://localhost:8000

Konfigurasi Database

Laravel 11 menggunakan SQLite secara default, ideal untuk pengembangan. Untuk aplikasi produksi, konfigurasi MySQL atau PostgreSQL dilakukan di file .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

Verifikasi koneksi database menggunakan perintah Artisan.

bash
# terminal
# Verify database connection
php artisan db:show

# Run default migrations
php artisan migrate

Membuat Model dan Migrasi

Eloquent ORM menyederhanakan manipulasi data dengan model yang ekspresif. Perintah make:model menghasilkan model, migrasi, controller, dan factory sekaligus.

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

Perintah ini membuat empat file penting untuk manajemen tugas yang lengkap.

php
<?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');
    }
};

Migrasi ini mendefinisikan struktur tabel dengan relasi, indeks, dan soft delete untuk mencegah kehilangan data secara tidak sengaja.

php
<?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(),
        ]);
    }
}

Model Task menggunakan Eloquent scope untuk query yang mudah dibaca dan dapat digunakan kembali. Konstanta memusatkan nilai valid untuk status dan prioritas.

Soft Deletes

Trait SoftDeletes menambahkan kolom deleted_at. Tugas yang dihapus tidak hilang dari database, melainkan ditandai sebagai terhapus. Gunakan withTrashed() untuk menyertakan data tersebut dalam query.

Mengatur Autentikasi dengan Breeze

Laravel Breeze menyediakan autentikasi lengkap dengan tampilan Blade atau API. Instalasi membutuhkan beberapa menit dan menghasilkan semua yang diperlukan: route, controller, view, dan test.

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 menghasilkan route autentikasi, controller, dan view untuk registrasi, login, reset password, dan verifikasi email.

php
<?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();
    }
}

Siap menguasai wawancara Laravel Anda?

Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.

Membuat Controller dan Route

Controller mengatur logika bisnis antara permintaan HTTP dan model. Laravel 11 mendorong penggunaan single-action controller atau RESTful resource.

php
<?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!');
    }
}

Controller ini menggunakan Gate untuk otorisasi, validasi bawaan, dan route model binding untuk kode yang ringkas dan aman.

Mendefinisikan Route

Route mendefinisikan URL dan mengaitkannya dengan aksi controller. Laravel 11 memusatkan route di routes/web.php.

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';

Method Route::resource() secara otomatis menghasilkan tujuh route RESTful standar: index, create, store, show, edit, update, dan destroy.

Mengatur Kebijakan Otorisasi

Policy memusatkan logika otorisasi untuk setiap model. Policy menentukan siapa yang dapat melakukan tindakan tertentu pada resource.

bash
# terminal
# Generate the policy for Task
php artisan make:policy TaskPolicy --model=Task
php
<?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;
    }
}

Laravel secara otomatis menemukan policy melalui konvensi penamaan. TaskPolicy berlaku untuk model Task.

Keamanan Policy

Policy sangat penting untuk keamanan. Tanpa policy, pengguna dapat memanipulasi URL untuk mengakses tugas milik pengguna lain. Selalu verifikasi kepemilikan resource.

Membangun REST API

Laravel menyederhanakan pembuatan RESTful API dengan route khusus, JSON resource, dan Sanctum untuk autentikasi.

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
<?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 Resource untuk Transformasi Data

API resource mentransformasi model Eloquent menjadi respons JSON terstruktur, mengontrol data yang diekspos secara presisi.

php
<?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,
            ]),
        ];
    }
}

Route API dengan Autentikasi Sanctum

php
<?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(),
        ]);
    });
});

Pengujian Otomatis dengan Pest

Laravel 11 mengintegrasikan Pest PHP secara default, framework testing yang elegan dan ekspresif. Pengujian memastikan aplikasi berfungsi dengan benar setelah setiap perubahan.

php
<?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',
        ]);
});

Menjalankan pengujian dengan 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

Optimasi Produksi

Sebelum deployment, beberapa optimasi meningkatkan performa aplikasi 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

Konfigurasi File .env Produksi

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
Variabel Sensitif

Jangan pernah meng-commit file .env ke repositori Git. Gunakan .env.example sebagai template dan konfigurasikan variabel sensitif langsung di server produksi atau melalui secret manager.

Kesimpulan

Laravel 11 secara signifikan menyederhanakan pengembangan PHP modern dengan struktur yang ramping dan konvensi yang cerdas. Panduan ini membahas dasar-dasar untuk membangun aplikasi lengkap: model Eloquent, autentikasi, controller RESTful, kebijakan otorisasi, API dengan Sanctum, dan pengujian otomatis.

Checklist untuk Aplikasi Laravel Berkualitas

  • Gunakan migrasi untuk semua perubahan skema
  • Implementasikan policy untuk mengamankan akses resource
  • Validasi semua input pengguna di controller
  • Gunakan API resource untuk mengontrol data yang diekspos
  • Tulis pengujian untuk fitur-fitur kritis
  • Konfigurasikan cache dan optimasi sebelum deployment
  • Amankan variabel lingkungan yang sensitif

Mulai berlatih!

Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.

Ekosistem Laravel terus berkembang dengan paket seperti Livewire untuk antarmuka reaktif, Horizon untuk manajemen antrian, dan Octane untuk performa luar biasa. Menguasai dasar-dasar ini membuka jalan menuju aplikasi PHP yang robust dan mudah dipelihara.

Tag

#laravel
#php
#rest api
#eloquent
#tutorial

Bagikan

Artikel terkait