diff --git a/app/Http/Controllers/Landingpage/HomeController.php b/app/Http/Controllers/Landingpage/HomeController.php index 7a9cca1..1638b1d 100644 --- a/app/Http/Controllers/Landingpage/HomeController.php +++ b/app/Http/Controllers/Landingpage/HomeController.php @@ -15,6 +15,90 @@ class HomeController extends Controller { public function index() { - // + // Ambil semua sub kriteria dan relasinya dengan kriteria + $subKriteria = SubKriteria::with('kriteria')->get()->groupBy(function ($item) { + return $item->kriteria->nama_kriteria; + }); + + return view('landingpage.master', [ + 'subKriteria' => $subKriteria + ]); } + public function prosesRekomendasi(Request $request) +{ + $sub_kriterias = $request->input('sub_kriteria'); // [kriteria_id => sub_kriteria_id] + + if (!$sub_kriterias) { + return back()->with('error', 'Harap pilih semua preferensi terlebih dahulu.'); + } + + $selectedSubs = SubKriteria::whereIn('id', array_values($sub_kriterias))->get()->keyBy('id'); + $allPakaian = Pakaian::with('subKriterias')->get(); + + // Step 1: Filter berdasarkan preferensi user + $filteredPakaian = $allPakaian->filter(function ($pakaian) use ($sub_kriterias, $selectedSubs) { + foreach ($sub_kriterias as $kriteria_id => $sub_id) { + if ($kriteria_id == 2) { // Harga (range) + $harga = $pakaian->harga; + $sub = $selectedSubs[$sub_id]; + if ($harga < $sub->min_harga || $harga > $sub->max_harga) { + return false; + } + } elseif ($kriteria_id == 3) { // Jenis Pakaian (C3) WAJIB SAMA + $match = $pakaian->subKriterias->contains(function ($item) use ($kriteria_id, $sub_id) { + return $item->kriteria_id == $kriteria_id && $item->id == $sub_id; + }); + if (!$match) return false; + } else { + // Untuk kriteria lain, cukup memiliki salah satu sub_kriteria + $match = $pakaian->subKriterias->where('kriteria_id', $kriteria_id)->isNotEmpty(); + if (!$match) return false; + } + } + return true; + }); + + if ($filteredPakaian->isEmpty()) { + return back()->with('error', 'Tidak ada pakaian yang sesuai dengan preferensi Anda.'); + } + + // Step 2: Siapkan bobot & maksimum nilai + $kriterias = Kriteria::all()->keyBy('id'); + $maxPerKriteria = []; + foreach ($kriterias as $kriteria_id => $kriteria) { + $maxPerKriteria[$kriteria_id] = SubKriteria::where('kriteria_id', $kriteria_id)->max('nilai') ?: 1; + } + + // Step 3: Hitung skor SAW + $matrix = []; + + foreach ($filteredPakaian as $pakaian) { + $score = 0; + + foreach ($kriterias as $kriteria_id => $kriteria) { + $bobot = $kriteria->bobot; + $jenis = $kriteria->jenis; + + // Ambil sub_kriteria tertinggi untuk kriteria ini + $subs = $pakaian->subKriterias->where('kriteria_id', $kriteria_id); + $sub = $subs->sortByDesc('nilai')->first(); + + if ($sub) { + $nilai = $sub->nilai; + $max = $maxPerKriteria[$kriteria_id]; + $normal = $jenis == 'COST' ? ($nilai ? $max / $nilai : 0) : $nilai / $max; + $score += $normal * $bobot; + } + } + + $matrix[] = [ + 'pakaian' => $pakaian, + 'score' => round($score, 3), + ]; + } + + $rekomendasi = collect($matrix)->sortByDesc('score')->values(); + return view('landingpage.hasil', compact('rekomendasi')); +} + } diff --git a/app/Http/Controllers/PakaianController.php b/app/Http/Controllers/PakaianController.php index d81b67f..3073910 100644 --- a/app/Http/Controllers/PakaianController.php +++ b/app/Http/Controllers/PakaianController.php @@ -2,9 +2,12 @@ namespace App\Http\Controllers; -use App\Models\Kriteria; use App\Models\Pakaian; +use App\Models\Kriteria; +use App\Models\SubKriteria; use Illuminate\Http\Request; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Storage; class PakaianController extends Controller { @@ -31,11 +34,14 @@ class PakaianController extends Controller /** * Show the form for creating a new resource. */ + public function create() { - // + $kriterias = Kriteria::with('subKriteria')->get(); // load kriteria + sub_kriterias + return view('admin.pages.pakaian.create', compact('kriterias')); } + /** * Store a newly created resource in storage. */ @@ -45,23 +51,44 @@ class PakaianController extends Controller 'nama_pakaian' => 'required|string|max:255', 'harga' => 'required|numeric', 'img' => 'nullable|image|max:2048', + 'sub_kriterias' => 'array|nullable', ]); - $imgPath = null; - if ($request->hasFile('img')) { - $imgPath = $request->file('img')->store('uploads/pakaian', 'public'); + DB::beginTransaction(); + + try { + // Upload image jika ada + $path = null; + if ($request->hasFile('img')) { + $path = $request->file('img')->store('pakaian', 'public'); + } + + // Simpan pakaian + $pakaian = Pakaian::create([ + 'nama_pakaian' => $request->nama_pakaian, + 'harga' => $request->harga, + 'img' => $path, + ]); + + // Simpan relasi sub kriteria + if ($request->filled('sub_kriterias')) { + $pakaian->subKriterias()->sync($request->sub_kriterias); + } + + DB::commit(); + + return redirect()->route('admin.pakaian.index')->with('success', 'Pakaian berhasil ditambahkan.'); + } catch (\Exception $e) { + // Rollback semua jika ada error + DB::rollBack(); + + // Hapus file yang sudah di-upload jika perlu + if ($path && Storage::disk('public')->exists($path)) { + Storage::disk('public')->delete($path); + } + + return redirect()->back()->with('error', 'Gagal menambahkan pakaian: ' . $e->getMessage()); } - - $pakaian = Pakaian::create([ - 'nama_pakaian' => $request->nama_pakaian, - 'harga' => $request->harga, - 'img' => $imgPath ? 'storage/' . $imgPath : null, - ]); - - // Simpan relasi subkriteria (many-to-many) - $pakaian->subKriterias()->sync($request->subkriterias); - - return redirect()->route('admin.pakaian.index')->with('success', 'Data pakaian berhasil ditambahkan.'); } @@ -76,42 +103,58 @@ class PakaianController extends Controller /** * Show the form for editing the specified resource. */ - public function edit(string $id) + public function edit($id) { - // + $pakaian = Pakaian::with('subKriterias')->findOrFail($id); + $kriterias = Kriteria::with('subKriteria')->get(); + + return view('admin.pages.pakaian.edit', compact('pakaian', 'kriterias')); } - /** - * Update the specified resource in storage. - */ public function update(Request $request, $id) { $request->validate([ 'nama_pakaian' => 'required|string|max:255', - 'harga' => 'required|numeric|min:0', - 'gambar' => 'nullable|image|mimes:jpeg,png,jpg|max:2048', + 'harga' => 'required|numeric', + 'img' => 'nullable|image|max:2048', + 'sub_kriterias' => 'array|nullable', ]); - $pakaian = Pakaian::findOrFail($id); - $pakaian->nama_pakaian = $request->nama_pakaian; - $pakaian->harga = $request->harga; + DB::beginTransaction(); - // Jika upload gambar baru - if ($request->hasFile('gambar')) { - if ($pakaian->img && file_exists(public_path($pakaian->img))) { - unlink(public_path($pakaian->img)); + try { + $pakaian = Pakaian::findOrFail($id); + $path = $pakaian->img; + + // Update gambar jika ada file baru + if ($request->hasFile('img')) { + if ($path && Storage::disk('public')->exists($path)) { + Storage::disk('public')->delete($path); + } + $path = $request->file('img')->store('pakaian', 'public'); } - $file = $request->file('gambar'); - $path = 'uploads/pakaian/'; - $filename = time() . '_' . $file->getClientOriginalName(); - $file->move(public_path($path), $filename); - $pakaian->img = $path . $filename; + $pakaian->update([ + 'nama_pakaian' => $request->nama_pakaian, + 'harga' => $request->harga, + 'img' => $path, + ]); + + // Update relasi sub_kriterias + $pakaian->subKriterias()->sync($request->sub_kriterias ?? []); + + DB::commit(); + + return redirect()->route('admin.pakaian.index')->with('success', 'Pakaian berhasil diperbarui.'); + } catch (\Exception $e) { + DB::rollBack(); + + if (isset($path) && $request->hasFile('img') && Storage::disk('public')->exists($path)) { + Storage::disk('public')->delete($path); + } + + return redirect()->back()->with('error', 'Gagal memperbarui pakaian: ' . $e->getMessage()); } - - $pakaian->save(); - - return redirect()->back()->with('success', 'Data pakaian berhasil diperbarui!'); } diff --git a/database/migrations/2025_05_24_133426_create_pakaian_sub_kriterias_table.php b/database/migrations/2025_05_24_133426_create_pakaian_sub_kriterias_table.php index 1bb02cd..04292af 100644 --- a/database/migrations/2025_05_24_133426_create_pakaian_sub_kriterias_table.php +++ b/database/migrations/2025_05_24_133426_create_pakaian_sub_kriterias_table.php @@ -11,14 +11,13 @@ return new class extends Migration */ public function up(): void { - Schema::create('pakaian_sub_kriterias', function (Blueprint $table) { - $table->id(); - $table->foreignId('pakaian_id')->constrained()->onDelete('cascade'); - $table->foreignId('sub_kriteria_id')->constrained()->onDelete('cascade'); - $table->timestamps(); - - $table->unique(['pakaian_id', 'sub_kriteria_id']); - }); + Schema::create('pakaian_sub_kriterias', function (Blueprint $table) { + $table->id(); + $table->foreignId('pakaian_id')->constrained()->onDelete('cascade'); + $table->foreignId('sub_kriteria_id')->constrained()->onDelete('cascade'); + $table->timestamps(); + $table->unique(['pakaian_id', 'sub_kriteria_id']); + }); } /** diff --git a/resources/views/admin/layouts/base.blade.php b/resources/views/admin/layouts/base.blade.php index 28c6467..38e9c27 100644 --- a/resources/views/admin/layouts/base.blade.php +++ b/resources/views/admin/layouts/base.blade.php @@ -25,9 +25,10 @@ @@ -92,33 +92,35 @@
Harga: Rp + {{ number_format($item['pakaian']->harga, 0, ',', '.') }}
+Skor Kecocokan: {{ number_format($item['score'], 3) }}
+