src/Controller/Buzz/RechercheController.php line 59

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Controller\Buzz;
  4. use App\Services\MetaTag;
  5. use App\Services\Buzz\RechercheService;
  6. use App\Services\Buzz\CanonicalUrlService;
  7. use App\Repository\SecteursActiviteRepository;
  8. use App\Repository\Buzz\BuzzActualitesRepository;
  9. use PhpOffice\PhpSpreadsheet\Spreadsheet;
  10. use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
  11. use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
  12. use Symfony\Component\HttpFoundation\Request;
  13. use Symfony\Component\HttpFoundation\Response;
  14. use Symfony\Component\HttpFoundation\JsonResponse;
  15. use Symfony\Component\HttpFoundation\StreamedResponse;
  16. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  17. class RechercheController extends AbstractController
  18. {
  19.     private MetaTag $metaTag;
  20.     private RechercheService $rechercheService;
  21.     private SecteursActiviteRepository $secteursActiviteRepository;
  22.     private BuzzActualitesRepository $buzzActualitesRepository;
  23.     private CanonicalUrlService $canonicalUrlService;
  24.     /**
  25.      *
  26.      * @param MetaTag $metaTag
  27.      * @param RechercheService $rechercheService
  28.      * @param SecteursActiviteRepository $secteursActiviteRepository
  29.      * @param BuzzActualitesRepository $buzzActualitesRepository
  30.      */
  31.     public function __construct(
  32.         MetaTag                    $metaTag,
  33.         RechercheService           $rechercheService,
  34.         SecteursActiviteRepository $secteursActiviteRepository,
  35.         BuzzActualitesRepository   $buzzActualitesRepository,
  36.         CanonicalUrlService        $canonicalUrlService
  37.     )
  38.     {
  39.         $this->metaTag $metaTag;
  40.         $this->rechercheService $rechercheService;
  41.         $this->secteursActiviteRepository $secteursActiviteRepository;
  42.         $this->buzzActualitesRepository $buzzActualitesRepository;
  43.         $this->canonicalUrlService $canonicalUrlService;
  44.     }
  45.     /**
  46.      * Page de recherche avancée des actualités
  47.      *
  48.      * @param Request $request
  49.      * @param string $codePays
  50.      * @return Response
  51.      */
  52.     public function avancee(Request $requeststring $codePays): Response
  53.     {
  54.         $cleanCodePays str_replace('_'''$codePays);
  55.         $codePaysWithUnderScore '_' $cleanCodePays '_';
  56.         $searchData $this->rechercheService->getSearchData();
  57.         $defaultTypeAnnonce $request->query->get('type_annonce');
  58.         if ($defaultTypeAnnonce === null || $defaultTypeAnnonce === '') {
  59.             $defaultTypeAnnonce '0';
  60.         }
  61.         $request->query->set('type_annonce'$defaultTypeAnnonce);
  62.         $criteres $this->rechercheService->extractSearchCriteria($request);
  63.         $isGroupedBySociete $this->rechercheService->isGroupedBySociete($criteres);
  64.         if ($isGroupedBySociete) {
  65.             $hasExplicitTri $request->query->has('tri') && $request->query->get('tri') !== '';
  66.             if (!$hasExplicitTri) {
  67.                 $criteres['tri'] = '2';
  68.             }
  69.         } else {
  70.             if (!isset($criteres['tri']) || $criteres['tri'] === '' || $criteres['tri'] === null) {
  71.                 $criteres['tri'] = '0';
  72.             }
  73.         }
  74.         $validTriValues = ['0''1''2''3''4''5'];
  75.         if (!in_array($criteres['tri'], $validTriValues)) {
  76.             $criteres['tri'] = $this->rechercheService->isGroupedBySociete($criteres) ? '2' '0';
  77.         }
  78.         $statusAuth $this->rechercheService->getUserAuthStatus($this->getUser());
  79.         $page max(1, (int)$request->query->get('page'1));
  80.         $limit 20;
  81.         $offset = ($page 1) * $limit;
  82.         $rechercheValue trim($request->query->get('recherche'''));
  83.         
  84.         $hasRecherche false;
  85.         if (!empty($rechercheValue) && $rechercheValue !== '1') {
  86.             $hasRecherche true;
  87.         } elseif (!empty($criteres['secteur']) && $criteres['secteur'] != '0') {
  88.             $hasRecherche true;
  89.         } elseif (!empty($criteres['secteur_niv2']) && $criteres['secteur_niv2'] != '0') {
  90.             $hasRecherche true;
  91.         } elseif (!empty($criteres['secteur_niv3']) && $criteres['secteur_niv3'] != '0') {
  92.             $hasRecherche true;
  93.         } elseif (!empty($criteres['localisation']) && $criteres['localisation'] != '0') {
  94.             $hasRecherche true;
  95.         } elseif (!empty($criteres['region'])) {
  96.             $hasRecherche true;
  97.         } elseif (!empty($criteres['type_operation']) && $criteres['type_operation'] != '1') {
  98.             $hasRecherche true;
  99.         } elseif (!empty($criteres['fonds']) && $criteres['fonds'] != '0') {
  100.             $hasRecherche true;
  101.         } elseif (!empty($criteres['code_naf'])) {
  102.             $hasRecherche true;
  103.         } elseif (!empty($criteres['afficher']) && $criteres['afficher'] != '0') {
  104.             $hasRecherche true;
  105.         }
  106.         $nbTotal 0;
  107.         $actualites = [];
  108.         if ($this->rechercheService->isGroupedBySociete($criteres)) {
  109.             $groupedLimit min(5000max($limit 10500)); // Limite adaptative
  110.             $searchResult $this->buzzActualitesRepository->searchAdvancedGroupedBySociete($criteres$codePays$statusAuth$groupedLimit0);
  111.             $criteres['offset'] = $offset;
  112.             $criteres['limit'] = $limit;
  113.             $groupedData $this->rechercheService->buildGroupedRowsForRecherche($searchResult['articles'], $criteres);
  114.             $actualites $groupedData;
  115.             $nbTotal $searchResult['total'];
  116.         } else {
  117.             if ($hasRecherche) {
  118.                 $searchResult $this->rechercheService->executeSearchWithDebug(
  119.                     $criteres,
  120.                     $codePays,
  121.                     $statusAuth,
  122.                     $limit,
  123.                     $offset,
  124.                     true
  125.                 );
  126.                 $actualites $searchResult['actualites'];
  127.                 $nbTotal $searchResult['nbTotal'];
  128.             } else {
  129.                 $emptyCriteres = [
  130.                     'recherche_dans' => '1',
  131.                     'date_filter' => '1',
  132.                     'date_filter_value' => date('d-m-Y'),
  133.                     'afficher' => '0',
  134.                     'type_operation' => '1',
  135.                     'fonds' => '0',
  136.                     'secteur' => '0',
  137.                     'localisation' => '0',
  138.                     'tri' => $criteres['tri'] ?? '0',
  139.                     'type_annonce' => $criteres['type_annonce'] ?? '0'
  140.                 ];
  141.                 $searchResult $this->rechercheService->executeSearchWithDebug(
  142.                     $emptyCriteres,
  143.                     $codePays,
  144.                     $statusAuth,
  145.                     $limit,
  146.                     $offset,
  147.                     true
  148.                 );
  149.                 $actualites $searchResult['actualites'];
  150.                 $nbTotal $searchResult['nbTotal'];
  151.             }
  152.         }
  153.         $nbTotalArticles $hasRecherche $nbTotal $this->buzzActualitesRepository->countTotalActualites($codePays$statusAuth);
  154.         $nbPages = (int) ceil($nbTotal $limit);
  155.         
  156.         $queryParams array_merge($criteres, ['recherche' => $rechercheValue]);
  157.         $paginationLinks $this->canonicalUrlService->getBuzzPaginationLinksWithQuery('buzz_recherche_avancee', [], null$cleanCodePays$page$nbPages$queryParams);
  158.         
  159.         $titreParts = [];
  160.         $descParts = [];
  161.         $motCle trim($criteres['recherche'] ?? $criteres['mot_cle'] ?? '');
  162.         if ($motCle !== '') {
  163.             $titreParts[] = $motCle;
  164.         }
  165.         if (!empty($criteres['secteur']) && isset($searchData['secteursActivite'][$criteres['secteur']])) {
  166.             $titreParts[] = $searchData['secteursActivite'][$criteres['secteur']];
  167.         }
  168.         if (!empty($criteres['localisation'])) {
  169.             $locLabel $this->rechercheService->getTypeOperationLabelForTemplate(null);
  170.             $locMap = [
  171.                 '33' => 'France',
  172.                 '32' => 'Belgique',
  173.                 '41' => 'Suisse',
  174.                 '01' => 'Canada'
  175.             ];
  176.             $titreParts[] = $locMap[$criteres['localisation']] ?? 'International';
  177.         }
  178.         if (!empty($criteres['type_operation']) && $criteres['type_operation'] !== '1') {
  179.             $typeLabel $this->rechercheService->getTypeOperationLabelForTemplate(
  180.                 ['2' => 'fusion_acquisition''3' => 'introduction_bourse''4' => 'levee_fonds''5' => 'liquidation''6' => 'operation_boursiere''7' => 'restructuration'][$criteres['type_operation']] ?? null
  181.             );
  182.             if ($typeLabel) {
  183.                 $titreParts[] = $typeLabel;
  184.             }
  185.         }
  186.         if (!empty($criteres['code_naf'])) {
  187.             $titreParts[] = 'Code NAF: ' $criteres['code_naf'];
  188.         }
  189.         if (!empty($criteres['fonds']) && $criteres['fonds'] !== '0') {
  190.             $titreParts[] = ($criteres['fonds'] === '1') ? 'avec fonds' 'sans fonds';
  191.         }
  192.         $baseTitle "Recherche avancée Buzz";
  193.         $baseDesc "Résultats de recherche des actualités Fusions-Acquisitions et Capital Investissement";
  194.         $dynamicTitle $baseTitle . (count($titreParts) ? ' – ' implode(' · '$titreParts) : '');
  195.         $dynamicDesc $baseDesc . (count($titreParts) ? ' : ' implode(' · '$titreParts) : '') . '. Filtrez par secteur, localisation, type d’opération et fonds.';
  196.         $canonicalUrl $this->canonicalUrlService->getBuzzSearchCanonical($cleanCodePays);
  197.         
  198.         $metaTag $this->metaTag
  199.             ->setTitle($dynamicTitle)
  200.             ->setDescription($dynamicDesc)
  201.             ->setCanonical($canonicalUrl);
  202.             
  203.         $hreflangLinks $this->canonicalUrlService->getBuzzHreflangLinks('buzz_recherche_avancee', [], null$cleanCodePays);
  204.         $searchResultsMessage $this->rechercheService->getSearchResultsMessage($actualites$nbTotal$request->query->has('recherche'));
  205.         $authStatusMessage $this->rechercheService->getAuthStatusMessage($statusAuth);
  206.         $triOptions $this->rechercheService->getTriOptions();
  207.         $baseUrl $this->generateUrl('buzz_recherche_avancee', ['codePays' => $codePaysWithUnderScore]);
  208.         $isExportAllowed $this->rechercheService->isExportAllowed((int)$nbTotal);
  209.         $exportLimit $this->rechercheService->getExportLimit();
  210.         $secteursNiv2 = [];
  211.         $secteursNiv3 = [];
  212.         if (!empty($criteres['secteur']) && $criteres['secteur'] != '0') {
  213.             try {
  214.                 $secteursNiv2Raw $this->secteursActiviteRepository
  215.                     ->getNomSecteurActivitieLevel2ByIdSecteurLevel1((int)$criteres['secteur']);
  216.                 $secteursNiv2 = [=> 'Tous'];
  217.                 foreach ($secteursNiv2Raw as $secteur) {
  218.                     $secteursNiv2[$secteur['idSecteurActivite']] = $secteur['nomSecteurActivite'];
  219.                 }
  220.                 if (!empty($criteres['secteur_niv2']) && $criteres['secteur_niv2'] != '0') {
  221.                     $secteursNiv3Raw $this->secteursActiviteRepository
  222.                         ->getNomSecteurActivitieLevel3ByIdSecteurLevel2((int)$criteres['secteur_niv2']);
  223.                     $secteursNiv3 = [=> 'Tous'];
  224.                     foreach ($secteursNiv3Raw as $secteur) {
  225.                         $secteursNiv3[$secteur['idSecteurActivite']] = $secteur['nomSecteurActivite'];
  226.                     }
  227.                 }
  228.             } catch (\Exception $e) {
  229.                 $secteursNiv2 = [];
  230.                 $secteursNiv3 = [];
  231.             }
  232.         }
  233.         return $this->render('buzz/recherche/avancee.html.twig'array_merge([
  234.             'metaTag' => $metaTag,
  235.             'lang' => 'fr',
  236.             'currentRoute' => $request->get('_route'),
  237.             'codePays' => $cleanCodePays,
  238.             'codePaysWithUnderScore' => $codePaysWithUnderScore,
  239.             'authFrom' => null,
  240.             'actualites' => $actualites,
  241.             'criteres' => $criteres,
  242.             'pays_principaux' => $this->rechercheService->getPaysPrincipaux(),
  243.             'tous_pays' => $this->rechercheService->getTousPays(),
  244.             'regions' => $this->rechercheService->getRegionsFrancaises(),
  245.             'nbTotal' => $nbTotal,
  246.             'nbTotalArticles' => $nbTotalArticles,
  247.             'statusAuth' => $statusAuth,
  248.             'page' => $page,
  249.             'limit' => $limit,
  250.             'nbPages' => $nbPages,
  251.             'hasRecherche' => $request->query->has('recherche'),
  252.             'searchResultsMessage' => $searchResultsMessage,
  253.             'authStatusMessage' => $authStatusMessage,
  254.             'triOptions' => $triOptions,
  255.             'isGroupedBySociete' => $this->rechercheService->isGroupedBySociete($criteres),
  256.             'baseUrl' => $baseUrl,
  257.             'isExportAllowed' => $isExportAllowed,
  258.             'exportLimit' => $exportLimit,
  259.             'secteursNiv2' => $secteursNiv2,
  260.             'secteursNiv3' => $secteursNiv3,
  261.             'hreflangLinks' => $hreflangLinks,
  262.             'paginationLinks' => $paginationLinks,
  263.         ], $searchData));
  264.     }
  265.     /**
  266.      * Export Excel des résultats de recherche
  267.      *
  268.      * @param Request $request
  269.      * @param string $codePays
  270.      * @return Response
  271.      */
  272.     public function export(Request $requeststring $codePays): Response
  273.     {
  274.         $criteres $this->rechercheService->extractSearchCriteria($request);
  275.         $cleanCodePays str_replace('_'''$codePays);
  276.         $statusAuth $this->rechercheService->getUserAuthStatus($this->getUser());
  277.         $exportLimit $this->rechercheService->getExportLimit();
  278.         $searchResult $this->rechercheService->getDataForExport($criteres$cleanCodePays$statusAuth$exportLimit);
  279.         $articles $searchResult['articles'];
  280.         $isGroupedBySociete = isset($criteres['type_annonce']) && $criteres['type_annonce'] === '1';
  281.         if ($isGroupedBySociete) {
  282.             $filename 'export_actualites_groupes_societe_' . (new \DateTimeImmutable())->format('Y-m-d_H-i-s') . '.xlsx';
  283.             $groupedRows $this->rechercheService->getGroupedRowsForExportDisplayPipeline($criteres$cleanCodePays$statusAuth1000);
  284.             $selectedFieldsParam trim((string)$request->query->get('export_fields'''));
  285.             if ($selectedFieldsParam !== '') {
  286.                 $selectedFields array_values(array_filter(array_map('trim'explode(','$selectedFieldsParam))));
  287.                 if (!empty($selectedFields)) {
  288.                     $headers $this->rechercheService->getExportHeaders($selectedFieldstrue);
  289.                     $exportData $this->rechercheService->formatArticlesForExportGrouped($groupedRows$selectedFields);
  290.                 } else {
  291.                     $headers $this->getHeadersForGroupedMode();
  292.                     $exportData $this->prepareDataForGroupedMode($groupedRows);
  293.                 }
  294.             } else {
  295.                 $headers $this->getHeadersForGroupedMode();
  296.                 $exportData $this->prepareDataForGroupedMode($groupedRows);
  297.             }
  298.         } else {
  299.             $filename 'export_actualites_' . (new \DateTimeImmutable())->format('Y-m-d_H-i-s') . '.xlsx';
  300.             $selectedFieldsParam trim((string)$request->query->get('export_fields'''));
  301.             if ($selectedFieldsParam !== '') {
  302.                 $selectedFields array_values(array_filter(array_map('trim'explode(','$selectedFieldsParam))));
  303.                 if (!empty($selectedFields)) {
  304.                     $headers $this->rechercheService->getExportHeaders($selectedFieldsfalse);
  305.                     $basic = ['id_actualite','titre_actualite','raison_sociale'];
  306.                     $onlyBasicRequested = empty(array_diff($selectedFields$basic));
  307.                     if ($onlyBasicRequested) {
  308.                         $source $this->rechercheService->getArticlesForExportDisplayPipeline($criteres$cleanCodePays$statusAuth$exportLimit);
  309.                     } else {
  310.                         $source $articles;
  311.                     }
  312.                     $exportData $this->rechercheService->formatArticlesForExport($source$selectedFields);
  313.                 } else {
  314.                     $headers $this->getHeadersForNormalMode();
  315.                     $exportData $this->prepareDataForNormalMode($articles);
  316.                 }
  317.             } else {
  318.                 $headers $this->getHeadersForNormalMode();
  319.                 $exportData $this->prepareDataForNormalMode($articles);
  320.             }
  321.         }
  322.         return $this->generateExcelResponse($exportData$headers$filename);
  323.     }
  324.     /**
  325.      * Génère la réponse Excel avec PhpSpreadsheet
  326.      *
  327.      * @param array $exportData
  328.      * @param array $headers
  329.      * @param string $filename
  330.      * @return Response
  331.      */
  332.     private function generateExcelResponse(array $exportData, array $headersstring $filename): Response
  333.     {
  334.         $spreadsheet = new Spreadsheet();
  335.         $sheet $spreadsheet->getActiveSheet();
  336.         foreach ($headers as $colIndex => $header) {
  337.             $colLetter Coordinate::stringFromColumnIndex($colIndex 1);
  338.             $sheet->setCellValue($colLetter '1'$header);
  339.             $sheet->getStyle($colLetter '1')->getFont()->setBold(true);
  340.         }
  341.         foreach ($exportData as $rowIndex => $row) {
  342.             foreach ($row as $colIndex => $value) {
  343.                 $colLetter Coordinate::stringFromColumnIndex($colIndex 1);
  344.                 $cellValue is_array($value) ? implode(', '$value) : (string)$value;
  345.                 $sheet->setCellValue($colLetter . ($rowIndex 2), $cellValue);
  346.             }
  347.         }
  348.         foreach (range(1count($headers)) as $colIndex) {
  349.             $colLetter Coordinate::stringFromColumnIndex($colIndex);
  350.             $sheet->getColumnDimension($colLetter)->setAutoSize(true);
  351.         }
  352.         $response = new StreamedResponse(function () use ($spreadsheet) {
  353.             $writer = new Xlsx($spreadsheet);
  354.             $writer->save('php://output');
  355.         });
  356.         $response->headers->set('Content-Type''application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
  357.         $response->headers->set('Content-Disposition''attachment; filename="' $filename '"');
  358.         $response->headers->set('Cache-Control''max-age=0');
  359.         return $response;
  360.     }
  361.     /**
  362.      * En-têtes pour le mode groupé par société
  363.      *
  364.      * @return array
  365.      */
  366.     private function getHeadersForGroupedMode(): array
  367.     {
  368.         return [
  369.             'Acquéreur',
  370.             'Nombre d\'opération',
  371.             'Dernière opération',
  372.             'Date de dernière opération',
  373.             'SIREN de société',
  374.             'Code NAF',
  375.             'Siège',
  376.             'Code postal',
  377.             'Ville',
  378.             'Région',
  379.             'Pays',
  380.             'Téléphone',
  381.             'Site internet',
  382.             'Résumé de l\'activité',
  383.             'Année des éléments chiffrés',
  384.             'Chiffre d\'affaires (k euros)',
  385.             'Nombre de personnes',
  386.             'Nom de l\'expert',
  387.             'Adresse Expert',
  388.             'Code postal Expert',
  389.             'Contact 2',
  390.             'Ville Expert',
  391.             'Téléphone Expert',
  392.             'Contact 1',
  393.             'Lien fiche annuaire expert'
  394.         ];
  395.     }
  396.     /**
  397.      * En-têtes pour le mode normal
  398.      *
  399.      * @return array
  400.      */
  401.     private function getHeadersForNormalMode(): array
  402.     {
  403.         return [
  404.             'ID d\'actualité',
  405.             'Titre de l\'actualité',
  406.             'Nom de société',
  407.             'SIREN de société',
  408.             'Code NAF',
  409.             'Code postal',
  410.             'Région',
  411.             'Téléphone',
  412.             'Résumé de l\'activité',
  413.             'Chiffre d\'affaires (k euros)',
  414.             'Informations concernant l\'expert',
  415.             'Date opération',
  416.             'Type d\'opération',
  417.             'Type société',
  418.             'Siège',
  419.             'Ville',
  420.             'Pays',
  421.             'Site internet',
  422.             'Année des éléments chiffrés',
  423.             'Nombre de personnes'
  424.         ];
  425.     }
  426.     /**
  427.      * Prépare les données pour le mode groupé
  428.      *
  429.      * @param array $articles
  430.      * @return array
  431.      */
  432.     private function prepareDataForGroupedMode(array $articles): array
  433.     {
  434.         $exportData = [];
  435.         foreach ($articles as $article) {
  436.             $exportData[] = [
  437.                 $article['raison_sociale'] ?? '',
  438.                 $article['nb_operations'] ?? '',
  439.                 $article['titre_actualite'] ?? '',
  440.                 $article['date_derniere_operation'] ?? '',
  441.                 $article['numero_siren'] ?? '',
  442.                 $article['code_naf'] ?? '',
  443.                 $article['adresse'] ?? '',
  444.                 $article['code_postal'] ?? '',
  445.                 $article['ville'] ?? '',
  446.                 $article['id_region'] ?? '',
  447.                 $article['id_pays'] ?? '',
  448.                 $article['telephone'] ?? '',
  449.                 $article['site_internet'] ?? '',
  450.                 $article['resume_activite'] ?? '',
  451.                 $article['annee_n'] ?? '',
  452.                 $article['ca_n'] ?? '',
  453.                 $article['nb_personnes_n'] ?? '',
  454.                 $article['nom_expert'] ?? '',
  455.                 $article['adresse_expert'] ?? '',
  456.                 $article['code_postal_expert'] ?? '',
  457.                 $article['contact2_expert'] ?? '',
  458.                 $article['ville_expert'] ?? '',
  459.                 $article['telephone_expert'] ?? '',
  460.                 $article['contact1_expert'] ?? '',
  461.                 $article['lien_expert'] ?? ''
  462.             ];
  463.         }
  464.         return $exportData;
  465.     }
  466.     /**
  467.      * Prépare les données pour le mode normal
  468.      *
  469.      * @param array $articles
  470.      * @return array
  471.      */
  472.     private function prepareDataForNormalMode(array $articles): array
  473.     {
  474.         $exportData = [];
  475.         foreach ($articles as $article) {
  476.             $exportData[] = [
  477.                 $article['id_actualite'] ?? '',
  478.                 $article['titre_actualite'] ?? '',
  479.                 $article['raison_sociale'] ?? '',
  480.                 $article['numero_siren'] ?? '',
  481.                 $article['code_naf'] ?? '',
  482.                 $article['code_postal'] ?? '',
  483.                 $article['id_region'] ?? '',
  484.                 $article['telephone'] ?? '',
  485.                 $article['resume_activite'] ?? '',
  486.                 $article['ca_n'] ?? '',
  487.                 $article['nom_expert'] ?? '',
  488.                 $article['date_operation'] ?? '',
  489.                 $article['type_operation'] ?? '',
  490.                 $article['nom_role'] ?? '',
  491.                 $article['adresse'] ?? '',
  492.                 $article['ville'] ?? '',
  493.                 $article['id_pays'] ?? '',
  494.                 $article['site_internet'] ?? '',
  495.                 $article['annee_n'] ?? '',
  496.                 $article['nb_personnes_n'] ?? ''
  497.             ];
  498.         }
  499.         return $exportData;
  500.     }
  501.     /**
  502.      * Route AJAX pour récupérer la liste des pays
  503.      *
  504.      * @param Request $request
  505.      * @param string $codePays
  506.      * @return JsonResponse
  507.      */
  508.     public function getListePays(Request $requeststring $codePays): JsonResponse
  509.     {
  510.         $listePays $this->rechercheService->getListePaysFormatted();
  511.         return new JsonResponse([
  512.             'success' => true,
  513.             'listePays' => $listePays
  514.         ]);
  515.     }
  516. }