Skip to content

HARI 4: Advanced Features dan API Development ​

Bootcamp Laravel - Sistem Pengelolaan Dokumen (SiDoku) ​


πŸ“‹ Informasi Sesi ​

ItemKeterangan
Hari4 dari 5
Durasi8 Jam (09:00 - 17:00 WIB)
Topik UtamaForm Handling, Validation, Auth, File Upload, REST API
ProjectSiDoku - Fitur lanjutan

🎯 Learning Objectives ​

Setelah menyelesaikan sesi ini, peserta akan mampu:

  1. Melakukan validasi form dengan Laravel
  2. Mengimplementasikan autentikasi dengan Laravel Breeze
  3. Menangani file upload
  4. Membuat middleware custom
  5. Membangun RESTful API
  6. Menggunakan Laravel Sanctum untuk API authentication

SESI 1: Form Handling dan Validation ​


1.1 CSRF Protection ​

Laravel secara otomatis melindungi dari Cross-Site Request Forgery:

html
<form method="POST" action="/dokumen">
    @csrf
    {{-- Form fields --}}
</form>

1.2 Form Request ​

Buat Form Request untuk validasi terpisah:

bash
php artisan make:request StoreDokumenRequest

Edit app/Http/Requests/StoreDokumenRequest.php:

php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreDokumenRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true; // atau cek permission
    }

    public function rules(): array
    {
        return [
            'judul' => 'required|string|max:255',
            'nomor' => 'required|string|max:50|unique:dokumens,nomor',
            'kategori_id' => 'required|exists:kategoris,id',
            'deskripsi' => 'nullable|string|max:1000',
            'tanggal_dokumen' => 'required|date|before_or_equal:today',
            'file' => 'nullable|file|mimes:pdf,doc,docx|max:2048',
        ];
    }
    
    public function messages(): array
    {
        return [
            'judul.required' => 'Judul dokumen wajib diisi.',
            'judul.max' => 'Judul maksimal 255 karakter.',
            'nomor.required' => 'Nomor dokumen wajib diisi.',
            'nomor.unique' => 'Nomor dokumen sudah digunakan.',
            'kategori_id.required' => 'Kategori wajib dipilih.',
            'kategori_id.exists' => 'Kategori tidak valid.',
            'tanggal_dokumen.before_or_equal' => 'Tanggal tidak boleh lebih dari hari ini.',
            'file.mimes' => 'File harus berformat PDF, DOC, atau DOCX.',
            'file.max' => 'Ukuran file maksimal 2MB.',
        ];
    }
    
    public function attributes(): array
    {
        return [
            'judul' => 'Judul Dokumen',
            'nomor' => 'Nomor Dokumen',
            'kategori_id' => 'Kategori',
            'tanggal_dokumen' => 'Tanggal Dokumen',
        ];
    }
}

Penggunaan di Controller:

php
use App\Http\Requests\StoreDokumenRequest;

public function store(StoreDokumenRequest $request)
{
    // Validasi sudah otomatis berjalan
    $validated = $request->validated();
    
    Dokumen::create($validated);
    
    return redirect()->route('dokumen.index')
        ->with('success', 'Dokumen berhasil ditambahkan!');
}

1.3 Validasi Rules ​

Rules Umum:

RuleKeterangan
requiredWajib diisi
nullableBoleh kosong
stringHarus string
integerHarus integer
numericHarus angka
emailFormat email valid
min:nMinimal n karakter/nilai
max:nMaksimal n karakter/nilai
between:min,maxAntara min dan max
in:val1,val2Harus salah satu nilai
unique:table,columnUnik di tabel
exists:table,columnHarus ada di tabel
dateFormat tanggal valid
before:dateSebelum tanggal
after:dateSetelah tanggal
fileHarus file upload
imageHarus gambar
mimes:ext1,ext2Ekstensi tertentu

Validasi Kondisional:

php
public function rules(): array
{
    return [
        'email' => 'required|email|unique:users,email',
        'password' => $this->isMethod('post') 
            ? 'required|min:8|confirmed' 
            : 'nullable|min:8|confirmed',
    ];
}

