orderBy('kriteria_id') // Urutkan berdasarkan ID kriteria utama ->orderBy('id') // Lalu urutkan berdasarkan ID sub kriteria ->get() ->groupBy(function ($item) { // Kelompokkan sub kriteria berdasarkan nama kriteria utama return $item->kriteria->nama_kriteria; }); // Mengambil semua kriteria kecuali "Kapasitas Kursi" // Kapasitas Kursi digunakan sebagai filter, bukan untuk perhitungan bobot $kriteria = Kriteria::where('nama_kriteria', '!=', 'Kapasitas Kursi')->get(); // Mengirim data ke view untuk ditampilkan di form return view('rekomendasi.index', compact('subKriteria', 'kriteria')); } public function getSubKriteriaFiltered(Request $request) { $kapasitasSubId = $request->input('kapasitas_sub_id'); // Ambil mobil yang cocok dengan kapasitas kursi $mobilFiltered = Mobil::where('sub_kriteria_id', $kapasitasSubId)->pluck('id'); // Ambil nilai alternatif berdasarkan mobil yang cocok $nilaiAlternatif = NilaiAlternatif::with('subKriteria.kriteria') ->whereIn('mobil_id', $mobilFiltered) ->get(); // Kelompokkan subkriteria yang valid berdasarkan kriteria_id $subKriteriaByKriteria = []; foreach ($nilaiAlternatif as $nilai) { $sub = $nilai->subKriteria; $kriteriaId = $sub->kriteria->id; $kriteriaName = $sub->kriteria->nama_kriteria; // Skip kapasitas kursi if ($kriteriaName == 'Kapasitas Kursi') continue; $subKriteriaByKriteria[$kriteriaName][$sub->id] = $sub->nama_subkriteria; } return response()->json($subKriteriaByKriteria); } /** * Memproses perhitungan rekomendasi berdasarkan input user * Menggunakan algoritma SAW (Simple Additive Weighting) */ public function index(Request $request) { // Mengambil input dari form: sub kriteria yang dipilih dan bobot $selectedSubKriteriaIds = $request->input('sub_kriteria', []); // Sub kriteria yang dipilih user $bobotUser = $request->input('bobot', []); // Bobot yang diinput user untuk setiap kriteria // Debug: Uncomment untuk melihat data input // dd($selectedSubKriteriaIds, $bobotUser); // VALIDASI BOBOT: Total bobot harus = 1 (100%) $total = array_sum($bobotUser); // Jumlahkan semua bobot if (abs($total - 1) > 0.01) { // Toleransi 0.01 untuk kesalahan floating point return back()->withErrors(['bobot' => 'Total bobot harus sama dengan 1.']); } // FILTER BERDASARKAN KAPASITAS KURSI // Mencari ID sub kriteria yang termasuk dalam kriteria "Kapasitas Kursi" $kapasitasKursiSubIds = SubKriteria::whereHas('kriteria', function ($query) { $query->where('nama_kriteria', 'Kapasitas Kursi'); })->pluck('id')->toArray(); // Mencari sub kriteria kapasitas kursi yang dipilih user $kapasitasYangDipilih = array_intersect($selectedSubKriteriaIds, $kapasitasKursiSubIds); // Filter mobil berdasarkan kapasitas kursi yang dipilih $mobilFiltered = Mobil::whereIn('sub_kriteria_id', $kapasitasYangDipilih)->pluck('id')->toArray(); // Debug: Uncomment untuk melihat mobil yang terfilter // dd($mobilFiltered); // Jika tidak ada mobil yang sesuai filter kapasitas if (empty($mobilFiltered)) { return view('rekomendasi.hasil', ['result' => [], 'message' => 'Tidak ditemukan mobil sesuai filter']); } // MENGAMBIL DATA NILAI ALTERNATIF // Ambil semua nilai alternatif untuk mobil yang sudah difilter $rows = NilaiAlternatif::with('subKriteria') ->whereIn('mobil_id', $mobilFiltered) // Hanya mobil yang sesuai filter kapasitas ->when(!empty($selectedSubKriteriaIds), function ($query) use ($selectedSubKriteriaIds) { return $query->whereIn('sub_kriteria_id', $selectedSubKriteriaIds); // Filter berdasarkan sub kriteria yang dipilih }) ->get(); // Debug: Uncomment untuk melihat data mentah // return response()->json($rows); // PENGOLAHAN DATA NILAI ALTERNATIF // Kelompokkan nilai berdasarkan mobil_id dan kriteria_id $nilaiSementara = []; foreach ($rows as $row) { $kriteriaId = $row->subKriteria->kriteria_id; // ID kriteria utama $nilaiSementara[$row->mobil_id][$kriteriaId][] = $row->nilai; // Kumpulkan nilai per mobil per kriteria } // Debug: Uncomment untuk melihat pengelompokan nilai // dd($nilaiSementara); // PERHITUNGAN RATA-RATA NILAI PER KRITERIA // Jika satu kriteria memiliki beberapa sub kriteria, hitung rata-ratanya $nilaiAlternatif = []; foreach ($nilaiSementara as $mobilId => $kriteriaArray) { foreach ($kriteriaArray as $kriteriaId => $nilaiList) { $sum = array_sum($nilaiList); // Jumlahkan semua nilai sub kriteria $jumlahSub = count($nilaiList); // Hitung jumlah sub kriteria $nilaiAlternatif[$mobilId][$kriteriaId] = $sum / $jumlahSub; // Hitung rata-rata } } // Debug: Uncomment untuk melihat nilai rata-rata // dd($nilaiAlternatif); // PERHITUNGAN SAW (Simple Additive Weighting) $hasil = $this->hitungSAW($nilaiAlternatif, $bobotUser); // Urutkan hasil dari yang terbesar (ranking tertinggi) arsort($hasil); // Ambil 3 mobil dengan skor tertinggi $top3 = array_slice($hasil, 0, 3, true); // PREPARE HASIL UNTUK DITAMPILKAN $result = []; foreach ($top3 as $mobilId => $score) { $mobil = Mobil::find($mobilId); // Ambil data lengkap mobil $result[] = [ 'mobil' => $mobil, // Data mobil 'score' => $score, // Skor SAW ]; } // Kirim hasil ke view return view('rekomendasi.hasil', compact('result')); } /** * IMPLEMENTASI ALGORITMA SAW (Simple Additive Weighting) * * Formula SAW: Si = Σ(wj × rij) * Dimana: * - Si = Skor alternatif ke-i * - wj = Bobot kriteria ke-j * - rij = Rating kinerja ternormalisasi alternatif ke-i pada kriteria ke-j * * Normalisasi: * - Benefit (semakin besar semakin baik): rij = xij / max(xij) * - Cost (semakin kecil semakin baik): rij = min(xij) / xij */ protected function hitungSAW(array $nilaiAlternatif, array $bobotUser): array { // PERSIAPAN DATA KRITERIA $kriteriaList = Kriteria::all(); // Ambil semua kriteria $bobot = []; // Array untuk menyimpan bobot kriteria $tipe = []; // Array untuk menyimpan tipe kriteria (benefit/cost) $skipKriteriaIds = []; // Array untuk kriteria yang tidak dihitung (Kapasitas Kursi) // Loop untuk setiap kriteria foreach ($kriteriaList as $k) { // Skip kriteria "Kapasitas Kursi" karena hanya untuk filter if ($k->nama_kriteria == 'Kapasitas Kursi') { $skipKriteriaIds[] = $k->id; continue; } // Ambil bobot dari input user, default 0 jika tidak ada $bobot[$k->id] = isset($bobotUser[$k->id]) ? floatval($bobotUser[$k->id]) : 0; $tipe[$k->id] = $k->tipe; // benefit atau cost } // MENCARI NILAI MAKSIMUM DAN MINIMUM PER KRITERIA // Digunakan untuk normalisasi nilai $maxPerKriteria = []; $minPerKriteria = []; // Loop untuk setiap mobil dan kriteria foreach ($nilaiAlternatif as $mobilId => $kriteriaNilai) { foreach ($kriteriaNilai as $kriteriaId => $nilai) { // Skip kriteria "Kapasitas Kursi" if (in_array($kriteriaId, $skipKriteriaIds)) continue; // Cari nilai maksimum per kriteria $maxPerKriteria[$kriteriaId] = max($maxPerKriteria[$kriteriaId] ?? $nilai, $nilai); // Cari nilai minimum per kriteria $minPerKriteria[$kriteriaId] = min($minPerKriteria[$kriteriaId] ?? $nilai, $nilai); } } // Debug: Uncomment untuk melihat nilai max dan min // dd($maxPerKriteria, $minPerKriteria); // PERHITUNGAN SKOR SAW $hasil = []; // Loop untuk setiap mobil (alternatif) foreach ($nilaiAlternatif as $mobilId => $kriteriaNilai) { $total = 0; // Total skor untuk mobil ini // Loop untuk setiap kriteria foreach ($kriteriaNilai as $kriteriaId => $nilai) { // Skip kriteria "Kapasitas Kursi" if (in_array($kriteriaId, $skipKriteriaIds)) continue; // Ambil nilai max dan min untuk kriteria ini $max = $maxPerKriteria[$kriteriaId] ?? 1; $min = $minPerKriteria[$kriteriaId] ?? 0; // NORMALISASI NILAI berdasarkan tipe kriteria if ($tipe[$kriteriaId] === 'benefit') { // Untuk kriteria benefit: semakin besar semakin baik // Formula: rij = xij / max(xij) $normalized = $nilai / $max; } else { // Untuk kriteria cost: semakin kecil semakin baik // Formula: rij = min(xij) / xij $normalized = $min / $nilai; } // PERHITUNGAN SKOR: normalized value × bobot kriteria // Formula SAW: Si = Σ(wj × rij) $total += $normalized * ($bobot[$kriteriaId] ?? 0); } // Simpan total skor untuk mobil ini $hasil[$mobilId] = $total; } // Return array skor SAW untuk semua mobil return $hasil; } }