> $rows */ public function import(array $rows, string $filename, ?int $userId = null): ImportLog { $success = 0; $duplicate = 0; $failed = 0; $errors = []; // Lindung integriti cabutan: jangan buang anggota jika sudah ada pemenang disahkan. if (DrawResult::where('status', DrawResult::STATUS_CONFIRMED)->exists()) { return ImportLog::create([ 'type' => 'members', 'filename' => $filename, 'total_rows' => count($rows), 'success_count' => 0, 'duplicate_count' => 0, 'failed_count' => count($rows), 'errors' => ['Import dibatalkan: sudah ada pemenang cabutan yang disahkan. Data anggota tidak boleh dibuang.'], 'imported_by' => $userId, ]); } // Set pra-muat untuk kesan duplicate dalam fail itu sendiri $seenKp = []; $seenAnggota = []; DB::transaction(function () use ($rows, &$success, &$duplicate, &$failed, &$errors, &$seenKp, &$seenAnggota) { // Buang semua anggota lama (ganti penuh setiap import) Member::query()->delete(); foreach ($rows as $i => $row) { $line = $i + 2; // baris fail (header = 1) $nama = trim($row['nama'] ?? ''); $noKp = $this->clean($row['no_kp'] ?? ''); $noAnggota = $this->clean($row['no_anggota'] ?? ''); if ($nama === '') { $failed++; $errors[] = "Baris {$line}: nama wajib diisi."; continue; } // Duplicate dalam fail if ($noKp && isset($seenKp[$noKp])) { $duplicate++; $errors[] = "Baris {$line}: no_kp '{$noKp}' duplicate dalam fail."; continue; } if ($noAnggota && isset($seenAnggota[$noAnggota])) { $duplicate++; $errors[] = "Baris {$line}: no_anggota '{$noAnggota}' duplicate dalam fail."; continue; } try { Member::create([ 'no_anggota' => $noAnggota ?: null, 'no_kp' => $noKp ?: null, 'nama' => $nama, ]); if ($noKp) { $seenKp[$noKp] = true; } if ($noAnggota) { $seenAnggota[$noAnggota] = true; } $success++; } catch (\Throwable $e) { $failed++; $errors[] = "Baris {$line}: {$e->getMessage()}"; } } }); return ImportLog::create([ 'type' => 'members', 'filename' => $filename, 'total_rows' => count($rows), 'success_count' => $success, 'duplicate_count' => $duplicate, 'failed_count' => $failed, 'errors' => $errors ?: null, 'imported_by' => $userId, ]); } private function clean(string $value): string { return trim($value); } }