Skip to content

Instantly share code, notes, and snippets.

@ay4t
Created July 29, 2025 17:30
Show Gist options
  • Select an option

  • Save ay4t/b294ffba1aa45a0b3aabbcd2cec9c7fe to your computer and use it in GitHub Desktop.

Select an option

Save ay4t/b294ffba1aa45a0b3aabbcd2cec9c7fe to your computer and use it in GitHub Desktop.

Panduan Implementasi Validasi Kustom dengan Closure di Laravel

Dokumen ini menjelaskan cara membuat aturan validasi kustom secara langsung di dalam controller menggunakan Closure. Metode ini sangat efektif untuk logika validasi yang spesifik untuk satu controller dan tidak memerlukan reuse di tempat lain, sehingga menjaga kode tetap ringkas dan mudah dibaca.

Studi Kasus: Validasi Jadwal Tumpang Tindih

Sebagai contoh, kita akan mengimplementasikan validasi untuk mencegah pembuatan atau pembaruan jadwal terapi yang waktunya tumpang tindih dengan jadwal lain dalam paket terapi dan hari yang sama.

Prasyarat

  • Controller Anda mewarisi dari BaseController yang sudah memiliki properti $rules.
  • Aturan validasi dasar sudah didefinisikan di dalam Model terkait (contoh: TherapySchedule::$rules).

Langkah-langkah Implementasi

1. Override Metode store dan update

Langkah pertama adalah meng-override metode store (untuk pembuatan data baru) dan update (untuk pembaruan data) di dalam controller Anda.

// app/Http/Controllers/Therapy/TherapyScheduleController.php

use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

// ...

public function store(Request $request): JsonResponse
{
    // Logika validasi akan ditambahkan di sini
    return parent::store($request);
}

public function update($id): JsonResponse
{
    // Logika validasi akan ditambahkan di sini
    return parent::update($id);
}

2. Menambahkan Aturan Validasi Closure

Di dalam metode yang telah di-override, kita akan menambahkan aturan validasi baru ke properti $this->rules. Karena aturan validasi dari model bisa berupa string ('required|date_format:H:i'), kita perlu mengubahnya menjadi array terlebih dahulu untuk menghindari error [] operator not supported for strings.

// Di dalam metode store()

// Pastikan aturan adalah array
if (is_string($this->rules['start_time'])) {
    $this->rules['start_time'] = explode('|', $this->rules['start_time']);
}

// Tambahkan closure sebagai aturan validasi baru
$this->rules['start_time'][] = function ($attribute, $value, $fail) use ($request) {
    $this->validateNoTimeOverlap($request, $fail);
};
  • $attribute: Nama field yang divalidasi (start_time).
  • $value: Nilai dari field tersebut.
  • $fail: Sebuah Closure yang dipanggil jika validasi gagal.
  • use ($request): Mengimpor variabel $request ke dalam scope Closure.

3. Membuat Logika Validasi Terpusat

Untuk menjaga kode tetap bersih (DRY - Don't Repeat Yourself), kita buat sebuah metode privat yang berisi logika validasi utama. Metode ini akan dipanggil dari Closure di store dan update.

// app/Http/Controllers/Therapy/TherapyScheduleController.php

private function validateNoTimeOverlap(Request $request, \Closure $fail, int $ignoreId = null): void
{
    $query = \App\Models\TherapySchedule::where('therapy_package_id', $request->therapy_package_id)
        ->where('day_of_week', $request->day_of_week)
        ->where(function ($q) use ($request) {
            // Kondisi untuk memeriksa tumpang tindih waktu
            // (start_time_baru < end_time_lama) AND (end_time_baru > start_time_lama)
            $q->where('start_time', '<', $request->end_time)
              ->where('end_time', '>', $request->start_time);
        });

    // Jika sedang dalam mode update, abaikan data yang sedang diedit
    if ($ignoreId) {
        $query->where('id', '!=', $ignoreId);
    }

    // Jika ditemukan data yang cocok, validasi gagal
    if ($query->exists()) {
        $fail('Jadwal yang dimasukkan tumpang tindih dengan jadwal yang sudah ada.');
    }
}

4. Kode Lengkap Controller

Berikut adalah implementasi penuh pada TherapyScheduleController:

<?php

namespace App\Http\Controllers\Therapy;

use Illuminate\Http\Request;
use App\Models\TherapySchedule;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\BaseController;

class TherapyScheduleController extends BaseController
{
    // ... constructor ...

    public function store(Request $request): JsonResponse
    {
        $request->merge(['is_active' => $request->has('is_active') ? 1 : 0]);

        if (is_string($this->rules['start_time'])) {
            $this->rules['start_time'] = explode('|', $this->rules['start_time']);
        }
        $this->rules['start_time'][] = function ($attribute, $value, $fail) use ($request) {
            $this->validateNoTimeOverlap($request, $fail);
        };

        return parent::store($request);
    }

    public function update($id): JsonResponse
    {
        $this->request->merge(['is_active' => $this->request->has('is_active') ? 1 : 0]);

        if (is_string($this->rules['start_time'])) {
            $this->rules['start_time'] = explode('|', $this->rules['start_time']);
        }
        $this->rules['start_time'][] = function ($attribute, $value, $fail) use ($id) {
            $this->validateNoTimeOverlap($this->request, $fail, $id);
        };

        return parent::update($id);
    }

    private function validateNoTimeOverlap(Request $request, \Closure $fail, int $ignoreId = null): void
    {
        $query = TherapySchedule::where('therapy_package_id', $request->therapy_package_id)
            ->where('day_of_week', $request->day_of_week)
            ->where(function ($q) use ($request) {
                $q->where('start_time', '<', $request->end_time)
                  ->where('end_time', '>', $request->start_time);
            });

        if ($ignoreId) {
            $query->where('id', '!=', $ignoreId);
        }

        if ($query->exists()) {
            $fail('Jadwal yang dimasukkan tumpang tindih dengan jadwal yang sudah ada.');
        }
    }
}

Kesimpulan

Menggunakan Closure untuk validasi kustom adalah cara yang rapi dan efisien untuk menangani logika yang unik tanpa harus membuat kelas Rule terpisah. Ini membuat controller tetap menjadi sumber utama untuk logika bisnis yang spesifik, sementara validasi yang lebih umum tetap berada di dalam model.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment