Hasil Rekomendasi SMART
@php
// Implementasi algoritma SMART
$cafes = \App\Models\Cafe::with(['ratings.subcategory', 'ratings.category'])->get();
$weights = request('weight') ?? [];
$criterias = request('criteria') ?? [];
$lokasi = request('lokasi');
$selectedAreas = request('area') ?? [];
// Frontend sudah mengirim bobot yang dinormalisasi (total ~100%)
// Jadi kita gunakan langsung tanpa normalisasi ulang
$totalWeight = array_sum($weights);
// Jika tidak ada weight atau total 0, buat bobot merata
if ($totalWeight == 0 || empty($weights)) {
$categories = \App\Models\Category::all();
foreach ($categories as $category) {
$weights[$category->id] = 100 / $categories->count();
}
$totalWeight = 100;
}
// Debug: tampilkan bobot yang diterima dari frontend
$rawWeights = request('weight') ?? [];
$debugRawWeights = [];
foreach ($rawWeights as $catId => $weight) {
$category = \App\Models\Category::find($catId);
if ($category) {
$debugRawWeights[] = $category->name . ': ' . number_format($weight, 2) . '%';
}
}
// Debug: tampilkan bobot yang diterima
$debugWeights = [];
foreach ($weights as $catId => $weight) {
$category = \App\Models\Category::find($catId);
if ($category) {
$debugWeights[] = $category->name . ': ' . number_format($weight, 1) . '%';
}
}
$selectedCriteria = [];
if (!empty($criterias)) {
foreach ($criterias as $categoryId => $subcategoryId) {
if ($subcategoryId) {
$category = \App\Models\Category::find($categoryId);
$subcategory = \App\Models\Subcategory::find($subcategoryId);
if ($category && $subcategory) {
$selectedCriteria[] = $category->name . ': ' . $subcategory->name;
}
}
}
}
// Filter berdasarkan lokasi jika ada
if ($lokasi) {
$cafes = $cafes->filter(function($cafe) use ($lokasi) {
return stripos($cafe->lokasi, $lokasi) !== false;
});
}
// Filter berdasarkan area yang dipilih
if (!empty($selectedAreas)) {
$cafes = $cafes->filter(function($cafe) use ($selectedAreas) {
// Jika cafe tidak punya area, lewati
if (!$cafe->area) return false;
// Cek apakah cafe memiliki setidaknya satu area yang dipilih
foreach ($selectedAreas as $selectedArea) {
if (stripos($cafe->area, $selectedArea) !== false) {
return true;
}
}
return false;
});
}
// Hitung skor SMART untuk setiap cafe
$cafeScores = [];
$debugOutput = "";
// Metode alternatif yang lebih langsung - ambil data dari database
foreach ($cafes as $cafe) {
$score = 0;
$debugInfo = [];
// Query langsung ke database untuk mendapatkan data yang diperlukan
$ratings = \Illuminate\Support\Facades\DB::table('cafe_ratings')
->join('subcategories', 'cafe_ratings.subcategory_id', '=', 'subcategories.id')
->join('categories', 'cafe_ratings.category_id', '=', 'categories.id')
->where('cafe_ratings.cafe_id', $cafe->id)
->select(
'cafe_ratings.category_id',
'categories.name as category_name',
'cafe_ratings.subcategory_id',
'subcategories.name as subcategory_name',
'subcategories.value'
)
->get();
$debugOutput .= "
";
$debugOutput .= "Cafe: " . $cafe->nama . " ";
foreach ($ratings as $rating) {
// Jika ada bobot untuk kategori ini
if (isset($weights[$rating->category_id])) {
$weight = floatval($weights[$rating->category_id]);
// Frontend sudah mengirim persentase yang dinormalisasi (total = 100%)
// Jadi kita gunakan langsung, konversi dari persentase ke desimal
$normalizedWeight = $weight / 100;
// Hanya proses jika bobot > 0
if ($weight > 0) {
// Bonus nilai jika kriteria dipilih
if (isset($criterias[$rating->category_id]) &&
$criterias[$rating->category_id] == $rating->subcategory_id) {
$value = 5; // Nilai maksimal untuk yang dipilih
} else {
// Hitung jarak dengan nilai yang dipilih
$selectedValue = 0;
if (isset($criterias[$rating->category_id])) {
$selectedSubcategory = \App\Models\Subcategory::find($criterias[$rating->category_id]);
if ($selectedSubcategory) {
$selectedValue = $selectedSubcategory->value;
}
}
// Hitung nilai berdasarkan jarak dengan nilai yang dipilih
$originalValue = intval($rating->value);
$distance = abs($originalValue - $selectedValue);
// Nilai akan lebih tinggi jika lebih dekat dengan nilai yang dipilih
// Nilai maksimal 4 untuk yang paling dekat, minimal 1
$value = max(1, 5 - $distance);
}
// Hitung utilitas
$utilityValue = ($value - 1) / 4; // Normalisasi ke rentang 0-1 (jika nilai antara 1-5)
$ratingScore = $normalizedWeight * $utilityValue;
$score += $ratingScore;
$debugOutput .= " " . $rating->category_name . ": " .
"Weight=" . number_format($weight, 1) . "%, " .
"Value=" . $value . ", " .
"Score=" . number_format($ratingScore, 2) . " ";
// Simpan untuk debug
$debugInfo[$rating->category_name] = [
'bobot' => $weight,
'bobot_normal' => $normalizedWeight,
'nilai' => $value,
'nilai_asli' => $rating->value,
'subcategory' => $rating->subcategory_name,
'skor_kriteria' => $ratingScore
];
} else {
// Kategori dengan bobot 0, diabaikan dalam perhitungan
$debugOutput .= " " . $rating->category_name . ": " .
"Weight=0% (Diabaikan) ";
// Simpan untuk debug dengan skor 0
$debugInfo[$rating->category_name] = [
'bobot' => 0,
'bobot_normal' => 0,
'nilai' => 0,
'nilai_asli' => $rating->value,
'subcategory' => $rating->subcategory_name,
'skor_kriteria' => 0
];
}
}
}
$debugOutput .= "Total Score: " . number_format($score, 2) . "
";
$cafeScores[$cafe->id] = [
'score' => $score,
'cafe' => $cafe,
'debug' => $debugInfo
];
}
// Urutkan cafe berdasarkan skor tertinggi, dengan logika khusus untuk kriteria harga mahal
uasort($cafeScores, function($a, $b) use ($criterias) {
// Primary sorting: berdasarkan skor SMART (tertinggi dulu)
$scoreComparison = $b['score'] <=> $a['score'];
// Jika skor sama, gunakan secondary sorting berdasarkan harga
if ($scoreComparison === 0) {
$hargaCategoryId = null;
$isMahalSelected = false;
$isMurahSelected = false;
// Cari kategori harga dan cek subkategori apa yang dipilih
foreach ($criterias as $categoryId => $subcategoryId) {
if ($subcategoryId) {
$category = \App\Models\Category::find($categoryId);
if ($category && strtolower($category->name) === 'harga') {
$hargaCategoryId = $categoryId;
$subcategory = \App\Models\Subcategory::find($subcategoryId);
if ($subcategory) {
$nameLower = strtolower($subcategory->name);
if (stripos($nameLower, 'mahal') !== false) {
$isMahalSelected = true;
} elseif (stripos($nameLower, 'murah') !== false) {
$isMurahSelected = true;
}
}
break;
}
}
}
// Jika user memilih "Mahal"
if ($isMahalSelected) {
$hargaTermahalA = $a['cafe']->harga_termahal ?? 0;
$hargaTermahalB = $b['cafe']->harga_termahal ?? 0;
$hargaTermahalComparison = $hargaTermahalB <=> $hargaTermahalA;
if ($hargaTermahalComparison === 0) {
$hargaTermurahA = $a['cafe']->harga_termurah ?? 0;
$hargaTermurahB = $b['cafe']->harga_termurah ?? 0;
return $hargaTermurahB <=> $hargaTermurahA;
}
return $hargaTermahalComparison;
// Jika user memilih "Murah"
} elseif ($isMurahSelected) {
$rataA = (($a['cafe']->harga_termurah ?? 0) + ($a['cafe']->harga_termahal ?? 0)) / 2;
$rataB = (($b['cafe']->harga_termurah ?? 0) + ($b['cafe']->harga_termahal ?? 0)) / 2;
return $rataA <=> $rataB; // Ascending (murah ke mahal)
// Jika tidak memilih mahal/murah secara spesifik
} else {
$hargaTermurahA = $a['cafe']->harga_termurah ?? PHP_INT_MAX;
$hargaTermurahB = $b['cafe']->harga_termurah ?? PHP_INT_MAX;
$hargaTermurahComparison = $hargaTermurahA <=> $hargaTermurahB;
if ($hargaTermurahComparison === 0) {
$hargaTermahalA = $a['cafe']->harga_termahal ?? PHP_INT_MAX;
$hargaTermahalB = $b['cafe']->harga_termahal ?? PHP_INT_MAX;
return $hargaTermahalA <=> $hargaTermahalB;
}
return $hargaTermurahComparison;
}
}
return $scoreComparison;
});
// Membuat array cafe yang sudah diurutkan berdasarkan skor
$rankedCafes = collect();
foreach ($cafeScores as $cafeScore) {
$rankedCafes->push($cafeScore['cafe']);
}
// Ambil hanya 10 cafe teratas
$rankedCafes = $rankedCafes->take(10);
// Array untuk akses skor pada tampilan
$cafeScoresById = [];
foreach ($cafeScores as $cafeId => $scoreData) {
$cafeScoresById[$cafeId] = $scoreData;
}
@endphp
@if(count($selectedCriteria) > 0 || $lokasi || !empty($selectedAreas))
Kriteria Pencarian:
@foreach($selectedCriteria as $criteria)
{{ $criteria }}
@endforeach
@endif
@if($rankedCafes->isEmpty())
Tidak ada café yang sesuai dengan filter lokasi Anda.
Coba ubah filter lokasi atau gunakan pencarian tanpa filter.
@else
@foreach($rankedCafes as $index => $cafe)
Peringkat {{ $index + 1 }}
@if($cafe->gambar)
@else
@endif
{{ $cafe->nama }}
{{ $cafe->area }}
@if($cafe->harga_termurah || $cafe->harga_termahal)
Range Harga:
@if($cafe->harga_termurah && $cafe->harga_termahal)
Rp {{ number_format($cafe->harga_termurah, 0, ',', '.') }} - Rp {{ number_format($cafe->harga_termahal, 0, ',', '.') }}
@elseif($cafe->harga_termurah)
Mulai dari Rp {{ number_format($cafe->harga_termurah, 0, ',', '.') }}
@elseif($cafe->harga_termahal)
Hingga Rp {{ number_format($cafe->harga_termahal, 0, ',', '.') }}
@endif
@endif
@foreach($cafe->ratings as $rating)
{{ $rating->category->name }}:
@for ($i = 0; $i < $rating->subcategory->value; $i++)
★
@endfor
@for ($i = $rating->subcategory->value; $i < 5; $i++)
☆
@endfor
@endforeach
Detail Perhitungan SMART
@if(isset($cafeScoresById[$cafe->id]['debug']) && !empty($cafeScoresById[$cafe->id]['debug']))
Kriteria
Bobot
Subkategori
Nilai
Skor
@php $totalScore = 0; @endphp
@foreach($cafeScoresById[$cafe->id]['debug'] as $category => $info)
{{ $category }}
{{ number_format($info['bobot'], 0) }}%
{{ $info['subcategory'] ?? 'N/A' }}
{{ number_format($info['nilai'], 1) }}
{{ number_format($info['skor_kriteria'], 2) }}
@php $totalScore += $info['skor_kriteria']; @endphp
@endforeach
Total
100%
{{ number_format($totalScore, 2) }}
@else
Tidak ada detail perhitungan tersedia.
@endif
Skor: {{ number_format($cafeScoresById[$cafe->id]['score'], 2) }}
Lihat Detail
@endforeach
@endif
Mulai Pencarian Café
Atur prioritas Anda dan pilih kriteria yang diinginkan, lalu klik "Cari Café Rekomendasi" untuk menemukan café yang sesuai dengan preferensi Anda.
Mulai Pencarian