181 lines
7.1 KiB
PHP
181 lines
7.1 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Kontrakan;
|
|
use App\Models\Laundry;
|
|
use App\Models\Kriteria;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Cache;
|
|
|
|
class DashboardController extends Controller
|
|
{
|
|
/**
|
|
* Display dashboard with statistics and recent data
|
|
* OPTIMIZED VERSION - Cache untuk 5 menit
|
|
*
|
|
* @return \Illuminate\View\View
|
|
*/
|
|
public function index()
|
|
{
|
|
// ========== CACHE STATISTIK (5 menit) ==========
|
|
$stats = Cache::remember('dashboard_stats', 300, function () {
|
|
return [
|
|
'jumlahKontrakan' => Kontrakan::count(),
|
|
'jumlahLaundry' => Laundry::count(),
|
|
'jumlahKriteria' => Kriteria::count(),
|
|
];
|
|
});
|
|
|
|
// ========== DATA TERBARU (No Cache) ==========
|
|
$recentKontrakan = Kontrakan::latest()
|
|
->select('id', 'nama', 'alamat', 'harga', 'jarak', 'jumlah_kamar', 'created_at') // Only needed columns
|
|
->take(5)
|
|
->get();
|
|
|
|
$recentLaundry = Laundry::with(['layanan' => function($query) {
|
|
$query->select('laundry_id', 'jenis_layanan', 'harga')
|
|
->orderBy('harga', 'asc')
|
|
->limit(1); // Only cheapest service
|
|
}])
|
|
->select('id', 'nama', 'alamat', 'jarak', 'created_at')
|
|
->latest()
|
|
->take(5)
|
|
->get();
|
|
|
|
// ========== DATA CHART (Cache 10 menit) ==========
|
|
$chartData = Cache::remember('dashboard_charts', 600, function () {
|
|
|
|
// 1. Harga Kontrakan (Top 5)
|
|
$hargaKontrakan = Kontrakan::select('nama', 'harga')
|
|
->orderBy('harga', 'desc')
|
|
->take(5)
|
|
->get();
|
|
|
|
// 2. Harga Laundry (Top 5 dengan SUBQUERY - LEBIH CEPAT!)
|
|
$hargaLaundry = DB::table('laundry')
|
|
->join(DB::raw('(
|
|
SELECT laundry_id, MIN(harga) as min_harga
|
|
FROM layanan_laundry
|
|
GROUP BY laundry_id
|
|
) as layanan'), 'laundry.id', '=', 'layanan.laundry_id')
|
|
->select('laundry.nama', 'layanan.min_harga as harga')
|
|
->orderBy('layanan.min_harga', 'asc')
|
|
->take(5)
|
|
->get();
|
|
|
|
// 3. Distribusi Jarak (Single Query dengan CASE)
|
|
$jarakKontrakan = DB::table('kontrakans')
|
|
->selectRaw("
|
|
SUM(CASE WHEN jarak <= 500 THEN 1 ELSE 0 END) as dekat,
|
|
SUM(CASE WHEN jarak > 500 AND jarak <= 1000 THEN 1 ELSE 0 END) as sedang,
|
|
SUM(CASE WHEN jarak > 1000 THEN 1 ELSE 0 END) as jauh
|
|
")
|
|
->first();
|
|
|
|
$jarakLaundry = DB::table('laundry')
|
|
->selectRaw("
|
|
SUM(CASE WHEN jarak <= 500 THEN 1 ELSE 0 END) as dekat,
|
|
SUM(CASE WHEN jarak > 500 AND jarak <= 1000 THEN 1 ELSE 0 END) as sedang,
|
|
SUM(CASE WHEN jarak > 1000 THEN 1 ELSE 0 END) as jauh
|
|
")
|
|
->first();
|
|
|
|
// 4. Statistik Aggregate (Single Query)
|
|
$kontrakanStats = DB::table('kontrakans')
|
|
->selectRaw('
|
|
AVG(harga) as avg_harga,
|
|
AVG(jarak) as avg_jarak,
|
|
AVG(jumlah_kamar) as avg_kamar,
|
|
MIN(harga) as min_harga,
|
|
MAX(harga) as max_harga
|
|
')
|
|
->first();
|
|
|
|
// 5. Top Kontrakan by Harga
|
|
$topKontrakan = Kontrakan::select('nama', 'harga', 'jumlah_kamar', 'jarak')
|
|
->orderBy('harga', 'desc')
|
|
->take(5)
|
|
->get();
|
|
|
|
// 6. Monthly Data (Optimized dengan GROUP BY)
|
|
$monthlyData = DB::table(DB::raw('
|
|
(SELECT DATE_FORMAT(DATE_SUB(CURDATE(), INTERVAL n MONTH), "%Y-%m") as month
|
|
FROM (SELECT 0 as n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) as numbers) as months
|
|
'))
|
|
->leftJoin(DB::raw('kontrakans'), DB::raw('DATE_FORMAT(kontrakans.created_at, "%Y-%m")'), '=', 'months.month')
|
|
->leftJoin(DB::raw('laundry'), DB::raw('DATE_FORMAT(laundry.created_at, "%Y-%m")'), '=', 'months.month')
|
|
->selectRaw('
|
|
months.month,
|
|
COUNT(DISTINCT kontrakans.id) as kontrakan_count,
|
|
COUNT(DISTINCT laundry.id) as laundry_count
|
|
')
|
|
->groupBy('months.month')
|
|
->orderBy('months.month', 'asc')
|
|
->get()
|
|
->map(function($item) {
|
|
return [
|
|
'month' => date('M Y', strtotime($item->month . '-01')),
|
|
'kontrakan' => $item->kontrakan_count,
|
|
'laundry' => $item->laundry_count
|
|
];
|
|
});
|
|
|
|
return [
|
|
'hargaKontrakan' => $hargaKontrakan,
|
|
'hargaLaundry' => $hargaLaundry,
|
|
'jarakKontrakan' => [
|
|
'dekat' => $jarakKontrakan->dekat ?? 0,
|
|
'sedang' => $jarakKontrakan->sedang ?? 0,
|
|
'jauh' => $jarakKontrakan->jauh ?? 0,
|
|
],
|
|
'jarakLaundry' => [
|
|
'dekat' => $jarakLaundry->dekat ?? 0,
|
|
'sedang' => $jarakLaundry->sedang ?? 0,
|
|
'jauh' => $jarakLaundry->jauh ?? 0,
|
|
],
|
|
'avgHargaKontrakan' => $kontrakanStats->avg_harga ?? 0,
|
|
'avgJarakKontrakan' => $kontrakanStats->avg_jarak ?? 0,
|
|
'avgKamarKontrakan' => $kontrakanStats->avg_kamar ?? 0,
|
|
'minHargaKontrakan' => $kontrakanStats->min_harga ?? 0,
|
|
'maxHargaKontrakan' => $kontrakanStats->max_harga ?? 0,
|
|
'topKontrakan' => $topKontrakan,
|
|
'monthlyData' => $monthlyData,
|
|
];
|
|
});
|
|
|
|
// ========== TAMBAHAN DATA YANG HILANG ==========
|
|
$additionalData = [
|
|
// Data review
|
|
'totalReviews' => \App\Models\Review::count() ?? 0,
|
|
|
|
// Data admin
|
|
'totalAdmins' => \App\Models\User::where('role', 'admin')->count() ?? 1,
|
|
|
|
// Average kecepatan laundry (dari estimasi_selesai dalam jam)
|
|
'avgKecepatan' => round(DB::table('layanan_laundry')
|
|
->where('estimasi_selesai', '>', 0)
|
|
->avg('estimasi_selesai') ?? 24, 1),
|
|
];
|
|
|
|
// Merge semua data
|
|
$data = array_merge($stats, $chartData, $additionalData, [
|
|
'recentKontrakan' => $recentKontrakan,
|
|
'recentLaundry' => $recentLaundry,
|
|
]);
|
|
|
|
return view('dashboard.index', $data);
|
|
}
|
|
|
|
/**
|
|
* Clear dashboard cache (optional - bisa dipanggil manual)
|
|
*/
|
|
public function clearCache()
|
|
{
|
|
Cache::forget('dashboard_stats');
|
|
Cache::forget('dashboard_charts');
|
|
|
|
return redirect()->route('dashboard')
|
|
->with('success', 'Cache dashboard berhasil dibersihkan!');
|
|
}
|
|
} |