authorize('view', $source); $source->load(['sourceType.fields', 'releves']); $fields = $source->sourceType->fields->sortBy('order'); $handle = fopen('php://temp', 'r+'); // BOM UTF-8 pour la compatibilité Excel fwrite($handle, "\xEF\xBB\xBF"); // En-tête fputcsv($handle, $fields->pluck('label')->toArray(), ';'); // Lignes foreach ($source->releves as $releve) { $row = []; foreach ($fields as $field) { $val = $releve->data[$field->name] ?? null; $row[] = $this->formatCsvValue($val, $field->type); } fputcsv($handle, $row, ';'); } rewind($handle); $csv = stream_get_contents($handle); fclose($handle); $filename = $this->sanitizeFilename($source->nom) . '.csv'; return response($csv, 200, [ 'Content-Type' => 'text/csv; charset=UTF-8', 'Content-Disposition' => "attachment; filename=\"{$filename}\"", ]); } /** Export de tous les relevés d'une source */ public function source(Source $source): Response { $this->authorize('view', $source); $gedcomContent = $this->gedcom->exportSource($source); $filename = $this->sanitizeFilename($source->nom) . '.ged'; return response($gedcomContent, 200, [ 'Content-Type' => 'text/plain; charset=UTF-8', 'Content-Disposition' => "attachment; filename=\"{$filename}\"", ]); } /** Export depuis les résultats de recherche (avec les mêmes filtres) */ public function recherche(Request $request): Response { $user = auth()->user(); $query = Releve::with(['source.sourceType', 'createur']) ->whereHas('source', function ($q) use ($user, $request) { if (! $user->isSectionManager()) { $assignedIds = $user->sourcesAssignees()->pluck('sources.id'); $q->where(function ($sq) use ($assignedIds) { $sq->where('status', SourceStatus::Termine) ->orWhereIn('id', $assignedIds); }); } if ($request->filled('source_type_id')) { $q->where('source_type_id', $request->integer('source_type_id')); } }); // $request déjà dans le use() if ($request->filled('q')) { $q = trim($request->get('q')); $like = DbCompat::like(); $fts = DbCompat::ftsRaw(); $query->where(function ($wq) use ($q, $like, $fts) { $wq->where('nom', $like, "%{$q}%") ->orWhere('prenom', $like, "%{$q}%") ->orWhere('date_evenement', $like, "%{$q}%"); if ($fts) { $wq->orWhereRaw($fts, [$q]); } }); } if ($request->filled('lieu_id')) { $rows = DB::select(" WITH RECURSIVE descendants AS ( SELECT id, nom FROM lieux WHERE id = ? UNION ALL SELECT l.id, l.nom FROM lieux l INNER JOIN descendants d ON l.lieu_parent_id = d.id ) SELECT DISTINCT nom FROM descendants WHERE nom IS NOT NULL ", [$request->integer('lieu_id')]); $noms = collect($rows)->pluck('nom')->filter(); if ($noms->isNotEmpty()) { $pattern = $noms->map(fn ($n) => preg_quote($n, '/'))->join('|'); $query->whereRaw(DbCompat::jsonRegexRaw('data'), [$pattern]); } } if ($request->filled('annee_debut')) { $query->whereRaw('date_evenement >= ?', [$request->integer('annee_debut') . '-01-01']); } if ($request->filled('annee_fin')) { $query->whereRaw('date_evenement <= ?', [$request->integer('annee_fin') . '-12-31']); } $releves = $query->orderByRaw(DbCompat::nullsLast('nom'))->get(); if ($releves->isEmpty()) { return back()->with('error', 'Aucun relevé à exporter.'); } $titre = $request->filled('q') ? 'Recherche_' . $request->get('q') : 'Export'; $gedcomContent = $this->gedcom->exportReleves($releves, $titre); $filename = $this->sanitizeFilename($titre) . '.ged'; return response($gedcomContent, 200, [ 'Content-Type' => 'text/plain; charset=UTF-8', 'Content-Disposition' => "attachment; filename=\"{$filename}\"", ]); } private function formatCsvValue(mixed $val, FieldType $type): string { if ($val === null || $val === '') { return ''; } return match ($type) { FieldType::Boolean => $val ? 'Oui' : 'Non', FieldType::Date => is_array($val) ? trim(($val['valeur'] ?? '') . ( ! empty($val['calendrier']) && $val['calendrier'] !== 'gregorien' ? ' (' . $val['calendrier'] . ')' : '' )) : (string) $val, FieldType::Place => is_array($val) ? ($val['nom_long'] ?? '') : (string) $val, default => (string) $val, }; } private function sanitizeFilename(string $name): string { $name = iconv('UTF-8', 'ASCII//TRANSLIT', $name) ?: $name; return preg_replace('/[^a-zA-Z0-9_-]/', '_', $name); } }