-
Notifications
You must be signed in to change notification settings - Fork 143
[Add] Menambah fitur Data Sarana #1335
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| <?php | ||
|
|
||
| namespace App\Exports; | ||
|
|
||
| use Illuminate\Support\Collection; | ||
| use Maatwebsite\Excel\Concerns\FromCollection; | ||
| use Maatwebsite\Excel\Concerns\WithHeadings; | ||
| use Maatwebsite\Excel\Concerns\WithStyles; | ||
| use Maatwebsite\Excel\Concerns\WithEvents; | ||
| use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; | ||
| use Maatwebsite\Excel\Events\AfterSheet; | ||
|
|
||
| class ExportDataSarana implements FromCollection, WithHeadings, WithStyles, WithEvents | ||
| { | ||
| protected $author; | ||
| protected $data; | ||
|
|
||
| public function __construct(Collection $data, $author = 'Admin') | ||
| { | ||
| $this->data = $data; | ||
| $this->author = $author; | ||
| } | ||
|
|
||
| public function collection() | ||
| { | ||
| return $this->data->map(function ($item) { | ||
| return [ | ||
| $item->id, | ||
| $item->desa->nama ?? '-', | ||
| $item->nama, | ||
| $item->jumlah, | ||
| $item->kategori, | ||
| $item->keterangan, | ||
| ]; | ||
| }); | ||
| } | ||
|
|
||
| public function headings(): array | ||
| { | ||
| return [ | ||
| ['Laporan Data Sarana'], | ||
| [''], | ||
| ['ID', 'Desa', 'Nama Sarana', 'Jumlah', 'Kategori', 'Keterangan'], | ||
| ]; | ||
| } | ||
|
|
||
| public function styles(Worksheet $sheet) | ||
| { | ||
| return [ | ||
| 1 => ['font' => ['bold' => true, 'size' => 14]], | ||
| 3 => ['font' => ['bold' => true, 'color' => ['rgb' => 'FFFFFF']]], | ||
| ]; | ||
| } | ||
|
|
||
| public function registerEvents(): array | ||
| { | ||
| return [ | ||
| AfterSheet::class => function (AfterSheet $event) { | ||
| $sheet = $event->sheet->getDelegate(); | ||
|
|
||
| $sheet->mergeCells('A1:F1'); | ||
| $sheet->getStyle('A1')->getAlignment()->setHorizontal('center'); | ||
|
|
||
| $sheet->setCellValue('A2', 'Nama: ' . $this->author); | ||
| $sheet->setCellValue('F2', 'Tanggal: ' . date('d-m-Y')); | ||
|
|
||
| $sheet->getStyle('A2')->getAlignment()->setHorizontal('left'); | ||
| $sheet->getStyle('F2')->getAlignment()->setHorizontal('right'); | ||
|
|
||
| $sheet->getStyle('A3:F3')->getFill() | ||
| ->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID) | ||
| ->getStartColor()->setARGB('4CAF50'); | ||
|
|
||
| $lastRow = $sheet->getHighestRow(); | ||
| $sheet->getStyle("A3:F{$lastRow}") | ||
| ->getBorders() | ||
| ->getAllBorders() | ||
| ->setBorderStyle(\PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN); | ||
| }, | ||
| ]; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,169 @@ | ||
| <?php | ||
|
|
||
| namespace App\Http\Controllers\Data; | ||
|
|
||
| use App\Http\Controllers\Controller; | ||
| use App\Models\DataDesa; | ||
| use App\Models\DataSarana; | ||
| use Illuminate\Http\Request; | ||
| use Maatwebsite\Excel\Facades\Excel; | ||
| use App\Exports\ExportDataSarana; | ||
| use App\Imports\ImportDataSarana; | ||
| use Illuminate\Support\Facades\DB; | ||
| use Yajra\DataTables\Facades\DataTables; | ||
|
|
||
| class DataSaranaController extends Controller | ||
| { | ||
| public function index() | ||
| { | ||
| $page_title = 'Data Sarana'; | ||
| $page_description = 'Daftar Sarana Desa'; | ||
|
|
||
| $desaSelect = DataDesa::all(); | ||
|
|
||
| return view('data.data_sarana.index', compact('page_title', 'page_description', 'desaSelect')); | ||
| } | ||
|
|
||
| public function getData(Request $request) | ||
| { | ||
| $query = DataSarana::with('desa'); | ||
|
|
||
| if ($request->desa_id) { | ||
| $query->where('desa_id', $request->desa_id); | ||
| } | ||
| if ($request->kategori) { | ||
| $query->where('kategori', $request->kategori); | ||
| } | ||
|
|
||
| return datatables()->of($query) | ||
| ->addColumn('desa', function ($row) { | ||
| return $row->desa ? $row->desa->nama : '-'; | ||
| }) | ||
| ->addColumn('aksi', function ($row) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [CRITICAL] ⚡ Performance: N+1 Query di DataTables Masalah: Method Kode: Dampak: Pada list dengan 100 sarana, akan terjadi 1 query utama + 100 query untuk load relasi desa = 101 queries total. Di production dengan traffic tinggi, ini bisa menyebabkan database bottleneck. Fix: // Line 42 - Tambahkan eager loading
$query = DataSarana::with('desa:id,nama')->query();
// Atau jika perlu filter desa
if ($request->has('desa_id') && $request->desa_id != '') {
$query->where('desa_id', $request->desa_id);
} |
||
| $editUrl = route('data.data-sarana.edit', $row->id); | ||
| $deleteUrl = route('data.data-sarana.destroy', $row->id); | ||
|
|
||
| return view('data.data_sarana.partials.action', compact('editUrl', 'deleteUrl'))->render(); | ||
| }) | ||
| ->rawColumns(['aksi']) | ||
| ->make(true); | ||
| } | ||
|
|
||
|
|
||
| public function create() | ||
| { | ||
| $page_title = "Tambah Sarana"; | ||
| $page_description = "Form tambah data sarana"; | ||
|
|
||
| $desas = DataDesa::all(); | ||
| return view('data.data_sarana.create', compact('page_title', 'page_description', 'desas')); | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [HIGH] 📝 Code Quality: Missing FormRequest Class Kategori: Architecture Fix: // 1. Buat app/Http/Requests/DataSaranaRequest.php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class DataSaranaRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'desa_id' => 'required|integer|exists:das_data_desa,id',
'nama' => 'required|string|max:255',
'jumlah' => 'required|integer|min:0',
'kategori' => 'required|string|max:255',
'keterangan' => 'nullable|string|max:255',
];
}
}
// 2. Update controller methods:
public function store(DataSaranaRequest $request)
{
DataSarana::create($request->validated());
return redirect()->route('data.data-sarana.index')
->with('success', 'Data Sarana berhasil ditambahkan');
}
public function update(DataSaranaRequest $request, $id)
{
$sarana = DataSarana::findOrFail($id);
$sarana->update($request->validated());
return redirect()->route('data.data-sarana.index')
->with('success', 'Data Sarana berhasil diupdate');
} |
||
|
|
||
| public function store(Request $request) | ||
| { | ||
| try { | ||
| $request->validate([ | ||
| 'desa_id' => 'required|integer:desa_id', | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [CRITICAL] 📝 Code Quality: Invalid Validation Rule Syntax Kategori: PHP Quality Fix: 'desa_id' => 'required|integer|exists:das_data_desa,id',
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [CRITICAL] 🐛 Bug: Invalid Validation Rule Syntax Kode: Skenario: Ketika user submit form create, Laravel validation akan throw exception karena rule 'integer:desa_id' adalah syntax yang salah. Rule 'integer' tidak menerima parameter dengan colon. Ini akan menyebabkan 500 error di production. Dampak: Form create tidak bisa digunakan sama sekali. Setiap submit akan crash dengan error "Method Illuminate\Validation\Validator::validateInteger does not exist" atau validation gagal silent. Fix: 'desa_id' => 'required|integer|exists:das_data_desa,id', |
||
| 'nama' => 'required|string|max:255', | ||
| 'jumlah' => 'required|integer|min:0', | ||
| 'kategori' => 'required|string|max:100', | ||
| 'keterangan' => 'required|string:max:255', | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [CRITICAL] 📝 Code Quality: Invalid Validation Rule Syntax Kategori: PHP Quality Fix: 'keterangan' => 'nullable|string|max:255',
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [CRITICAL] 🐛 Bug: Invalid Validation Rule Syntax Kode: Skenario: Syntax validation salah, seharusnya 'string|max:255' bukan 'string:max:255'. Colon digunakan untuk parameter rule tertentu (seperti max:255), bukan untuk chain rule. Ini akan menyebabkan validation error atau exception. Dampak: Form create akan crash atau validation tidak berjalan dengan benar. User tidak bisa menyimpan data sarana baru. Fix: 'keterangan' => 'nullable|string|max:255',
// Ubah juga dari 'required' ke 'nullable' karena keterangan seharusnya optional |
||
| ]); | ||
| DataSarana::create($request->all()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [CRITICAL] 📝 Code Quality: Mass Assignment Vulnerability Kategori: Architecture Fix: DataSarana::create($request->only(['desa_id', 'nama', 'jumlah', 'kategori', 'keterangan']));
// atau lebih baik:
DataSarana::create($request->validated());
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [HIGH] 🐛 Bug: Mass Assignment Vulnerability Kode: Skenario: Menggunakan $request->all() memungkinkan attacker mengirim field tambahan yang tidak diinginkan dalam POST request. Jika model tidak memiliki $fillable/$guarded yang ketat, field seperti 'id', 'created_at', atau field lain bisa di-inject. Dampak: Security vulnerability. Attacker bisa manipulasi data dengan menambahkan field tidak authorized dalam request payload. Misalnya inject 'id' untuk overwrite record lain, atau field internal lainnya. Fix: DataSarana::create($request->only(['desa_id', 'nama', 'jumlah', 'kategori', 'keterangan']));
// Atau lebih baik:
DataSarana::create($request->validated()); |
||
| } catch (\Exception $e) { | ||
| report($e); | ||
| return back()->withInput()->with('error', 'Data Sarana gagal disimpan!'); | ||
| } | ||
| return redirect()->route('data.data-sarana.index')->with('success', 'Data Sarana berhasil disimpan!'); | ||
| } | ||
|
|
||
| public function edit($id) | ||
| { | ||
| $page_title = 'Edit Data Sarana'; | ||
| $page_description = 'Ubah informasi sarana desa'; | ||
|
|
||
| $sarana = DataSarana::findOrFail($id); | ||
| $desas = DataDesa::all(); | ||
|
|
||
| return view('data.data_sarana.edit', compact('page_title', 'page_description', 'sarana', 'desas')); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [HIGH] 🐛 Bug: Missing Null Check Before Method Call Kode: Skenario: Jika DataSarana::find($id) tidak menemukan record (return null), view akan menerima $sarana = null. Ketika blade template mencoba akses $sarana->nama, $sarana->kategori, dll, akan terjadi error "Attempt to read property on null". Dampak: User yang akses URL /edit/999 (ID tidak exist) akan mendapat 500 error instead of 404. Ini juga bisa terjadi karena race condition (record dihapus setelah user buka halaman list tapi sebelum klik edit). Fix: $sarana = DataSarana::findOrFail($id);
// findOrFail() otomatis throw 404 jika record tidak ditemukan |
||
| } | ||
|
|
||
| public function update(Request $request, $id) | ||
| { | ||
| try { | ||
| $request->validate([ | ||
| 'desa_id' => 'required|integer:desa_id', | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [CRITICAL] 📝 Code Quality: Invalid Validation Rule Syntax Kategori: PHP Quality Fix: 'desa_id' => 'required|integer|exists:das_data_desa,id',
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [CRITICAL] 🐛 Bug: Invalid Validation Rule Syntax Kode: Skenario: Bug yang sama dengan line 66, tapi di method update(). Validation rule syntax salah akan menyebabkan exception saat user submit form edit. Dampak: Form edit tidak bisa digunakan. Setiap update akan crash dengan validation error atau exception. Fix: 'desa_id' => 'required|integer|exists:das_data_desa,id', |
||
| 'nama' => 'required|string|max:255', | ||
| 'jumlah' => 'required|integer|min:0', | ||
| 'kategori' => 'required|string|max:100', | ||
| 'keterangan' => 'required|string:max:255', | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [CRITICAL] 📝 Code Quality: Invalid Validation Rule Syntax Kategori: PHP Quality Fix: 'keterangan' => 'nullable|string|max:255',
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [CRITICAL] 🐛 Bug: Invalid Validation Rule Syntax Kode: Skenario: Bug yang sama dengan line 70, tapi di method update(). Syntax validation salah akan crash form edit. Dampak: User tidak bisa update data sarana yang sudah ada. Form edit tidak fungsional. Fix: 'keterangan' => 'nullable|string|max:255', |
||
| ]); | ||
| $sarana = DataSarana::findOrFail($id); | ||
| $sarana->update($request->all()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [HIGH] 📝 Code Quality: Mass Assignment Vulnerability Kategori: Architecture Fix: $sarana->update($request->only(['desa_id', 'nama', 'jumlah', 'kategori', 'keterangan']));
// atau lebih baik:
$sarana->update($request->validated());
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [HIGH] 🐛 Bug: Mass Assignment Vulnerability Kode: Skenario: Sama seperti line 72, menggunakan $request->all() di update memungkinkan injection field tidak diinginkan. Attacker bisa menambahkan field dalam PUT request untuk memodifikasi data yang seharusnya protected. Dampak: Security vulnerability. Attacker bisa manipulasi field yang tidak seharusnya bisa diubah melalui form edit. Fix: $sarana->update($request->only(['desa_id', 'nama', 'jumlah', 'kategori', 'keterangan']));
// Atau:
$sarana->update($request->validated()); |
||
| } catch (\Exception $e) { | ||
| report($e); | ||
| return back()->withInput()->with('error', 'Data Sarana gagal diperbarui'); | ||
| } | ||
| return redirect()->route('data.data-sarana.index')->with('success', 'Data Sarana berhasil diperbarui'); | ||
| } | ||
|
|
||
| public function destroy($id) | ||
| { | ||
| try { | ||
| $sarana = DataSarana::findOrFail($id); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [HIGH] 🐛 Bug: Missing Null Check Before Method Call Kode: Skenario: Jika DataSarana::find($id) return null (record tidak ditemukan), memanggil ->delete() pada null akan throw fatal error "Call to a member function delete() on null". Ini bisa terjadi karena race condition (2 user delete record yang sama secara bersamaan) atau direct API call dengan ID invalid. Dampak: Application crash dengan 500 error. User mendapat error page instead of graceful error message. Logs penuh dengan fatal error. Fix: $sarana = DataSarana::findOrFail($id);
$sarana->delete();
// Atau dengan manual check:
if (!$sarana) {
return back()->with('error', 'Data tidak ditemukan');
} |
||
| $sarana->delete(); | ||
| } catch (\Exception $e) { | ||
| report($e); | ||
| return back()->withInput()->with('error', 'Data Sarana gagal dihapus'); | ||
| } | ||
| return redirect()->route('data.data-sarana.index')->with('success', 'Data Sarana berhasil dihapus'); | ||
| } | ||
|
|
||
| public function export() | ||
| { | ||
| try { | ||
| $search = request('search'); | ||
| $kategori = request('kategori'); | ||
| $startDate = request('start_date'); | ||
| $endDate = request('end_date'); | ||
|
|
||
| $data = \App\Models\DataSarana::with('desa') | ||
| ->when($search, fn($q) => $q->where('nama', 'like', "%$search%")) | ||
| ->when($kategori, fn($q) => $q->where('kategori', $kategori)) | ||
| ->when($startDate, fn($q) => $q->whereDate('created_at', '>=', $startDate)) | ||
| ->when($endDate, fn($q) => $q->whereDate('created_at', '<=', $endDate)) | ||
| ->latest('id') | ||
| ->get(); | ||
|
|
||
| return Excel::download(new ExportDataSarana($data, 'Admin Desa'), 'data_sarana.xlsx'); | ||
| } catch (\Exception $e) { | ||
| report($e); | ||
| return back()->withInput()->with('error', 'Data Sarana gagal dihapus'); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [HIGH] 📝 Code Quality: Wrong Error Message Kategori: PHP Quality Fix: return back()->with('error', 'Data Sarana gagal diexport');
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [HIGH] 🐛 Bug: Wrong Error Message Kode: Skenario: Ini ada di dalam method export(), bukan destroy(). Ketika export gagal, user melihat pesan error "Data Sarana gagal dihapus" yang sangat misleading. User akan bingung kenapa ada pesan delete padahal mereka sedang export. Dampak: User confusion. Troubleshooting jadi sulit karena error message tidak match dengan action yang dilakukan. User mungkin report bug yang salah. Fix: return back()->with('error', 'Data Sarana gagal diexport');
// Juga hapus withInput() karena tidak perlu di export |
||
| } | ||
| } | ||
|
|
||
| public function import() | ||
| { | ||
| $page_title = "Import Sarana"; | ||
| $page_description = "Upload data sarana"; | ||
|
|
||
| $desas = DataDesa::all(); | ||
|
|
||
| return view('data.data_sarana.import', compact('page_title', 'page_description', 'desas')); | ||
| } | ||
|
|
||
| public function importExcel(Request $request) | ||
| { | ||
| try { | ||
| $request->validate([ | ||
| 'file' => 'required|mimes:xlsx,xls,csv' | ||
| ]); | ||
| Excel::import(new ImportDataSarana, $request->file('file')); | ||
| } catch (\Exception $e) { | ||
| report($e); | ||
| return back()->withInput()->with('error', 'Data Sarana gagal diimport'); | ||
| } | ||
| return redirect()->route('data.data-sarana.index')->with('success', 'Data Sarana berhasil diimport'); | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,6 +36,7 @@ | |
| use App\Models\DataUmum; | ||
| use Illuminate\Http\Request; | ||
| use Illuminate\Http\Response; | ||
| use Illuminate\Support\Facades\DB; | ||
|
|
||
| class DataUmumController extends Controller | ||
| { | ||
|
|
@@ -51,7 +52,12 @@ public function index() | |
| $page_title = 'Data Umum'; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [HIGH] ⚡ Performance: Missing Cache untuk Data Statis Masalah: Query agregasi sarana dipanggil setiap kali form edit dibuka, padahal data ini relatif statis dan bisa di-cache. Kode: Dampak: Setiap page load form edit = 14 queries (1 untuk pengaturan + 13 untuk rekap sarana). Dengan 1000 page views/hari = 14,000 queries yang sebenarnya bisa di-cache. Fix: // Cache rekap sarana per desa selama 1 jam
$rekapSarana = Cache::remember("sarana_rekap_{$desa_id}", 3600, function() use ($desa_id) {
return [
'puskesmas' => DataSarana::where('desa_id', $desa_id)
->where('kategori', 'puskesmas')->sum('jumlah'),
'puskesmas_pembantu' => DataSarana::where('desa_id', $desa_id)
->where('kategori', 'puskesmas_pembantu')->sum('jumlah'),
// ... dst untuk 13 kategori
];
});
// Invalidate cache saat ada perubahan di DataSaranaController:
// Cache::forget("sarana_rekap_{$request->desa_id}"); |
||
| $page_description = 'Ubah Data Umum'; | ||
|
|
||
| return view('data.data_umum.edit', compact('page_title', 'page_description', 'data_umum', 'luas_wilayah')); | ||
| $rekapKategori = DB::table('das_data_sarana') | ||
| ->select('kategori', DB::raw('SUM(jumlah) as total')) | ||
| ->groupBy('kategori') | ||
| ->pluck('total', 'kategori'); | ||
|
|
||
| return view('data.data_umum.edit', compact('page_title', 'page_description', 'data_umum', 'luas_wilayah', 'rekapKategori')); | ||
| } | ||
|
|
||
| /** | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| <?php | ||
|
|
||
| namespace App\Imports; | ||
|
|
||
| use App\Models\DataDesa; | ||
| use App\Models\DataSarana; | ||
| use Maatwebsite\Excel\Concerns\ToModel; | ||
| use Maatwebsite\Excel\Concerns\WithHeadingRow; | ||
|
|
||
| class ImportDataSarana implements ToModel, WithHeadingRow | ||
| { | ||
| /** | ||
| * @param array $row | ||
| * | ||
| * @return \Illuminate\Database\Eloquent\Model|null | ||
| */ | ||
| public function model(array $row) | ||
| { | ||
| if (!DataDesa::where('id', $row['desa_id'])->exists()) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [HIGH] ⚡ Performance: Query Validasi di Loop Import Masalah: Setiap row import melakukan query Kode: Dampak: Import 1000 rows = 1000 queries validasi. Proses import yang seharusnya 5 detik bisa jadi 30+ detik karena database round-trip overhead. Fix: // Preload semua valid desa_id sekali di constructor
private $validDesaIds;
public function __construct()
{
$this->validDesaIds = DataDesa::pluck('id')->flip();
}
public function model(array $row)
{
// Validasi menggunakan in-memory array
if (!isset($this->validDesaIds[$row['desa_id']])) {
\Log::warning("Import skipped: desa_id {$row['desa_id']} not found");
return null;
}
return new DataSarana([
'desa_id' => $row['desa_id'],
'nama' => $row['nama'],
'jumlah' => $row['jumlah'],
'kategori' => $row['kategori'],
'keterangan' => $row['keterangan'] ?? null,
]);
}
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [HIGH] 🐛 Bug: Silent Failure Without Logging Kode: Skenario: Ketika import Excel dengan 100 rows, jika 20 rows memiliki desa_id yang tidak valid, mereka akan di-skip secara silent (return null). User hanya melihat "Import berhasil" tapi tidak tahu bahwa 20 rows gagal. Tidak ada cara untuk track row mana yang gagal atau kenapa. Dampak: Data loss tanpa notifikasi. User mengira semua data ter-import tapi sebenarnya ada yang hilang. Debugging sangat sulit karena tidak ada log. Production data bisa incomplete tanpa disadari. Fix: if (!DataDesa::where('id', $row['desa_id'])->exists()) {
\Log::warning('Import skipped: desa_id not found', [
'desa_id' => $row['desa_id'],
'row' => $row
]);
return null;
} |
||
| return null; | ||
| } | ||
|
|
||
| return new DataSarana([ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [HIGH] 🐛 Bug: Missing Array Key Validation Kode: Skenario: Jika user upload Excel file dengan kolom yang hilang (misalnya tidak ada kolom 'kategori'), akses $row['kategori'] akan throw "Undefined array key 'kategori'" error di PHP 8+. Import akan crash di tengah jalan. Dampak: Import gagal total dengan 500 error. User tidak mendapat feedback yang jelas tentang apa yang salah dengan file Excel mereka. Harus trial-error untuk menemukan kolom mana yang hilang. Fix: // Tambahkan validasi di awal method model()
if (!isset($row['desa_id'], $row['nama'], $row['jumlah'], $row['kategori'])) {
\Log::warning('Import skipped: missing required columns', ['row' => $row]);
return null;
}
return new DataSarana([
'desa_id' => $row['desa_id'],
'nama' => $row['nama'],
'jumlah' => $row['jumlah'],
'kategori' => $row['kategori'],
'keterangan' => $row['keterangan'] ?? null,
]); |
||
| 'desa_id' => $row['desa_id'], | ||
| 'nama' => $row['nama'], | ||
| 'jumlah' => $row['jumlah'], | ||
| 'kategori' => $row['kategori'], | ||
| 'keterangan' => $row['keterangan'], | ||
| ]); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| <?php | ||
|
|
||
| namespace App\Models; | ||
|
|
||
| use Illuminate\Database\Eloquent\Factories\HasFactory; | ||
| use Illuminate\Database\Eloquent\Model; | ||
|
|
||
|
|
||
| class DataSarana extends Model | ||
| { | ||
| use HasFactory; | ||
|
|
||
| protected $table = 'das_data_sarana'; | ||
| protected $fillable = ['desa_id','kategori','nama','jumlah','keterangan']; | ||
|
|
||
| public function desa() | ||
| { | ||
| return $this->belongsTo(DataDesa::class, 'desa_id', 'id'); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| <?php | ||
|
|
||
| namespace Database\Factories; | ||
|
|
||
| use App\Models\DataSarana; | ||
| use App\Models\DataDesa; | ||
| use Illuminate\Database\Eloquent\Factories\Factory; | ||
|
|
||
| class DataSaranaFactory extends Factory | ||
| { | ||
| protected $model = DataSarana::class; | ||
|
|
||
| public function definition() | ||
| { | ||
| return [ | ||
| 'desa_id' => DataDesa::factory(), // otomatis buat desa baru | ||
| 'nama' => $this->faker->word, | ||
| 'jumlah' => $this->faker->numberBetween(1, 100), | ||
| 'kategori' => $this->faker->randomElement([ | ||
| 'puskesmas','puskesmas_pembantu','posyandu','pondok_bersalin', | ||
| 'paud','sd','smp','sma', | ||
| 'masjid_besar','mushola','gereja','pasar','balai_pertemuan' | ||
| ]), | ||
| 'keterangan' => $this->faker->sentence, | ||
| ]; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[CRITICAL] ⚡ Performance: Memory Overflow pada Export Besar
Masalah: Method
collection()load semua data sekaligus ke memory tanpa chunking. Untuk dataset besar (>10k rows), ini akan menyebabkan memory exhaustion.Kode:
return DataSarana::with('desa')->get();Dampak: Export 50k sarana dengan relasi desa bisa consume 500MB+ memory. Server dengan memory_limit 256M akan crash dengan error "Allowed memory size exhausted".
Fix: