*/ public function readHeaders(string $path): array { $sheet = IOFactory::load($path)->getActiveSheet(); $headers = []; foreach ($sheet->getRowIterator(1, 1) as $row) { foreach ($row->getCellIterator() as $cell) { $value = trim((string) $cell->getValue()); if ($value !== '') { $headers[] = $value; } } } return $headers; } /** * @return array> */ public function readRows(string $path, ?int $limit = null): array { $sheet = IOFactory::load($path)->getActiveSheet(); $headers = $this->readHeaders($path); $rows = []; $highestRow = $sheet->getHighestDataRow(); for ($rowIndex = 2; $rowIndex <= $highestRow; $rowIndex++) { $rowData = []; $isEmpty = true; foreach ($headers as $columnIndex => $header) { $columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($columnIndex + 1); $value = $sheet->getCell($columnLetter . $rowIndex)->getFormattedValue(); $rowData[$header] = is_string($value) ? trim($value) : $value; if ($rowData[$header] !== null && $rowData[$header] !== '') { $isEmpty = false; } } if (! $isEmpty) { $rows[] = $rowData; } if ($limit !== null && count($rows) >= $limit) { break; } } return $rows; } /** * @param array $columnMapping * @return array> */ public function mapRows(array $rows, array $columnMapping): array { return array_map(function (array $row) use ($columnMapping): array { $mapped = []; foreach ($columnMapping as $field => $header) { if ($header === null || $header === '') { continue; } $mapped[$field] = $row[$header] ?? null; } return $mapped; }, $rows); } /** * @param array> $mappedRows * @return array}> */ public function validatePreview(ImportType $type, array $mappedRows, int $limit = 10): array { $previewRows = array_slice($mappedRows, 0, $limit); $results = []; foreach ($previewRows as $index => $row) { $results[] = [ 'row' => $index + 2, 'errors' => $this->validateRow($type, $row), ]; } return $results; } /** * @param array> $mappedRows */ public function import(ImportType $type, array $mappedRows, ?int $userId = null): ImportResultDto { $imported = 0; $skipped = 0; $failed = 0; $errors = []; foreach ($mappedRows as $index => $row) { $rowNumber = $index + 2; $validationErrors = $this->validateRow($type, $row); if ($validationErrors !== []) { $failed++; $errors[] = "Row {$rowNumber}: " . implode(' ', $validationErrors); continue; } if ($this->isDuplicate($type, $row)) { $skipped++; continue; } try { $this->persistRow($type, $row, $userId); $imported++; } catch (\Throwable $exception) { $failed++; $errors[] = "Row {$rowNumber}: {$exception->getMessage()}"; } } return new ImportResultDto( total: count($mappedRows), imported: $imported, skipped: $skipped, failed: $failed, errors: array_slice($errors, 0, 50), ); } /** * @param array $row * @return array */ private function validateRow(ImportType $type, array $row): array { return match ($type) { ImportType::Employees => $this->employeeTransformer->validate(EmployeeRowDto::fromMappedRow($row)), ImportType::Vacations => $this->vacationTransformer->validate(VacationRowDto::fromMappedRow($row)), ImportType::Bonuses => $this->bonusTransformer->validate(BonusRowDto::fromMappedRow($row)), ImportType::Reports => $this->reportTransformer->validate(ReportRowDto::fromMappedRow($row)), }; } /** * @param array $row */ private function isDuplicate(ImportType $type, array $row): bool { return match ($type) { ImportType::Employees => $this->employeeTransformer->isDuplicate(EmployeeRowDto::fromMappedRow($row)), ImportType::Vacations => $this->vacationTransformer->isDuplicate(VacationRowDto::fromMappedRow($row)), ImportType::Bonuses => $this->bonusTransformer->isDuplicate(BonusRowDto::fromMappedRow($row)), ImportType::Reports => $this->reportTransformer->isDuplicate(ReportRowDto::fromMappedRow($row)), }; } /** * @param array $row */ private function persistRow(ImportType $type, array $row, ?int $userId): void { match ($type) { ImportType::Employees => $this->employeeService->create( $this->employeeTransformer->toAttributes(EmployeeRowDto::fromMappedRow($row)), ), ImportType::Vacations => Vacation::create( $this->vacationTransformer->toAttributes(VacationRowDto::fromMappedRow($row)), ), ImportType::Bonuses => Bonus::create( $this->bonusTransformer->toAttributes(BonusRowDto::fromMappedRow($row)), ), ImportType::Reports => DisciplinaryReport::create( $this->reportTransformer->toAttributes(ReportRowDto::fromMappedRow($row), $userId), ), }; } }