MIF_E31231226/app/Http/Controllers/RekomendasiController.php

206 lines
7.6 KiB
PHP

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\HasilAnalisis;
use Illuminate\Support\Facades\DB;
class RekomendasiController extends Controller
{
/**
* Rule-based: mapping kategori isu ke keyword terkait
*/
private array $issueRules = [
'Kebersihan' => [
'keywords' => ['kotor', 'sampah', 'bersih', 'jorok', 'kumuh', 'toilet', 'wc', 'kamar mandi', 'limbah', 'bau'],
'color' => 'red',
'icon' => '🧹',
'saran' => [
'actions' => ['Tambah tempat sampah', 'Jadwal pembersihan rutin', 'Petugas kebersihan tambahan'],
'dampak' => 'Kepuasan meningkat signifikan',
'tip' => 'Tambah tempat sampah & jadwal pembersihan rutin.',
],
],
'Aksesibilitas' => [
'keywords' => ['parkir', 'lahan parkir', 'tempat parkir', 'parkiran', 'motor', 'mobil'],
'color' => 'orange',
'icon' => '🚗',
'saran' => [
'actions' => ['Penataan area parkir', 'Tarif transparan', 'Petugas lebih ramah'],
'dampak' => 'Mengurangi keluhan',
'tip' => 'Sistem parkir lebih rapi dan transparan.',
],
],
'Harga / Tiket' => [
'keywords' => ['mahal', 'harga', 'tiket', 'bayar', 'biaya', 'tarif', 'murah', 'terjangkau'],
'color' => 'yellow',
'icon' => '🎟️',
'saran' => [
'actions' => ['Evaluasi harga tiket', 'Promo wisata', 'Diskon tertentu'],
'dampak' => 'Daya tarik meningkat',
'tip' => 'Penyesuaian harga tiket agar lebih terjangkau.',
],
],
'Fasilitas' => [
'keywords' => ['fasilitas', 'gazebo', 'warung', 'kantin', 'kamar ganti', 'musholla', 'wifi', 'bangku', 'kursi'],
'color' => 'green',
'icon' => '🏗️',
'saran' => [
'actions' => ['Perbaiki fasilitas rusak', 'Tambah fasilitas umum', 'Perawatan berkala'],
'dampak' => 'Kenyamanan pengunjung meningkat',
'tip' => 'Fasilitas lengkap & terawat meningkatkan kepuasan.',
],
],
];
public function index(Request $request)
{
// --- 1. Dropdown destinasi ---
$destinasiList = DB::table('hasil_analisis')
->select('wisata')
->distinct()
->pluck('wisata');
// --- 2. Query dengan filter destinasi ---
$queryAll = HasilAnalisis::query();
$queryFilter = HasilAnalisis::query();
if ($request->filled('destinasi')) {
$queryFilter->where('wisata', $request->destinasi);
}
// --- 3. Hitung total & sentimen ---
$totalUlasan = $queryFilter->count();
$totalNegatif = (clone $queryFilter)->where('sentimen', 'negatif')->count();
$totalPositif = (clone $queryFilter)->where('sentimen', 'positif')->count();
$totalNetral = (clone $queryFilter)->where('sentimen', 'netral')->count();
$persenNegatif = $totalUlasan > 0 ? round(($totalNegatif / $totalUlasan) * 100, 2) : 0;
$tingkatKepuasan = $totalUlasan > 0 ? round(($totalPositif / $totalUlasan) * 100) : 0;
// Label kepuasan
$labelKepuasan = match(true) {
$tingkatKepuasan >= 80 => 'Baik',
$tingkatKepuasan >= 60 => 'Sedang',
default => 'Kurang',
};
// --- 4. Ambil ulasan negatif & bersihkan teks ---
$ulasanNegatif = (clone $queryFilter)
->where('sentimen', 'negatif')
->pluck('ulasan_bersih') // gunakan kolom yang sudah dipreproses
->toArray();
$text = strtolower(implode(' ', $ulasanNegatif));
$words = array_filter(preg_split('/\s+/', $text));
// --- 5. Rule-based: hitung skor tiap kategori isu ---
$issueSkor = [];
$issueWords = []; // kata dominan per kategori
foreach ($this->issueRules as $kategori => $rule) {
$skor = 0;
$matchWords = [];
foreach ($rule['keywords'] as $kw) {
$count = substr_count($text, $kw);
if ($count > 0) {
$skor += $count;
$matchWords[$kw] = $count;
}
}
$issueSkor[$kategori] = $skor;
$issueWords[$kategori] = $matchWords;
}
// Urutkan berdasarkan skor tertinggi
arsort($issueSkor);
$totalSkorIsu = array_sum($issueSkor) ?: 1; // hindari division by zero
// --- 6. Isu utama (top 5) dengan persentase ---
$isuUtama = [];
foreach (array_slice($issueSkor, 0, 5, true) as $nama => $skor) {
$isuUtama[] = [
'nama' => $nama,
'skor' => $skor,
'persen' => round(($skor / $totalSkorIsu) * 100),
'color' => $this->issueRules[$nama]['color'],
'icon' => $this->issueRules[$nama]['icon'],
];
}
// --- 7. Isu dominan (ranking 1) ---
$isuDominan = $isuUtama[0]['nama'] ?? 'Belum ada isu';
$isuDominanPersen = $isuUtama[0]['persen'] ?? 0;
// --- 8. Kata kunci dominan (global, top 10, filter stopword) ---
$stopwords = [
'yang', 'dan', 'di', 'ke', 'dari', 'untuk', 'dengan',
'ini', 'itu', 'tidak', 'ada', 'juga', 'sangat', 'lebih',
'sudah', 'bisa', 'tapi', 'karena', 'pada', 'akan', 'atau',
'saya', 'kami', 'mereka', 'nya', 'ga', 'gak', 'udah', 'pas',
'kita', 'sih', 'deh', 'aja', 'ya', 'yg', 'tp', 'bgt',
];
$freq = array_count_values($words);
foreach ($stopwords as $sw) {
unset($freq[$sw]);
}
// filter kata pendek (< 3 karakter)
foreach (array_keys($freq) as $w) {
if (mb_strlen($w) < 3) unset($freq[$w]);
}
arsort($freq);
$kataDominan = array_slice($freq, 0, 10, true);
// --- 9. Prioritas rekomendasi (top 3 isu) ---
$prioritas = [];
$rank = 1;
foreach (array_slice($issueSkor, 0, 3, true) as $nama => $skor) {
$prioritas[] = [
'rank' => $rank++,
'nama' => $nama,
'icon' => $this->issueRules[$nama]['icon'],
'actions' => $this->issueRules[$nama]['saran']['actions'],
'dampak' => $this->issueRules[$nama]['saran']['dampak'],
'color' => $this->issueRules[$nama]['color'],
];
}
// --- 10. Saran perbaikan (top 3) ---
$saranPerbaikan = [];
foreach (array_slice($issueSkor, 0, 3, true) as $nama => $skor) {
$saranPerbaikan[] = [
'nama' => 'Perbaikan ' . $nama,
'tip' => $this->issueRules[$nama]['saran']['tip'],
'icon' => $this->issueRules[$nama]['icon'],
];
}
// --- 11. Filter destinasi yang sedang aktif ---
$destinasiAktif = $request->input('destinasi', 'Semua Destinasi');
return view('rekomendasi.index', compact(
'destinasiList',
'destinasiAktif',
'totalUlasan',
'totalNegatif',
'totalPositif',
'totalNetral',
'persenNegatif',
'tingkatKepuasan',
'labelKepuasan',
'isuUtama',
'isuDominan',
'isuDominanPersen',
'kataDominan',
'saranPerbaikan',
'prioritas',
));
}
}