getRankingData(); // Jika bobot belum dihitung, arahkan kembali ke menu perbandingan if (isset($data['redirect'])) { return redirect()->route('perbandingan.index')->with('error', $data['error']); } return view('perankingan.index', $data); } /** * Menampilkan Detail Perhitungan Tiap Warga (Untuk Sidang / Dosen) */ public function show($id) { $warga = Warga::findOrFail($id); $bobot = Kriteria::pluck('bobot', 'kode')->toArray(); if (empty($bobot) || array_sum($bobot) == 0) { return redirect()->route('perbandingan.index')->with('error', 'Bobot kriteria belum dihitung.'); } // 1. Dapatkan Skor (1-5) dari Data Riil sesuai Tabel Validasi Desa $skor = [ 'C1' => $this->skorPendapatan($warga->c1_pendapatan), 'C2' => $this->skorTanggungan($warga->c2_tanggungan), 'C3' => $this->skorRumah($warga->c3_status_rumah), 'C4' => $this->skorAset($warga->c4_aset), 'C5' => $this->skorPekerjaan($warga->c5_pekerjaan), 'C6' => $this->skorUsia($warga->c6_usia), ]; // 2. Format Data Riil untuk Ditampilkan di Tabel Detail $dataRiil = [ 'C1' => 'Rp ' . number_format((int) preg_replace('/[^0-9]/', '', $warga->c1_pendapatan), 0, ',', '.'), 'C2' => $warga->c2_tanggungan . ' Orang', 'C3' => $warga->c3_status_rumah, 'C4' => $warga->c4_aset, 'C5' => $warga->c5_pekerjaan, 'C6' => $warga->c6_usia . ' Tahun', ]; $namaKriteria = [ 'C1' => 'Pendapatan', 'C2' => 'Jumlah Tanggungan', 'C3' => 'Status Tempat Tinggal', 'C4' => 'Kepemilikan Aset', 'C5' => 'Pekerjaan', 'C6' => 'Usia Kepala Keluarga', ]; // 3. Susun Array Detail Perhitungan $perhitungan = []; $totalSkorAkhir = 0; foreach ($skor as $kode => $nilaiSkor) { $nilaiBobot = $bobot[$kode] ?? 0; // Normalisasi: Skor dibagi 5 (Karena nilai maksimal di tabel adalah 5) $normalisasi = $nilaiSkor / 5; $hasilKali = $normalisasi * $nilaiBobot; $totalSkorAkhir += $hasilKali; $perhitungan[$kode] = [ 'nama' => $namaKriteria[$kode], 'riil' => $dataRiil[$kode], 'skor' => $nilaiSkor, 'normalisasi' => $normalisasi, 'bobot' => $nilaiBobot, 'hasil' => $hasilKali ]; } // Kirim ke view (Pastikan file resources/views/perankingan/show.blade.php sudah dibuat) return view('perankingan.show', compact('warga', 'perhitungan', 'totalSkorAkhir')); } /** * Logika Utama Perhitungan Ranking */ private function getRankingData() { // 1. Ambil Bobot Final dari Tabel Kriteria (Hasil F-AHP Chang) $bobot = Kriteria::pluck('bobot', 'kode')->toArray(); // Validasi: Pastikan bobot sudah dihitung if (empty($bobot) || array_sum($bobot) == 0) { return [ 'redirect' => true, 'error' => 'Bobot kriteria belum dihitung. Silakan lakukan perhitungan di menu Perbandingan terlebih dahulu!', ]; } // 2. Ambil Warga yang sudah di-setujui oleh Verifikator Desa $wargas = Warga::where('status_verifikasi', 'setuju')->get(); $hasilRanking = []; $threshold = 0.65; // Ambang batas kelayakan (65%) foreach ($wargas as $w) { // TAHAP TRANSFORMASI: Data Riil -> Skor (1, 3, 4, 5) $s1 = $this->skorPendapatan($w->c1_pendapatan); $s2 = $this->skorTanggungan($w->c2_tanggungan); $s3 = $this->skorRumah($w->c3_status_rumah); $s4 = $this->skorAset($w->c4_aset); $s5 = $this->skorPekerjaan($w->c5_pekerjaan); $s6 = $this->skorUsia($w->c6_usia); /** * TAHAP PERHITUNGAN: * Rumus: Σ ((Skor / 5) * Bobot) * Normalisasi skor dibagi 5 (karena skor tertinggi 5) sebelum dikalikan bobot. */ $totalSkor = ( (($s1 / 5) * ($bobot['C1'] ?? 0)) + (($s2 / 5) * ($bobot['C2'] ?? 0)) + (($s3 / 5) * ($bobot['C3'] ?? 0)) + (($s4 / 5) * ($bobot['C4'] ?? 0)) + (($s5 / 5) * ($bobot['C5'] ?? 0)) + (($s6 / 5) * ($bobot['C6'] ?? 0)) ); $hasilRanking[] = [ 'id' => $w->id, // PENTING: Ditambahkan agar tombol "Detail" bisa mendeteksi ID Warga 'nama' => $w->nama, 'nik' => $w->nik, 'rt_rw' => "RT $w->rt / RW $w->rw", 'skor_akhir' => $totalSkor, 'status' => $totalSkor >= $threshold ? 'LAYAK' : 'TIDAK LAYAK', ]; } // 3. TAHAP PERANGKINGAN: Urutkan Skor Terbesar ke Terkecil usort($hasilRanking, function ($a, $b) { return $b['skor_akhir'] <=> $a['skor_akhir']; }); return [ 'hasilRanking' => $hasilRanking, 'threshold' => $threshold, ]; } // --- HELPER SCORING: DISINKRONKAN DENGAN TABEL DATA RIIL --- private function skorPendapatan($val) { // Pastikan input berupa angka murni (hapus "Rp" atau titik jika ada) $val = (int) preg_replace('/[^0-9]/', '', $val); if ($val <= 500000) return 5; if ($val <= 1000000) return 4; if ($val <= 1500000) return 3; return 1; // > Rp 1.500.000 } private function skorTanggungan($val) { $val = (int) $val; if ($val >= 5) return 5; if ($val >= 3) return 3; // Range 3-4 Orang return 1; // Range 1-2 Orang } private function skorRumah($val) { $val = strtolower($val); // antisipasi huruf besar/kecil if (str_contains($val, 'numpang') || str_contains($val, 'menumpang')) return 5; if (str_contains($val, 'sewa') || str_contains($val, 'kontrak')) return 3; return 1; // Milik Sendiri } private function skorAset($val) { $val = strtolower($val); if (str_contains($val, 'tidak memiliki')) return 5; if (str_contains($val, 'elektronik') || str_contains($val, 'tv') || str_contains($val, 'hp')) return 3; return 1; // Memiliki Kendaraan (Motor/Mobil) } private function skorPekerjaan($val) { $val = strtolower($val); if (str_contains($val, 'tidak bekerja') || str_contains($val, 'lansia')) return 5; if (str_contains($val, 'tani') || str_contains($val, 'serabutan')) return 4; return 1; // Karyawan / Wiraswasta } private function skorUsia($val) { $val = (int) $val; if ($val >= 60) return 5; // Lansia if ($val >= 45) return 3; // 45 - 59 Tahun return 1; // < 45 Tahun } }