MIF_E31230680/app/Http/Controllers/RankingController.php

205 lines
7.1 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\Kriteria;
use App\Models\Warga;
class RankingController extends Controller
{
/**
* Menampilkan Halaman Ranking Utama
*/
public function index()
{
$data = $this->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
}
}