1.4 Menampilkan Error di View ​

html
{{-- Semua errors --}}
@if($errors->any())
    <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
        <ul class="list-disc list-inside">
            @foreach($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

{{-- Error per field --}}
<div class="mb-4">
    <label>Judul</label>
    <input type="text" 
           name="judul" 
           value="{{ old('judul') }}"
           class="border rounded px-3 py-2 w-full @error('judul') border-red-500 @enderror">
    @error('judul')
        <p class="text-red-500 text-sm mt-1">{{ $message }}</p>
    @enderror
</div>

SESI 2: Authentication dengan Laravel Breeze ​


2.1 Instalasi Laravel Breeze ​

bash
composer require laravel/breeze --dev
php artisan breeze:install blade
npm install
npm run build
php artisan migrate

⚠️ CATATAN PENTING tentang Layout:

  • Breeze menggunakan component layout dengan {{ $slot }} di resources/views/layouts/app.blade.php
  • Jika Anda sudah membuat views dengan @extends('layouts.app') dan @section('content') di hari-2, Anda perlu menyesuaikan layout untuk mendukung keduanya:
blade
{{-- Dalam resources/views/layouts/app.blade.php --}}
<main>
    @if(isset($slot) && $slot->isNotEmpty())
        {{ $slot }}
    @else
        @yield('content')
    @endif
</main>

2.2 Fitur yang Tersedia ​

Setelah instalasi, Anda mendapatkan:

  • Login & Register
  • Password Reset
  • Email Verification
  • Profile Management

Routes yang ditambahkan:

URLFungsi
/registerHalaman registrasi
/loginHalaman login
/logoutLogout
/forgot-passwordLupa password
/profileEdit profil

2.3 Protecting Routes ​

php
// Satu route
Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware('auth');

// Group routes (RECOMMENDED - Laravel 12)
Route::middleware('auth')->group(function () {
    Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
    Route::resource('dokumen', DokumenController::class);
});

// Middleware per route
Route::resource('dokumen', DokumenController::class)
    ->middleware('auth')
    ->except(['index', 'show']);

// Òő ï¸ CATATAN PENTING Laravel 12:
// Menggunakan $this->middleware() di constructor TIDAK LAGI DIDUKUNG di Laravel 12!
// Gunakan middleware di routes (seperti di atas) sebagai gantinya.

2.4 Mengakses User yang Login ​

php
// Di Controller
$user = auth()->user();
$userId = auth()->id();
$isLoggedIn = auth()->check();

// Di Blade
@auth
    <p>Halo, {{ auth()->user()->name }}!</p>
@endauth

@guest
    <a href="/login">Login</a>
@endguest

2.5 Authorization (Policies) ​

Buat policy:

bash
php artisan make:policy DokumenPolicy --model=Dokumen

Edit app/Policies/DokumenPolicy.php:

php
<?php

namespace App\Policies;

use App\Models\Dokumen;
use App\Models\User;

class DokumenPolicy
{
    public function viewAny(User $user): bool
    {
        return true;
    }

    public function view(User $user, Dokumen $dokumen): bool
    {
        return true;
    }

    public function create(User $user): bool
    {
        return true;
    }

    public function update(User $user, Dokumen $dokumen): bool
    {
        return $user->id === $dokumen->user_id || $user->is_admin;
    }

    public function delete(User $user, Dokumen $dokumen): bool
    {
        return $user->id === $dokumen->user_id || $user->is_admin;
    }
}

Penggunaan di Controller:

Òő ï¸ PENTING Laravel 12: Untuk menggunakan $this->authorize(), controller harus menggunakan trait AuthorizesRequests!

php
namespace App\Http\Controllers;

use Illuminate\Foundation\Auth\Access\AuthorizesRequests;

class DokumenController extends Controller
{
    use AuthorizesRequests;  // Wajib untuk Laravel 12!
    
    public function update(Request $request, Dokumen $dokumen)
    {
        $this->authorize('update', $dokumen);
        // atau
        if ($request->user()->cannot('update', $dokumen)) {
            abort(403);
        }
        
        // Update logic
    }

Penggunaan di Blade:

html
@can('update', $dokumen)
    <a href="{{ route('dokumen.edit', $dokumen) }}">Edit</a>
@endcan

@cannot('delete', $dokumen)
    <span class="text-gray-400">Tidak bisa hapus</span>
@endcannot

SESI 3: File Upload ​


3.1 Konfigurasi Storage ​

Laravel menggunakan filesystem abstraction. Konfigurasi di config/filesystems.php:

php
'disks' => [
    'local' => [
        'driver' => 'local',
        'root' => storage_path('app'),
    ],
    'public' => [
        'driver' => 'local',
        'root' => storage_path('app/public'),
        'url' => env('APP_URL').'/storage',
        'visibility' => 'public',
    ],
],

Buat symbolic link:

bash
php artisan storage:link

⚠️ PENTING: Perintah ini WAJIB dijalankan sebelum upload file! Ini membuat link public/storage β†’ storage/app/public sehingga file yang diupload bisa diakses via URL.


3.2 Upload File di Controller ​

php
use Illuminate\Support\Facades\Storage;

public function store(StoreDokumenRequest $request)
{
    $validated = $request->validated();
    
    // Handle file upload
    if ($request->hasFile('file')) {
        $file = $request->file('file');
        
        // Generate nama unik
        $filename = time() . '_' . $file->getClientOriginalName();
        
        // Simpan ke disk 'public', folder 'dokumen'
        $path = $file->storeAs('dokumen', $filename, 'public');
        
        $validated['file_path'] = $path;
    }
    
    $validated['user_id'] = auth()->id();
    
    Dokumen::create($validated);
    
    return redirect()->route('dokumen.index')
        ->with('success', 'Dokumen berhasil diupload!');
}

3.3 Menampilkan dan Download File ​

Di View:

html
@if($dokumen->file_path)
    <div class="mt-4">
        <h3 class="font-medium">File Lampiran:</h3>
        <a href="{{ Storage::url($dokumen->file_path) }}" 
           target="_blank"
           class="text-blue-500 hover:underline">
            Lihat File
        </a>
        <a href="{{ route('dokumen.download', $dokumen) }}" 
           class="text-green-500 hover:underline ml-4">
            Download
        </a>
    </div>
@endif

Route dan Controller untuk Download:

php
// Route
Route::get('/dokumen/{dokumen}/download', [DokumenController::class, 'download'])
    ->name('dokumen.download');

// Controller
public function download(Dokumen $dokumen)
{
    if (!$dokumen->file_path || !Storage::disk('public')->exists($dokumen->file_path)) {
        abort(404, 'File tidak ditemukan');
    }
    
    return Storage::disk('public')->download(
        $dokumen->file_path,
        $dokumen->nomor . '.' . pathinfo($dokumen->file_path, PATHINFO_EXTENSION)
    );
}

3.4 Hapus File ​

php
public function destroy(Dokumen $dokumen)
{
    // Hapus file jika ada
    if ($dokumen->file_path && Storage::disk('public')->exists($dokumen->file_path)) {
        Storage::disk('public')->delete($dokumen->file_path);
    }
    
    $dokumen->delete();
    
    return redirect()->route('dokumen.index')
        ->with('success', 'Dokumen berhasil dihapus!');
}

SESI 4: Middleware ​


4.1 Pengenalan Middleware ​

Middleware adalah filter yang memproses HTTP request sebelum mencapai controller.

Middleware bawaan:

  • auth - Memastikan user sudah login
  • guest - Memastikan user belum login
  • verified - Email sudah diverifikasi
  • throttle - Rate limiting

4.2 Membuat Custom Middleware ​

bash
php artisan make:middleware CheckRole

Edit app/Http/Middleware/CheckRole.php:

php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class CheckRole
{
    public function handle(Request $request, Closure $next, string $role): Response
    {
        if (!$request->user()) {
            return redirect('login');
        }
        
        if ($request->user()->role !== $role) {
            abort(403, 'Anda tidak memiliki akses ke halaman ini.');
        }
        
        return $next($request);
    }
}

4.3 Mendaftarkan Middleware ​

Edit bootstrap/app.php (Laravel 11+):

php
->withMiddleware(function (Middleware $middleware) {
    $middleware->alias([
        'role' => \App\Http\Middleware\CheckRole::class,
    ]);
})

4.4 Menggunakan Middleware ​

php
// Di route
Route::get('/admin', function () {
    return 'Admin Area';
})->middleware('role:admin');

// Di group
Route::middleware(['auth', 'role:admin'])->prefix('admin')->group(function () {
    Route::get('/dashboard', [AdminController::class, 'dashboard']);
    Route::resource('/users', UserController::class);
});

SESI 5: REST API Development ​


5.1 Pengenalan REST API ​

REST (Representational State Transfer) adalah arsitektur untuk membangun web services.

HTTP Methods:

MethodCRUDContoh
GETReadAmbil data
POSTCreateBuat data baru
PUT/PATCHUpdateUpdate data
DELETEDeleteHapus data

Response Codes:

CodeArti
200OK
201Created
204No Content
400Bad Request
401Unauthorized
403Forbidden
404Not Found
422Unprocessable Entity
500Server Error

5.2 API Routes ​

API routes ada di routes/api.php:

php
<?php

use App\Http\Controllers\Api\DokumenController;
use App\Http\Controllers\Api\KategoriController;
use Illuminate\Support\Facades\Route;

// Public routes
Route::get('/kategoris', [KategoriController::class, 'index']);

// Protected routes
Route::middleware('auth:sanctum')->group(function () {
    // PENTING: Gunakan ->parameters() untuk memastikan route model binding benar
    Route::apiResource('dokumen', DokumenController::class)
        ->parameters(['dokumen' => 'dokumen']);  // Mencegah nama parameter menjadi {dokuman}
});

Òő ï¸ CATATAN: Laravel membuat nama parameter berdasarkan singular resource. Untuk resource dokumen, Laravel mungkin menghasilkan {dokuman}. Gunakan ->parameters(['dokumen' => 'dokumen']) untuk memastikan nama parameter sesuai dengan parameter di controller.


5.3 API Controller ​

bash
php artisan make:controller Api/DokumenController --api

Edit app/Http/Controllers/Api/DokumenController.php:

php
<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Dokumen;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;

class DokumenController extends Controller
{
    public function index(Request $request): JsonResponse
    {
        $query = Dokumen::with(['kategori', 'user:id,name']);
        
        if ($request->has('status')) {
            $query->where('status', $request->status);
        }
        
        if ($request->has('search')) {
            $query->cari($request->search);
        }
        
        $dokumens = $query->orderBy('created_at', 'desc')
            ->paginate($request->per_page ?? 15);
        
        return response()->json([
            'success' => true,
            'message' => 'Daftar dokumen berhasil diambil',
            'data' => $dokumens,
        ]);
    }
    
    public function store(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'judul' => 'required|string|max:255',
            'nomor' => 'required|string|max:50|unique:dokumens',
            'kategori_id' => 'required|exists:kategoris,id',
            'deskripsi' => 'nullable|string',
            'tanggal_dokumen' => 'required|date',
        ]);
        
        $validated['user_id'] = $request->user()->id;
        $validated['status'] = 'draft';
        
        $dokumen = Dokumen::create($validated);
        $dokumen->load(['kategori', 'user:id,name']);
        
        return response()->json([
            'success' => true,
            'message' => 'Dokumen berhasil ditambahkan',
            'data' => $dokumen,
        ], 201);
    }
    
    public function show(Dokumen $dokumen): JsonResponse
    {
        $dokumen->load(['kategori', 'user:id,name', 'tags']);
        
        return response()->json([
            'success' => true,
            'message' => 'Detail dokumen',
            'data' => $dokumen,
        ]);
    }
    
    public function update(Request $request, Dokumen $dokumen): JsonResponse
    {
        $validated = $request->validate([
            'judul' => 'required|string|max:255',
            'nomor' => 'required|string|max:50|unique:dokumens,nomor,' . $dokumen->id,
            'kategori_id' => 'required|exists:kategoris,id',
            'deskripsi' => 'nullable|string',
            'tanggal_dokumen' => 'required|date',
            'status' => 'nullable|in:draft,pending,approved,rejected',
        ]);
        
        $dokumen->update($validated);
        $dokumen->load(['kategori', 'user:id,name']);
        
        return response()->json([
            'success' => true,
            'message' => 'Dokumen berhasil diupdate',
            'data' => $dokumen,
        ]);
    }
    
    public function destroy(Dokumen $dokumen): JsonResponse
    {
        $dokumen->delete();
        
        return response()->json([
            'success' => true,
            'message' => 'Dokumen berhasil dihapus',
        ]);
    }
}

