> $items * * @return array{rows: list>, skipped: int} */ public static function mapMany(array $items): array { $bySiret = []; $skipped = 0; foreach ($items as $item) { $row = self::mapOne($item); if (null === $row) { ++$skipped; continue; } // Cle = SIRET normalise : une occurrence ulterieure ecrase la // precedente (derniere gagnante). $bySiret[$row['siret']] = $row; } return ['rows' => array_values($bySiret), 'skipped' => $skipped]; } /** * Mappe un item unique. Retourne null si le SIRET est absent ou vide * (ligne inexploitable : pas de cle naturelle pour l'upsert). * * @param array $item * * @return null|array */ public static function mapOne(array $item): ?array { $siret = self::normalizeSiret(self::str($item['Siret'] ?? null)); if (null === $siret) { return null; } return [ 'siret' => $siret, // Nom et Societe sont identiques a la source : une seule colonne. 'name' => self::str($item['Nom'] ?? null) ?? '', 'address' => self::str($item['Adresse'] ?? null), 'postal_code' => self::str($item['CodePostal'] ?? null), 'city' => self::str($item['Ville'] ?? null), 'phone' => self::str($item['Telephone_1'] ?? null), 'department' => self::str($item['Departement'] ?? null), // Statut conserve brut (feed externe, valeurs non contraintes). 'status' => self::str($item['Statut'] ?? null) ?? '', 'validity_date' => self::parseDate(self::str($item['Validite'] ?? null)), ]; } /** * Normalise un SIRET : ne conserve que les chiffres. Null si vide. * La source est "sale" (longueurs variables 7 a 14) : aucune contrainte * de longueur, on stocke les chiffres tels quels. */ public static function normalizeSiret(?string $raw): ?string { if (null === $raw) { return null; } $digits = preg_replace('/\D+/', '', $raw) ?? ''; return '' === $digits ? null : $digits; } /** * Convertit une date "dd/mm/yyyy" en "yyyy-mm-dd". Null si le format ne * correspond pas ou si la date n'est pas un jour calendaire valide * (garde-fou : evite un INSERT en erreur sur une date impossible). */ public static function parseDate(?string $raw): ?string { if (null === $raw || !preg_match('#^(\d{2})/(\d{2})/(\d{4})$#', $raw, $m)) { return null; } $day = (int) $m[1]; $month = (int) $m[2]; $year = (int) $m[3]; if (!checkdate($month, $day, $year)) { return null; } return sprintf('%04d-%02d-%02d', $year, $month, $day); } /** * Trim d'une valeur scalaire ; null si la chaine resultante est vide. */ private static function str(mixed $value): ?string { if (null === $value) { return null; } $trimmed = trim((string) $value); return '' === $trimmed ? null : $trimmed; } }