Beranda
laravel
optimasi
OPTIMASI QUERY UNTUK DATA BESAR DI LARAVEL (10 JUTA ROWS)

OPTIMASI QUERY UNTUK DATA BESAR DI LARAVEL (10 JUTA ROWS)

April 22, 2025
0 Komentar

OPTIMASI QUERY UNTUK DATA BESAR DI LARAVEL (10 JUTA ROWS)


๐ŸŽฏ Tujuan

Mengoptimalkan performa query list data dari tabel besar (mutations, 10 juta baris), agar:

  • ⚡ Cepat (sub-second query time)
  • ✅ Modular & scalable
  • ๐Ÿ’ก Mudah dirawat dan dikembangkan
  • ๐Ÿ” Aman dengan validasi input

๐Ÿงฉ Masalah Awal

  • Query lambat (hingga 45 detik), meskipun sudah ada index.
  • Banyak when() di controller membuat kode kompleks.
  • Menggunakan simplePaginate() yang masih menghitung total data.
  • Relasi berat (with('mutationRetur')) selalu di-load.

✅ Solusi Utama

1. Gunakan pendekatan modular filter class:

  • Setiap filter dibuat di class-nya sendiri: Search.php, CashFlow.php, dll.
  • Mudah dikembangkan saat filter bertambah.
  • Dapat digunakan lintas fitur: list, export, reporting.

2. Gunakan cursorPaginate() untuk performa tinggi:

  • Tidak menghitung total data.
  • Lebih ringan dari paginate() dan simplePaginate().
  • Cocok untuk dataset besar.

3. Validasi input request pakai FormRequest:

  • Lebih aman, mencegah query tidak valid.
  • Menampilkan pesan error otomatis.

๐Ÿ” Dua Pendekatan Modular Filtering


๐Ÿงฉ Pendekatan 1: Manual panggil filter class di controller

Contoh Filter Class: Search.php

namespace App\Filters\MutationFilters;

use Illuminate\Database\Eloquent\Builder;

class Search
{
    public static function apply(Builder $query, $value, $type)
    {
        if (!$value) return $query;

        return match ($type) {
            'description' => $query->where('description', 'ilike', "%$value%"),
            'debit'       => $query->where('debit', $value),
            'credit'      => $query->where('credit', $value),
            default       => $query->where('key_code', $value),
        };
    }
}

Di Controller:

use App\Filters\MutationFilters\Search;

public function index(Request $request)
{
    $query = Mutation::select(...)->with('bank:id,name')->orderByDesc('id');
    $query = Search::apply($query, $request->search, $request->type ?? 'key_code');
    return $query->cursorPaginate(20);
}

๐Ÿงฑ Pendekatan 2: Dynamic Modular Filtering dengan Trait Filterable

Filterable.php

namespace App\Traits;

use Illuminate\Http\Request;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Str;

trait Filterable
{
    public function scopeFilter(Builder $query, Request $request): Builder
    {
        $modelName = class_basename($query->getModel());
        $namespace = 'App\\Filters\\' . $modelName . 'Filters\\';

        foreach ($request->all() as $key => $value) {
            $class = $namespace . Str::studly($key);

            if (class_exists($class) && method_exists($class, 'apply')) {
                $query = $class::apply($query, $value);
            }
        }

        return $query;
    }
}

Di Model:

use App\Traits\Filterable;

class Mutation extends Model
{
    use Filterable;
}

Di Controller:

public function index(ListMutationRequest $request)
{
    return Mutation::with('bank:id,name')
        ->filter($request)
        ->orderByDesc('id')
        ->cursorPaginate(20);
}

๐Ÿ›ก️ Validasi dengan Form Request

ListMutationRequest.php

class ListMutationRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'search' => ['nullable', 'string'],
            'type' => ['nullable', 'in:key_code,description,debit,credit'],
            'start_date' => ['nullable', 'date'],
            'end_date' => ['nullable', 'date'],
            'is_key_code_null' => ['nullable', 'in:true,false'],
            'bank' => ['nullable', 'exists:banks,id'],
            'cash_flow' => ['nullable', 'in:credit,debit'],
            'doesnt_have_refund' => ['nullable', 'in:true'],
        ];
    }
}

๐Ÿ“ฆ Perbandingan dengan Spatie Laravel Query Builder

Fitur Custom Filterable (Punyamu) Spatie Laravel Query Builder
Performa Data Besar ✅ Sangat cocok (cursorPaginate) ❌ Tidak mendukung
Modular & Clean
Include relasi Manual Built-in allowedIncludes()
Sorting Manual Built-in allowedSorts()
Validasi Laravel-native Harus ditangani manual
Dokumentasi & Komunitas Custom Lengkap dan populer

Kesimpulan: Pendekatan Filterable + cursorPaginate adalah pilihan terbaik untuk dataset besar seperti milikmu.


๐Ÿง  Tips Tambahan

  • Gunakan index di kolom: key_code, post_date, bank_id, debit, credit
  • Gunakan EXPLAIN ANALYZE untuk cek performa query
  • Hindari relasi berat kecuali dibutuhkan (mutationRetur)
  • Gunakan withQueryString() jika ingin pagination mempertahankan filter

✅ Penutup

Dengan pendekatan ini, kamu sekarang punya sistem:

  • ๐Ÿ’จ Super cepat
  • ๐Ÿงฑ Modular
  • ๐Ÿงผ Bersih & maintainable
  • ๐Ÿ”’ Aman (dengan validasi FormRequest)

Tidak ada komentar

Popular Posts