5.4 API Resource (Transformers) ​

Untuk kontrol lebih terhadap response JSON:

bash
php artisan make:resource DokumenResource
php artisan make:resource DokumenCollection --collection

Edit app/Http/Resources/DokumenResource.php:

php
<?php

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class DokumenResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'judul' => $this->judul,
            'nomor' => $this->nomor,
            'deskripsi' => $this->deskripsi,
            'status' => $this->status,
            'tanggal_dokumen' => $this->tanggal_dokumen->format('Y-m-d'),
            'tanggal_indonesia' => $this->tanggal_indonesia,
            'file_url' => $this->file_path 
                ? asset('storage/' . $this->file_path) 
                : null,
            'kategori' => [
                'id' => $this->kategori->id,
                'nama' => $this->kategori->nama,
            ],
            'pembuat' => [
                'id' => $this->user->id,
                'nama' => $this->user->name,
            ],
            'created_at' => $this->created_at->toIso8601String(),
            'updated_at' => $this->updated_at->toIso8601String(),
        ];
    }
}

Penggunaan:

php
use App\Http\Resources\DokumenResource;

public function show(Dokumen $dokumen): DokumenResource
{
    return new DokumenResource($dokumen->load(['kategori', 'user']));
}

public function index(): JsonResponse
{
    $dokumens = Dokumen::with(['kategori', 'user'])->paginate(15);
    
    return response()->json([
        'success' => true,
        'data' => DokumenResource::collection($dokumens),
        'meta' => [
            'current_page' => $dokumens->currentPage(),
            'total' => $dokumens->total(),
            'per_page' => $dokumens->perPage(),
        ],
    ]);
}

5.5 API Authentication dengan Sanctum ​

Instalasi:

bash
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate

⚠️ PENTING: Perintah vendor:publish akan membuat file migration untuk tabel personal_access_tokens. Jika Anda menggunakan Laravel 11+, migration ini sudah termasuk secara default. Jalankan php artisan migrate untuk membuat tabelnya.

Setup Model User:

php
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
}

Auth Routes:

php
// routes/api.php

use App\Http\Controllers\Api\AuthController;

Route::post('/register', [AuthController::class, 'register']);
Route::post('/login', [AuthController::class, 'login']);
Route::post('/logout', [AuthController::class, 'logout'])->middleware('auth:sanctum');
Route::get('/me', [AuthController::class, 'me'])->middleware('auth:sanctum');

Auth Controller:

php
<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;

class AuthController extends Controller
{
    public function register(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users',
            'password' => 'required|string|min:8|confirmed',
        ]);
        
        $user = User::create([
            'name' => $validated['name'],
            'email' => $validated['email'],
            'password' => Hash::make($validated['password']),
        ]);
        
        $token = $user->createToken('auth_token')->plainTextToken;
        
        return response()->json([
            'success' => true,
            'message' => 'Registrasi berhasil',
            'data' => [
                'user' => $user,
                'token' => $token,
            ],
        ], 201);
    }
    
    public function login(Request $request): JsonResponse
    {
        $request->validate([
            'email' => 'required|email',
            'password' => 'required',
        ]);
        
        $user = User::where('email', $request->email)->first();
        
        if (!$user || !Hash::check($request->password, $user->password)) {
            throw ValidationException::withMessages([
                'email' => ['Kredensial tidak valid.'],
            ]);
        }
        
        // Hapus token lama
        $user->tokens()->delete();
        
        $token = $user->createToken('auth_token')->plainTextToken;
        
        return response()->json([
            'success' => true,
            'message' => 'Login berhasil',
            'data' => [
                'user' => $user,
                'token' => $token,
            ],
        ]);
    }
    
    public function logout(Request $request): JsonResponse
    {
        $request->user()->currentAccessToken()->delete();
        
        return response()->json([
            'success' => true,
            'message' => 'Logout berhasil',
        ]);
    }
    
    public function me(Request $request): JsonResponse
    {
        return response()->json([
            'success' => true,
            'data' => $request->user(),
        ]);
    }
}

