diff --git a/app/Http/Controllers/CuranmorController.php b/app/Http/Controllers/CuranmorController.php index c53b0d9..05dc474 100644 --- a/app/Http/Controllers/CuranmorController.php +++ b/app/Http/Controllers/CuranmorController.php @@ -5,10 +5,11 @@ use App\Models\Klaster; use App\Models\Curanmor; use App\Models\Kecamatan; -use App\Models\Detail_Curanmor; use Illuminate\Http\Request; +use App\Models\Detail_Curanmor; use App\Services\KMeansService; use Illuminate\Validation\Rule; +use Illuminate\Support\Facades\DB; class CuranmorController extends Controller { @@ -35,13 +36,14 @@ public function create() */ public function store(Request $request) { + $request->validate([ + 'kecamatan_id' => 'required|exists:kecamatans,id', + 'jumlah_curanmor' => 'required|numeric', + ]); try{ - $request->validate([ - 'kecamatan_id' => 'required|exists:kecamatans,id', - 'jumlah_curanmor' => 'required|numeric', - ]); - + DB::beginTransaction(); + $kecamatan_id = $request->kecamatan_id; $tambahan_curanmor = $request->jumlah_curanmor; @@ -75,18 +77,11 @@ public function store(Request $request) $serviceSSECuranmor = new KMeansService(); $serviceSSECuranmor->SSEElbowCuranmor(); - // =====CODE TAMBAH SEBELUMNYA========= - // $validateData = $request->validate([ - // 'kecamatan_id' =>'required|max:255|exists:kecamatans,id|unique:curanmors,kecamatan_id', - // 'jumlah_curanmor' =>'required', - // 'klaster_id' =>'required|max:255|exists:klasters,id', - - // ]); - - // Curanmor::create($validateData); + DB::commit(); return redirect('/dashboard/curanmor')->with('succes', 'Berhasil Menambahkan Data Curanmor Baru'); }catch (\Exception $e){ - return redirect('/dashboard/curanmor')->with('error', 'Gagal Menambahkan Data Curanmor Baru'); + DB::rollBack(); + return redirect('/dashboard/curanmor')->with('error', 'Gagal Menambahkan Data Curanmor Baru '. $e->getMessage()); } } @@ -103,18 +98,6 @@ public function show(Curanmor $curanmor) */ public function edit($curanmor) { - try { - - $edit = Curanmor::find($curanmor); - - return view('admin.dashboardEditCuranmor', [ - 'curanmor' => $edit, - 'kecamatans' => Kecamatan::all(), - 'klasters' => Klaster::all(), - ]); - } catch (\Exception $e) { - abort(404); - } } /** @@ -122,39 +105,7 @@ public function edit($curanmor) */ public function update(Request $request, Curanmor $curanmor) { - try { - - // Validasi input - $request->validate([ - 'kecamatan_id' => [ - 'required', - 'exists:kecamatans,id', - Rule::unique('curanmors')->ignore($curanmor->id), - ], - 'klaster_id' => 'required|exists:klasters,id', - 'jumlah_curanmor' => 'required|numeric|min:0', - ]); - - // Update data - $curanmor->update([ - 'kecamatan_id' => $request->kecamatan_id, - 'klaster_id' => $request->klaster_id, - 'jumlah_curanmor' => $request->jumlah_curanmor, - ]); - - $service = new KMeansService(); - $hasil = $service->hitungKMeansCuranmor(); - - // simpan hasil ke file json - file_put_contents(storage_path('app/public/hasil_kmeans_curanmor.json'), json_encode($hasil)); - - $serviceSSECuranmor = new KMeansService(); - $serviceSSECuranmor->SSEElbowCuranmor(); - - return redirect('/dashboard/curanmor')->with('succes', 'Data Kecamatan Berhasil Diubah'); - } catch (\Exception $e) { - return redirect('/dashboard/curanmor')->with('error', 'Data Kecamatan Gagal Diubah: ' . $e->getMessage()); - } + } /** @@ -162,25 +113,6 @@ public function update(Request $request, Curanmor $curanmor) */ public function destroy($curanmor) { - try { - // Cari data berdasarkan ID - $hapus = Curanmor::find($curanmor); - - // Pastikan data ditemukan sebelum menghapus - if (!$hapus) { - return redirect('/dashboard/curanmor')->with('error', 'Data tidak ditemukan.'); - } - - // Hapus data - $hapus->delete(); - - $serviceSSECuranmor = new KMeansService(); - $serviceSSECuranmor->SSEElbowCuranmor(); - - return redirect('/dashboard/curanmor')->with('succes', 'Data Curanmor Berhasil Dihapus'); - } catch (\Exception $e) { - return redirect('/dashboard/curanmor')->with('error', 'Terjadi kesalahan: ' . $e->getMessage()); - } } } diff --git a/app/Http/Controllers/CurasController.php b/app/Http/Controllers/CurasController.php index 91c148c..f02e547 100644 --- a/app/Http/Controllers/CurasController.php +++ b/app/Http/Controllers/CurasController.php @@ -9,6 +9,7 @@ use Illuminate\Http\Request; use App\Services\KMeansService; use Illuminate\Validation\Rule; +use Illuminate\Support\Facades\DB; class CurasController extends Controller { @@ -36,13 +37,14 @@ public function create() */ public function store(Request $request) { + $request->validate([ + 'kecamatan_id' => 'required|exists:kecamatans,id', + 'jumlah_curas' => 'required|numeric', + ]); try{ - $request->validate([ - 'kecamatan_id' => 'required|exists:kecamatans,id', - 'jumlah_curas' => 'required|numeric', - ]); - + DB::beginTransaction(); + $kecamatan_id = $request->kecamatan_id; $tambahan_curas = $request->jumlah_curas; @@ -74,16 +76,19 @@ public function store(Request $request) $service = new KMeansService(); $hasil = $service->hitungKMeansCuras(); - - // simpan hasil ke file json + file_put_contents(storage_path('app/public/hasil_kmeans_curas.json'), json_encode($hasil)); $serviceSSECuras = new KMeansService(); $serviceSSECuras->SSEElbowCuras(); - + + DB::commit(); + return redirect('/dashboard/curas')->with('succes', 'Data curas berhasil ditambahkan.'); }catch (\Exception $e){ - return redirect('/dashboard/curas')->with('error', 'Gagal Menambahkan Data Curas Baru'); + + DB::rollBack(); + return redirect('/dashboard/curas')->with('error', 'Gagal Menambahkan Data Curas Baru'. $e->getMessage()); } } @@ -100,18 +105,6 @@ public function show(Curas $curas) */ public function edit($curas) { - try { - - $edit = Curas::find($curas); - - return view('admin.dashboardEditCuras', [ - 'curas' => $edit, - 'kecamatans' => Kecamatan::all(), - 'klasters' => Klaster::all(), - ]); - } catch (\Exception $e) { - abort(404); - } } /** @@ -119,71 +112,13 @@ public function edit($curas) */ public function update(Request $request, $id) { - try { - // Cari data berdasarkan ID yang dikirim - $curas = Curas::findOrFail($id); - - // Debugging untuk memastikan data ditemukan - // dd($curas->toArray()); // Jika berhasil, ini akan menampilkan data curas - - // Validasi input - $request->validate([ - 'kecamatan_id' => [ - 'required', - 'exists:kecamatans,id', - Rule::unique('curas')->ignore($curas->id), - ], - 'klaster_id' => 'required|exists:klasters,id', - 'jumlah_curas' => 'required|integer|min:0', - ]); - - // Update data - $curas->update([ - 'kecamatan_id' => $request->kecamatan_id, - 'klaster_id' => $request->klaster_id, - 'jumlah_curas' => $request->jumlah_curas, - ]); - - $service = new KMeansService(); - $hasil = $service->hitungKMeansCuras(); - - // simpan hasil ke file json - file_put_contents(storage_path('app/public/hasil_kmeans_curas.json'), json_encode($hasil)); - - $serviceSSECuras = new KMeansService(); - $serviceSSECuras->SSEElbowCuras(); - - return redirect('/dashboard/curas')->with('succes', 'Data Kecamatan Berhasil Diubah'); - } catch (\Exception $e) { - return redirect('/dashboard/curas')->with('error', 'Data Kecamatan Gagal Diubah: ' . $e->getMessage()); - } } - - - - /** * Remove the specified resource from storage. */ public function destroy($curas) { - try { - // Cari data berdasarkan ID - $hapus = Curas::find($curas); - - // Pastikan data ditemukan sebelum menghapus - if (!$hapus) { - return redirect('/dashboard/curas')->with('error', 'Data tidak ditemukan.'); - } - - // Hapus data - $hapus->delete(); - - return redirect('/dashboard/curas')->with('succes', 'Data Curas Berhasil Dihapus'); - } catch (\Exception $e) { - return redirect('/dashboard/curas')->with('error', 'Terjadi kesalahan: ' . $e->getMessage()); - } } diff --git a/app/Http/Controllers/DetailCuranmorController.php b/app/Http/Controllers/DetailCuranmorController.php index 598e83c..c971617 100644 --- a/app/Http/Controllers/DetailCuranmorController.php +++ b/app/Http/Controllers/DetailCuranmorController.php @@ -6,6 +6,7 @@ use Illuminate\Http\Request; use App\Models\Detail_Curanmor; use App\Services\KMeansService; +use Illuminate\Support\Facades\DB; class DetailCuranmorController extends Controller { @@ -65,6 +66,8 @@ public function update(Request $request, Detail_Curanmor $detail_Curanmor) public function destroy($id) { try { + + DB::beginTransaction(); $detail = Detail_Curanmor::findOrFail($id); $curanmor = Curanmor::findOrFail($detail->curanmor_id); @@ -89,8 +92,12 @@ public function destroy($id) $serviceSSECuranmor = new KMeansService(); $serviceSSECuranmor->SSEElbowCuranmor(); + DB::commit(); + return redirect('/dashboard/detail-curanmor')->with('succes', 'Data berhasil dihapus dan curanmor diperbarui.'); } catch (\Exception $e) { + + DB::rollback(); return redirect('/dashboard/detail-curanmor')->with('error', 'Terjadi kesalahan Ketika Menghapus Data : ' . $e->getMessage()); } } diff --git a/app/Http/Controllers/DetailCurasController.php b/app/Http/Controllers/DetailCurasController.php index 0928731..0b0418b 100644 --- a/app/Http/Controllers/DetailCurasController.php +++ b/app/Http/Controllers/DetailCurasController.php @@ -6,6 +6,7 @@ use App\Models\Detail_Curas; use Illuminate\Http\Request; use App\Services\KMeansService; +use Illuminate\Support\Facades\DB; class DetailCurasController extends Controller { @@ -65,6 +66,9 @@ public function update(Request $request, Detail_Curas $detail_Curas) public function destroy($id) { try { + + DB::beginTransaction(); + $detail = Detail_Curas::findOrFail($id); // Ambil curas terkait @@ -92,8 +96,12 @@ public function destroy($id) $serviceSSECuras = new KMeansService(); $serviceSSECuras->SSEElbowCuras(); + DB::commit(); + return redirect('/dashboard/detail-curas')->with('succes', 'Data berhasil dihapus dan curas diperbarui.'); } catch (\Exception $e) { + + DB::rollBack(); return redirect('/dashboard/detail-curas')->with('error', 'Terjadi kesalahan Ketika Menghapus Data : ' . $e->getMessage()); } diff --git a/app/Http/Controllers/KlasterController.php b/app/Http/Controllers/KlasterController.php index 0ccd038..ee23c86 100644 --- a/app/Http/Controllers/KlasterController.php +++ b/app/Http/Controllers/KlasterController.php @@ -4,6 +4,7 @@ use App\Models\Klaster; use Illuminate\Http\Request; +use App\Services\KMeansService; class KlasterController extends Controller { @@ -34,12 +35,25 @@ public function store(Request $request) 'nama_klaster' =>'required|max:255|unique:klasters,nama_klaster', 'warna' =>'required|max:255', ]); + + + $serviceKMeans = new KMeansService(); + $serviceKMeans->SSEElbowCuranmor(); + $serviceKMeans->SSEElbowCuras(); + + $serviceKMeansCuras = new KMeansService(); + $hasilKMeansCuras = $serviceKMeansCuras->hitungKMeansCuras(); + file_put_contents(storage_path('app/public/hasil_kmeans_curas.json'), json_encode($hasilKMeansCuras)); + + $serviceKmeansCuranmor = new KMeansService(); + $hasilKMeansCuranmor = $serviceKmeansCuranmor->hitungKMeansCuranmor(); + file_put_contents(storage_path('app/public/hasil_kmeans_curanmor.json'), json_encode($hasilKMeansCuranmor)); Klaster::create($validateData); return redirect('/dashboard/klaster')->with('succes', 'Berhasil Menambahkan Klaster Baru'); }catch (\Exception $e){ - return redirect('/dashboard/klaster')->with('error', 'Gagal Menambahkan Klaster Baru'); + return redirect('/dashboard/klaster')->with('error', 'Gagal Menambahkan Klaster Baru ' .$e->getMessage()); } } diff --git a/app/Services/KMeansService.php b/app/Services/KMeansService.php index 31ccb52..4dc2a93 100644 --- a/app/Services/KMeansService.php +++ b/app/Services/KMeansService.php @@ -12,27 +12,24 @@ class KMeansService public function hitungKMeansCuras() { $data = Curas::select('id', 'kecamatan_id', 'klaster_id', 'jumlah_curas')->orderBy('jumlah_curas', 'asc')->get(); - $k = Klaster::count('id'); $maxIterasi = 100; - $uniqueCount = $data->unique('jumlah_curas')->count(); - if ($uniqueCount < $k) { - throw new \Exception("Jumlah nilai unik pada 'jumlah_curas' ($uniqueCount) kurang dari jumlah klaster ($k). Pastikan data memiliki variasi yang cukup."); + // Ambil centroid awal yang unik + $minValue = $data->min('jumlah_curas'); + $maxValue = $data->max('jumlah_curas'); + + $generated = collect(); + while ($generated->count() < $k) { + $randomFloat = round(mt_rand($minValue * 100, $maxValue * 100) / 100, 2); + if (!$generated->contains($randomFloat)) { + $generated->push($randomFloat); + } } - // Ambil centroid awal yang unik - $centroids = $data->unique('jumlah_curas') // Pastikan nilai unik - ->shuffle() // Acak - ->take($k) // Ambil k data - ->values() - ->map(function ($item) { - return [ - 'jumlah_curas' => $item->jumlah_curas, - ]; - }); - - // Simpan centroid awal sebelum iterasi + $centroids = $generated->map(function ($value) { + return ['jumlah_curas' => $value]; + }); $centroidAwal = $centroids->toArray(); $iterasi = []; @@ -52,7 +49,7 @@ public function hitungKMeansCuras() $iterasi[$i][] = array_merge(['kecamatan_id' => $item->kecamatan_id], $jarak); - $minIndex = array_keys($jarak, min($jarak))[0]; // e.g. "jarakC2" + $minIndex = array_keys($jarak, min($jarak))[0]; $clusterNumber = (int) str_replace("C", "", $minIndex); $clustered[$clusterNumber][] = $item; @@ -60,15 +57,12 @@ public function hitungKMeansCuras() $currentAssignment[$item->id] = $clusterNumber; } - // ✨ Cek konvergensi: jika assignment sekarang == sebelumnya, break if ($currentAssignment === $prevAssignment) { break; } $prevAssignment = $currentAssignment; - - // Update centroid berdasarkan rata-rata foreach ($clustered as $key => $group) { $avg = collect($group)->avg('jumlah_curas'); @@ -79,21 +73,17 @@ public function hitungKMeansCuras() }); } } - - // Final mapping centroid ke klaster_id (aman/sedang/rawan) + // Final mapping centroid ke klaster_id $finalCentroids = $centroids->map(function ($item, $index) { return ['index' => $index + 1, 'jumlah_curas' => $item['jumlah_curas']]; })->sortBy('jumlah_curas')->values(); $centroidToKlaster = []; - foreach ($finalCentroids as $i => $centroid) { - // Klaster ID mulai dari 1 (asumsi klaster di DB bernomor 1, 2, 3, ...) $centroidToKlaster[$centroid['index']] = $i + 1; } - // Update ke database foreach ($data as $item) { Curas::where('id', $item->id)->update([ @@ -101,19 +91,23 @@ public function hitungKMeansCuras() ]); } - + // Format centroid awal $centroidAwalFormatted = collect($centroidAwal)->values()->map(function ($item, $index) { return ['C' . ($index + 1) => $item['jumlah_curas']]; }); - + + // Format centroid akhir + $centroidAkhirFormatted = $centroids->values()->map(function ($item, $index) { + return ['C' . ($index + 1) => $item['jumlah_curas']]; + }); + return [ 'centroid_awal' => $centroidAwalFormatted, + 'centroid_akhir' => $centroidAkhirFormatted, 'iterasi' => $iterasi ]; - } - public function hitungKMeansCuranmor() { $data = Curanmor::select('id', 'kecamatan_id', 'klaster_id', 'jumlah_curanmor')->orderBy('jumlah_curanmor', 'asc')->get(); @@ -121,23 +115,20 @@ public function hitungKMeansCuranmor() $k = Klaster::count('id'); $maxIterasi = 100; - $uniqueCount = $data->unique('jumlah_curanmor')->count(); - if ($uniqueCount < $k) { - throw new \Exception("Jumlah nilai unik pada 'jumlah_curanmor' ($uniqueCount) kurang dari jumlah klaster ($k). Pastikan data memiliki variasi yang cukup."); + $minValue = $data->min('jumlah_curanmor'); + $maxValue = $data->max('jumlah_curanmor'); + + $generated = collect(); + while ($generated->count() < $k) { + $randomFloat = round(mt_rand($minValue * 100, $maxValue * 100) / 100, 2); + if (!$generated->contains($randomFloat)) { + $generated->push($randomFloat); + } } - // Ambil centroid awal yang unik - $centroids = $data->unique('jumlah_curanmor') // Pastikan nilai unik - ->shuffle() // Acak - ->take($k) // Ambil k data - ->values() - ->map(function ($item) { - return [ - 'jumlah_curanmor' => $item->jumlah_curanmor, - ]; - }); - - // Simpan centroid awal sebelum iterasi + $centroids = $generated->map(function ($value) { + return ['jumlah_curanmor' => $value]; + }); $centroidAwal = $centroids->toArray(); $iterasi = []; @@ -224,42 +215,43 @@ public function SSEElbowCuras() $maxK = 10; $maxIterasi = 100; $elbowData = []; - - for ($k = 1; $k <= $maxK; $k++) { - // Inisialisasi centroid awal secara acak - $centroids = $data->unique('jumlah_curas')->shuffle()->take($k)->values()->map(function ($item) { - return ['jumlah_curas' => $item->jumlah_curas]; + + // Ambil nilai minimum dan maksimum dari jumlah_curas + $min = $data->min('jumlah_curas'); + $max = $data->max('jumlah_curas'); + + for ($k = 2; $k <= $maxK; $k++) { + // Inisialisasi centroid awal sebagai float acak dalam rentang min-max + $centroids = collect(range(1, $k))->map(function () use ($min, $max) { + return ['jumlah_curas' => mt_rand($min * 100, $max * 100) / 100]; }); - - // Simpan centroid awal sebagai array angka + $centroidAwal = $centroids->pluck('jumlah_curas')->toArray(); - $prevAssignment = []; - + for ($iter = 0; $iter < $maxIterasi; $iter++) { $clustered = []; $currentAssignment = []; - + foreach ($data as $item) { $jarak = []; - + foreach ($centroids as $idx => $centroid) { $dist = abs($item->jumlah_curas - $centroid['jumlah_curas']); $jarak[$idx] = $dist; } - + $minIndex = array_keys($jarak, min($jarak))[0]; $clustered[$minIndex][] = $item; $currentAssignment[$item->id] = $minIndex; } - + if ($currentAssignment === $prevAssignment) { break; } - + $prevAssignment = $currentAssignment; - - // Update centroid + foreach ($clustered as $key => $group) { $avg = collect($group)->avg('jumlah_curas'); $centroids = $centroids->map(function ($centroid, $idx) use ($key, $avg) { @@ -269,8 +261,8 @@ public function SSEElbowCuras() }); } } - - // Hitung SSE untuk k saat ini + + // Hitung SSE $sse = 0; foreach ($clustered as $key => $group) { $centroidVal = $centroids[$key]['jumlah_curas']; @@ -278,20 +270,22 @@ public function SSEElbowCuras() $sse += pow($item->jumlah_curas - $centroidVal, 2); } } - + $elbowData[] = [ 'k' => $k, 'sse' => $sse, 'centroid_awal' => $centroidAwal ]; } - - // Simpan ke file + + + file_put_contents( storage_path('app/public/sse_elbow_curas.json'), json_encode($elbowData, JSON_PRETTY_PRINT) ); } + public function SSEElbowCuranmor() @@ -301,7 +295,7 @@ public function SSEElbowCuranmor() $maxIterasi = 100; $elbowData = []; - for ($k = 1; $k <= $maxK; $k++) { + for ($k = 2; $k <= $maxK; $k++) { // Inisialisasi centroid awal secara acak $centroids = $data->unique('jumlah_curanmor')->shuffle()->take($k)->values()->map(function ($item) { return ['jumlah_curanmor' => $item->jumlah_curanmor];