authorize('create', [Releve::class, $source]); $source->load('sourceType.fields'); return view('sources.import', compact('source')); } public function store(Request $request, Source $source): RedirectResponse { $this->authorize('create', [Releve::class, $source]); $request->validate([ 'fichier' => ['required', 'file', 'mimes:csv,txt', 'max:10240'], ]); $source->load('sourceType.fields'); $fieldsByLabel = $source->sourceType->fields->keyBy('label'); $path = $request->file('fichier')->getRealPath(); $handle = fopen($path, 'r'); // Suppression du BOM UTF-8 éventuel $bom = fread($handle, 3); if ($bom !== "\xEF\xBB\xBF") { rewind($handle); } // Détection automatique du séparateur (; ou ,) $firstLine = fgets($handle); rewind($handle); if ($bom === "\xEF\xBB\xBF") { fread($handle, 3); } $sep = substr_count($firstLine, ';') >= substr_count($firstLine, ',') ? ';' : ','; $header = fgetcsv($handle, 0, $sep); if (! $header) { fclose($handle); return back()->with('error', 'Fichier CSV vide ou invalide.'); } $header = array_map('trim', $header); $imported = 0; $userId = auth()->id(); while (($line = fgetcsv($handle, 0, $sep)) !== false) { if (count(array_filter($line, fn ($v) => $v !== '')) === 0) { continue; } $data = []; foreach ($header as $i => $label) { $field = $fieldsByLabel->get($label); if (! $field) { continue; } $data[$field->name] = $this->parseValue(trim($line[$i] ?? ''), $field->type); } $source->releves()->create([ 'data' => $data, 'created_by' => $userId, 'updated_by' => $userId, ]); $imported++; } fclose($handle); if ($imported === 0) { return back()->with('error', 'Aucun relevé importé — vérifiez que les en-têtes correspondent aux libellés des champs.'); } return redirect()->route('sources.releves.index', $source) ->with('success', "{$imported} relevé(s) importé(s) avec succès."); } private function parseValue(string $raw, FieldType $type): mixed { return match ($type) { FieldType::Boolean => in_array(mb_strtolower($raw), ['oui', 'yes', '1', 'true'], true), FieldType::Number => $raw !== '' ? (float) str_replace(',', '.', $raw) : null, FieldType::Date => $this->parseDate($raw), FieldType::Place => $this->parsePlace($raw), default => $raw !== '' ? $raw : null, }; } private function parseDate(string $raw): array { if ($raw === '') { return ['valeur' => null, 'calendrier' => 'gregorien']; } // Format export : "YYYY-MM-DD" ou "YYYY-MM-DD (calendrier)" if (preg_match('/^(.+?)\s*\((\w+)\)\s*$/', $raw, $m)) { return ['valeur' => trim($m[1]), 'calendrier' => trim($m[2])]; } return ['valeur' => $raw, 'calendrier' => 'gregorien']; } private function parsePlace(string $raw): ?array { if ($raw === '') { return null; } $like = DbCompat::like(); $lieu = Lieu::where('nom_long', $raw)->first(['id', 'nom_long']) ?? Lieu::where('nom_long', $like, $raw . '%')->first(['id', 'nom_long']); return $lieu ? ['id' => $lieu->id, 'nom_long' => $lieu->nom_long] : ['id' => null, 'nom_long' => $raw]; } }