5.6 Testing API dengan Postman/cURL ​

Login:

bash
curl -X POST http://localhost:8000/api/login \
  -H "Content-Type: application/json" \
  -d '{"email": "admin@sidoku.test", "password": "password"}'

Get Dokumen (dengan token):

bash
curl -X GET http://localhost:8000/api/dokumen \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Accept: application/json"

Create Dokumen:

bash
curl -X POST http://localhost:8000/api/dokumen \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "judul": "SK Pengangkatan",
    "nomor": "SK/100/2025",
    "kategori_id": 1,
    "tanggal_dokumen": "2025-06-20"
  }'

PRAKTIKUM: Implementasi Fitur Lanjutan ​


Praktikum 1: Update Form dengan Validasi ​

Update resources/views/dokumen/create.blade.php dengan validasi lengkap dan file upload.


Praktikum 2: Tambah Role ke User ​

bash
php artisan make:migration add_role_to_users_table
php
public function up(): void
{
    Schema::table('users', function (Blueprint $table) {
        $table->enum('role', ['admin', 'operator', 'viewer'])->default('viewer')->after('email');
    });
}

Praktikum 3: Test API ​

  1. Register user baru via API
  2. Login dan dapatkan token
  3. Create dokumen via API
  4. List dokumen dengan filter
  5. Update dan delete dokumen

