355 lines
14 KiB
PHP
355 lines
14 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use Illuminate\Http\Request;
|
|
use App\Models\Makanan;
|
|
use App\Models\Komponen;
|
|
use App\Models\WaktuMakan;
|
|
use App\Models\MakananKomponenWaktu;
|
|
use App\Models\PerbandinganAlternatif;
|
|
use App\Models\SkorMakanan;
|
|
use App\Models\Kriteria;
|
|
use App\Models\BobotKriteria;
|
|
use App\Models\ConsistencyRatioAlternatif;
|
|
use App\Models\SimulasiAhp;
|
|
use App\Models\SimulasiAhpPairwise;
|
|
use App\Models\SimulasiAhpNormalisasi;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Session;
|
|
use Illuminate\Support\Facades\Auth;
|
|
|
|
class SimulasiController extends Controller
|
|
{
|
|
public function index(Request $request)
|
|
{
|
|
$waktuMakans = WaktuMakan::all();
|
|
$komponens = Komponen::all();
|
|
$kriterias = Kriteria::all();
|
|
$makanans = null;
|
|
|
|
$waktuMakanId = $request->waktu_makan_id;
|
|
$komponenId = $request->komponen_id;
|
|
|
|
if ($waktuMakanId && $komponenId) {
|
|
$query = MakananKomponenWaktu::with('makanan')
|
|
->where('waktu_makan_id', $waktuMakanId);
|
|
$makananKomponenWaktu = $query->where('status', true)->get();
|
|
if ($makananKomponenWaktu->isEmpty()) {
|
|
$makananKomponenWaktu = MakananKomponenWaktu::with('makanan')
|
|
->where('waktu_makan_id', $waktuMakanId)
|
|
->get();
|
|
}
|
|
$filtered = $makananKomponenWaktu->where('komponen_id', $komponenId);
|
|
$makanans = $filtered->map(function($item) {
|
|
$makanan = $item->makanan;
|
|
$makanan->komponen_id = $item->komponen_id;
|
|
return $makanan;
|
|
})->values();
|
|
}
|
|
|
|
return view('user.simulasi.index', compact('waktuMakans', 'komponens', 'kriterias', 'makanans'));
|
|
}
|
|
|
|
public function getMakananByKomponen($komponen, $waktuMakan)
|
|
{
|
|
// Ambil semua data makanan_komponen_waktu untuk waktu makan tertentu
|
|
$query = MakananKomponenWaktu::with('makanan')
|
|
->where('waktu_makan_id', $waktuMakan);
|
|
|
|
// Coba filter status true dulu
|
|
$makananKomponenWaktu = $query->where('status', true)->get();
|
|
|
|
// Jika kosong, ambil semua tanpa filter status
|
|
if ($makananKomponenWaktu->isEmpty()) {
|
|
$makananKomponenWaktu = MakananKomponenWaktu::with('makanan')
|
|
->where('waktu_makan_id', $waktuMakan)
|
|
->get();
|
|
}
|
|
|
|
// Filter berdasarkan komponen
|
|
$filtered = $makananKomponenWaktu->where('komponen_id', $komponen);
|
|
|
|
// Map ke array makanan, tambahkan komponen_id
|
|
$makanans = $filtered->map(function($item) {
|
|
$makanan = $item->makanan;
|
|
$makanan->komponen_id = $item->komponen_id;
|
|
return $makanan;
|
|
})->values();
|
|
|
|
return response()->json($makanans);
|
|
}
|
|
|
|
public function prosesSimulasi(Request $request)
|
|
{
|
|
try {
|
|
DB::beginTransaction();
|
|
|
|
// Validasi minimal 4 makanan
|
|
$selectedMakanans = collect($request->makanan_ids)->filter()->values();
|
|
if ($selectedMakanans->count() < 4) {
|
|
return redirect()->back()->with('error', 'Pilih minimal 4 makanan untuk simulasi');
|
|
}
|
|
|
|
$waktuMakanId = $request->waktu_makan_id;
|
|
$komponenId = $request->komponen_id;
|
|
$kriterias = Kriteria::all();
|
|
|
|
// Ambil bobot kriteria berdasarkan waktu makan
|
|
$bobotKriteria = [];
|
|
foreach ($kriterias as $kriteria) {
|
|
$bobot = BobotKriteria::where('kriteria_id', $kriteria->id)
|
|
->where('waktu_makan_id', $waktuMakanId)
|
|
->first();
|
|
$bobotKriteria[$kriteria->id] = $bobot ? $bobot->bobot : 0;
|
|
}
|
|
|
|
// Normalisasi bobot kriteria
|
|
$totalBobot = array_sum($bobotKriteria);
|
|
if ($totalBobot > 0) {
|
|
foreach ($bobotKriteria as $key => $value) {
|
|
$bobotKriteria[$key] = $value / $totalBobot;
|
|
}
|
|
}
|
|
|
|
// Buat record simulasi baru
|
|
$simulasi = SimulasiAhp::create([
|
|
'user_id' => Auth::id(),
|
|
'judul' => 'Simulasi ' . WaktuMakan::find($waktuMakanId)->nama . ' - ' . Komponen::find($komponenId)->nama,
|
|
'pilihan_makanan_ids' => $selectedMakanans->toArray(),
|
|
'hasil_rangking' => []
|
|
]);
|
|
|
|
// Proses perbandingan berpasangan untuk setiap kriteria
|
|
foreach ($kriterias as $kriteria) {
|
|
$matriksPerbandingan = [];
|
|
$jumlahKolom = [];
|
|
|
|
// Inisialisasi matriks
|
|
foreach ($selectedMakanans as $makananId) {
|
|
$jumlahKolom[$makananId] = 0;
|
|
foreach ($selectedMakanans as $makananId2) {
|
|
$matriksPerbandingan[$makananId][$makananId2] = 1;
|
|
}
|
|
}
|
|
|
|
// Hitung nilai perbandingan berdasarkan kriteria
|
|
foreach ($selectedMakanans as $makananId1) {
|
|
$makanan1 = Makanan::find($makananId1);
|
|
foreach ($selectedMakanans as $makananId2) {
|
|
if ($makananId1 != $makananId2) {
|
|
$makanan2 = Makanan::find($makananId2);
|
|
$nilai1 = $this->getNilaiKriteria($makanan1, $kriteria);
|
|
$nilai2 = $this->getNilaiKriteria($makanan2, $kriteria);
|
|
|
|
if ($nilai1 > 0 && $nilai2 > 0) {
|
|
// Terapkan nilai invers untuk kriteria cost (lemak dan natrium)
|
|
if (in_array(strtolower($kriteria->nama), ['lemak', 'natrium'])) {
|
|
$ratio = $nilai2 / $nilai1; // Invers untuk cost
|
|
} else {
|
|
$ratio = $nilai1 / $nilai2;
|
|
}
|
|
|
|
if ($ratio > 9) $ratio = 9;
|
|
if ($ratio < 1/9) $ratio = 1/9;
|
|
|
|
$matriksPerbandingan[$makananId1][$makananId2] = $ratio;
|
|
$matriksPerbandingan[$makananId2][$makananId1] = 1 / $ratio;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Simpan matriks perbandingan berpasangan
|
|
foreach ($selectedMakanans as $makananId1) {
|
|
foreach ($selectedMakanans as $makananId2) {
|
|
if ($makananId1 != $makananId2) {
|
|
SimulasiAhpPairwise::create([
|
|
'simulasi_ahp_id' => $simulasi->id,
|
|
'makanan_1_id' => $makananId1,
|
|
'makanan_2_id' => $makananId2,
|
|
'nilai' => $matriksPerbandingan[$makananId1][$makananId2]
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Hitung jumlah kolom
|
|
foreach ($selectedMakanans as $makananId2) {
|
|
foreach ($selectedMakanans as $makananId1) {
|
|
$jumlahKolom[$makananId2] += $matriksPerbandingan[$makananId1][$makananId2];
|
|
}
|
|
}
|
|
|
|
// Normalisasi matriks dan hitung priority vector
|
|
$matriksNormal = [];
|
|
$priorityVector = [];
|
|
foreach ($selectedMakanans as $makananId1) {
|
|
$jumlahBaris = 0;
|
|
foreach ($selectedMakanans as $makananId2) {
|
|
if ($jumlahKolom[$makananId2] != 0) {
|
|
$nilai = $matriksPerbandingan[$makananId1][$makananId2] / $jumlahKolom[$makananId2];
|
|
$matriksNormal[$makananId1][$makananId2] = $nilai;
|
|
$jumlahBaris += $nilai;
|
|
}
|
|
}
|
|
$priorityVector[$makananId1] = $jumlahBaris / count($selectedMakanans);
|
|
}
|
|
|
|
// Simpan hasil normalisasi
|
|
foreach ($selectedMakanans as $makananId) {
|
|
SimulasiAhpNormalisasi::create([
|
|
'simulasi_ahp_id' => $simulasi->id,
|
|
'makanan_id' => $makananId,
|
|
'nilai_normalisasi' => $priorityVector[$makananId],
|
|
'bobot_akhir' => $priorityVector[$makananId] * $bobotKriteria[$kriteria->id]
|
|
]);
|
|
}
|
|
|
|
// Hitung Consistency Ratio
|
|
$n = count($selectedMakanans);
|
|
if ($n > 1) {
|
|
$lambdaMax = 0;
|
|
foreach ($selectedMakanans as $makananId1) {
|
|
$sum = 0;
|
|
foreach ($selectedMakanans as $makananId2) {
|
|
$sum += $matriksPerbandingan[$makananId1][$makananId2] * $priorityVector[$makananId2];
|
|
}
|
|
$lambdaMax += $sum / $priorityVector[$makananId1];
|
|
}
|
|
$lambdaMax /= $n;
|
|
|
|
$CI = ($lambdaMax - $n) / ($n - 1);
|
|
$RI = [
|
|
1 => 0.00, 2 => 0.00, 3 => 0.58, 4 => 0.90, 5 => 1.12,
|
|
6 => 1.24, 7 => 1.32, 8 => 1.41, 9 => 1.45, 10 => 1.49
|
|
];
|
|
$CR = $CI / ($RI[$n] ?? end($RI));
|
|
|
|
// Simpan CR ke database
|
|
ConsistencyRatioAlternatif::updateOrCreate(
|
|
[
|
|
'kriteria_id' => $kriteria->id,
|
|
'waktu_makan_id' => $waktuMakanId,
|
|
'komponen_id' => $komponenId
|
|
],
|
|
[
|
|
'ci' => $CI,
|
|
'cr' => $CR,
|
|
'is_consistent' => $CR <= 0.1
|
|
]
|
|
);
|
|
}
|
|
|
|
// Simpan skor ke database
|
|
foreach ($selectedMakanans as $makananId) {
|
|
SkorMakanan::updateOrCreate(
|
|
[
|
|
'makanan_id' => $makananId,
|
|
'kriteria_id' => $kriteria->id,
|
|
'waktu_makan_id' => $waktuMakanId,
|
|
'komponen_id' => $komponenId
|
|
],
|
|
['nilai' => $priorityVector[$makananId]]
|
|
);
|
|
}
|
|
}
|
|
|
|
// Hitung skor akhir
|
|
$hasilRekomendasi = [];
|
|
foreach ($selectedMakanans as $makananId) {
|
|
$skorAkhir = 0;
|
|
foreach ($kriterias as $kriteria) {
|
|
$skor = SkorMakanan::where([
|
|
'makanan_id' => $makananId,
|
|
'kriteria_id' => $kriteria->id,
|
|
'waktu_makan_id' => $waktuMakanId,
|
|
'komponen_id' => $komponenId
|
|
])->first();
|
|
|
|
if ($skor) {
|
|
$skorAkhir += $skor->nilai * $bobotKriteria[$kriteria->id];
|
|
}
|
|
}
|
|
$hasilRekomendasi[$makananId] = $skorAkhir;
|
|
}
|
|
|
|
// Urutkan hasil berdasarkan skor
|
|
arsort($hasilRekomendasi);
|
|
|
|
// Update hasil ranking di tabel simulasi
|
|
$simulasi->update(['hasil_rangking' => $hasilRekomendasi]);
|
|
|
|
// Simpan hasil ke session
|
|
Session::put('hasil_simulasi', [
|
|
'simulasi_id' => $simulasi->id,
|
|
'waktu_makan_id' => $waktuMakanId,
|
|
'komponen_id' => $komponenId,
|
|
'hasil' => $hasilRekomendasi,
|
|
'makanan_ids' => $selectedMakanans->toArray(),
|
|
'bobot_kriteria' => $bobotKriteria
|
|
]);
|
|
|
|
DB::commit();
|
|
return redirect()->route('user.simulasi.hasil');
|
|
|
|
} catch (\Exception $e) {
|
|
DB::rollback();
|
|
return redirect()->back()->with('error', 'Terjadi kesalahan: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
public function hasil()
|
|
{
|
|
$hasilSimulasi = Session::get('hasil_simulasi');
|
|
if (!$hasilSimulasi) {
|
|
return redirect()->route('user.simulasi.index');
|
|
}
|
|
|
|
$waktuMakan = WaktuMakan::find($hasilSimulasi['waktu_makan_id']);
|
|
$komponen = Komponen::find($hasilSimulasi['komponen_id']);
|
|
$makanans = Makanan::whereIn('id', $hasilSimulasi['makanan_ids'])->get();
|
|
$kriterias = Kriteria::all();
|
|
$crCriteria = \App\Models\ConsistencyRatioCriteria::where('waktu_makan_id', $waktuMakan->id ?? $hasilSimulasi['waktu_makan_id'])->orderByDesc('created_at')->first();
|
|
|
|
return view('user.simulasi.hasil', compact('hasilSimulasi', 'waktuMakan', 'komponen', 'makanans', 'kriterias', 'crCriteria'));
|
|
}
|
|
|
|
public function history()
|
|
{
|
|
$simulasis = SimulasiAhp::where('user_id', Auth::id())
|
|
->orderBy('created_at', 'desc')
|
|
->paginate(10);
|
|
|
|
return view('user.simulasi.history', compact('simulasis'));
|
|
}
|
|
|
|
public function detailHistory($id)
|
|
{
|
|
$simulasi = SimulasiAhp::where('user_id', Auth::id())
|
|
->where('id', $id)
|
|
->firstOrFail();
|
|
|
|
$makanans = Makanan::whereIn('id', $simulasi->pilihan_makanan_ids)->get();
|
|
$pairwise = SimulasiAhpPairwise::where('simulasi_ahp_id', $id)->get();
|
|
$normalisasi = SimulasiAhpNormalisasi::where('simulasi_ahp_id', $id)->get();
|
|
|
|
return view('user.simulasi.detail-history', compact('simulasi', 'makanans', 'pairwise', 'normalisasi'));
|
|
}
|
|
|
|
private function getNilaiKriteria($makanan, $kriteria)
|
|
{
|
|
switch (strtolower($kriteria->nama)) {
|
|
case 'energi':
|
|
return $makanan->energi;
|
|
case 'lemak':
|
|
return $makanan->lemak;
|
|
case 'karbohidrat':
|
|
return $makanan->karbohidrat;
|
|
case 'natrium':
|
|
return $makanan->natrium;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
}
|