Files
cabutan-bertuah-koipb/app/Services/MemberImportService.php
2026-06-24 18:30:00 +08:00

118 lines
3.7 KiB
PHP

<?php
namespace App\Services;
use App\Models\ImportLog;
use App\Models\Member;
class MemberImportService
{
/**
* Import baris anggota. Tidak crash bila ada duplicate/ralat —
* pulangkan ringkasan + simpan ImportLog.
*
* @param array<int, array<string, string>> $rows
*/
public function import(array $rows, string $filename, ?int $userId = null): ImportLog
{
$success = 0;
$duplicate = 0;
$failed = 0;
$errors = [];
// Set pra-muat untuk kesan duplicate dalam fail itu sendiri
$seenKp = [];
$seenPekerja = [];
foreach ($rows as $i => $row) {
$line = $i + 2; // baris fail (header = 1)
$nama = trim($row['nama'] ?? '');
$noKp = $this->clean($row['no_kp'] ?? '');
$noPekerja = $this->clean($row['no_pekerja'] ?? '');
$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 ($noPekerja && isset($seenPekerja[$noPekerja])) {
$duplicate++;
$errors[] = "Baris {$line}: no_pekerja '{$noPekerja}' duplicate dalam fail.";
continue;
}
// Duplicate dalam DB
$exists = Member::query()
->when($noKp, fn ($q) => $q->orWhere('no_kp', $noKp))
->when($noPekerja, fn ($q) => $q->orWhere('no_pekerja', $noPekerja))
->when($noKp || $noPekerja, fn ($q) => $q, fn ($q) => $q->whereRaw('1 = 0'))
->exists();
if ($exists) {
$duplicate++;
$errors[] = "Baris {$line}: anggota '{$nama}' sudah wujud (no_kp/no_pekerja sepadan).";
continue;
}
try {
Member::create([
'no_anggota' => $noAnggota ?: null,
'no_pekerja' => $noPekerja ?: null,
'no_kp' => $noKp ?: null,
'nama' => $nama,
'jabatan' => $this->clean($row['jabatan'] ?? '') ?: null,
'bahagian' => $this->clean($row['bahagian'] ?? '') ?: null,
'telefon' => $this->clean($row['telefon'] ?? '') ?: null,
'status_aktif' => $this->parseAktif($row['status_aktif'] ?? ''),
]);
if ($noKp) {
$seenKp[$noKp] = true;
}
if ($noPekerja) {
$seenPekerja[$noPekerja] = 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);
}
private function parseAktif(string $value): bool
{
$value = strtolower(trim($value));
if ($value === '') {
return true; // default aktif
}
return in_array($value, ['1', 'aktif', 'active', 'ya', 'yes', 'true', 'y'], true);
}
}