⚠️ Catatan Penting untuk Laravel 12 ​

Laravel 12 memiliki beberapa perubahan signifikan yang perlu diperhatikan:

1. Controller Middleware Deprecated ​

php
// [TIDAK DIDUKUNG] di Laravel 12:
public function __construct()
{
    $this->middleware('auth');
}

// [BENAR] GUNAKAN middleware di routes:
Route::middleware('auth')->group(function () {
    Route::resource('dokumen', DokumenController::class);
});

2. AuthorizesRequests Trait Wajib ​

php
// Untuk menggunakan $this->authorize(), tambahkan trait:
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;

class DokumenController extends Controller
{
    use AuthorizesRequests;  // WAJIB di Laravel 12!
    
    public function update(Request $request, Dokumen $dokumen)
    {
        $this->authorize('update', $dokumen);
        // ...
    }
}

3. Route Parameter Binding ​

php
// Untuk resource dengan nama non-English, gunakan ->parameters():
Route::resource('dokumen', DokumenController::class)
    ->parameters(['dokumen' => 'dokumen']);

// Ini mencegah Laravel membuat parameter {dokuman} yang salah

4. Blade Layout Compatibility ​

Jika menggunakan Breeze dengan blade stack dan custom views dengan @extends:

blade
{{-- Layout harus mendukung keduanya: --}}
<main>
    @if(isset($slot) && $slot->isNotEmpty())
        {{ $slot }}
    @else
        @yield('content')
    @endif
</main>

βœ… Checkpoint Hari 4 ​

Pastikan Anda sudah bisa:

  • Membuat Form Request untuk validasi
  • Mengimplementasikan autentikasi dengan Breeze
  • Mengupload dan mengelola file
  • Membuat middleware custom
  • Membangun RESTful API
  • Menggunakan Sanctum untuk API authentication

Selamat! Anda siap untuk Hari 5: Testing dan Deployment!


Referensi ​

Dibuat dengan ❀️ untuk ASN Indonesia