src/Controller/Frontend/VehicleController.php line 767

Open in your IDE?
  1. <?php
  2. namespace App\Controller\Frontend;
  3. use App\Api\RMS\SaleEventDataPersister;
  4. use App\Controller\Controller;
  5. use App\Counter\SalesCounter;
  6. use App\Entity\Annotation;
  7. use App\Entity\ExternalSale;
  8. use App\Entity\Sale;
  9. use App\Entity\SearchAlert;
  10. use App\Entity\Vehicle;
  11. use App\Event\FilterBehaviourEvent;
  12. use App\Form\Type\Frontend\PageLimitType;
  13. use App\Form\Type\Frontend\Popin\VehicleCityLocationType;
  14. use App\Form\Type\Frontend\Popin\VehicleEnergyType;
  15. use App\Form\Type\Frontend\Popin\VehicleGroupType;
  16. use App\Form\Type\Frontend\Popin\VehicleMakerType;
  17. use App\Form\Type\Frontend\Popin\VehicleOptionType;
  18. use App\Form\Type\Frontend\Popin\VehicleSegmenttailleType;
  19. use App\Form\Type\Frontend\RecommandVehicleType;
  20. use App\Form\Type\Frontend\VehicleFreeSearchType;
  21. use App\Form\Type\Frontend\VehicleListSortType;
  22. use App\Form\Type\Frontend\VehicleSearchType;
  23. use App\Live\SocketNotifier;
  24. use App\Mailer\Mailer;
  25. use App\Parameter\Provider;
  26. use App\Repository\AuctionRepository;
  27. use App\Repository\CategoryRepository;
  28. use App\Repository\EnergyRepository;
  29. use App\Repository\OptionRepository;
  30. use App\Repository\PurchaseInstructionRepository;
  31. use App\Repository\SaleEventRepository;
  32. use App\Repository\SaleRepository;
  33. use App\Repository\SelectionRepository;
  34. use App\Repository\TransmissionRepository;
  35. use App\Repository\UserRepository;
  36. use App\Repository\VehicleRepository;
  37. use App\Repository\WikiRepository;
  38. use App\Search\AliasHandler;
  39. use App\Search\SearchAlertConverter;
  40. use App\Search\SearchConverter;
  41. use App\Utils\Slugger;
  42. use Doctrine\ORM\EntityManagerInterface;
  43. use Gedmo\Sluggable\Util\Urlizer;
  44. use Knp\Snappy\Pdf;
  45. use Pagerfanta\Adapter\ArrayAdapter;
  46. use Pagerfanta\Pagerfanta;
  47. use Psr\Log\LoggerInterface;
  48. use Symfony\Component\Form\FormFactoryInterface;
  49. use Symfony\Component\HttpFoundation\JsonResponse;
  50. use Symfony\Component\HttpFoundation\Request;
  51. use Symfony\Component\HttpFoundation\Response;
  52. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  53. use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
  54. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  55. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  56. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  57. use Symfony\Contracts\Translation\TranslatorInterface;
  58. class VehicleController extends Controller
  59. {
  60.     private $offsets;
  61.     public function __construct(
  62.         private readonly SearchConverter $searchConverter,
  63.         private readonly AliasHandler $aliasHandler,
  64.         private readonly EnergyRepository $energyRepository,
  65.         private readonly OptionRepository $optionRepository,
  66.         private readonly SaleRepository $saleRepository,
  67.         private readonly SaleEventRepository $saleEventRepository,
  68.         private readonly VehicleRepository $vehicleRepository,
  69.         private readonly TransmissionRepository $transmissionRepository,
  70.         private readonly FormFactoryInterface $formFactory,
  71.         private readonly SessionInterface $session,
  72.         private readonly Mailer $mailer,
  73.         int $estimationPriceOffset,
  74.         int $argusPriceOffset,
  75.         private readonly string $defaultFrom,
  76.         private readonly LoggerInterface $saleLogger
  77.     ) {
  78.         $this->offsets = [
  79.             'estimation' => $estimationPriceOffset,
  80.             'argus' => $argusPriceOffset,
  81.         ];
  82.     }
  83.     public function resetCriteriaAction(Request $request)
  84.     {
  85.         $request->getSession()->remove('freesearch_criteria');
  86.         $request->getSession()->remove('form_values');
  87.         $request->getSession()->remove('badges');
  88.         $request->getSession()->remove('sale');
  89.         $request->getSession()->remove('event');
  90.         return $this->redirect($this->generateUrl('frontend_vehicle_list'));
  91.     }
  92.     public function indexAction(Request $requestSelectionRepository $selectionRepositoryAuctionRepository $auctionRepository)
  93.     {
  94.         // session cleanup
  95.         if ($this->session->get('sale_results') && !$request->isXmlHttpRequest()) {
  96.             // clear all form session values if sales results list was previously accessed (remove interaction)
  97.             $this->clearSearchFormSession($request->getSession(), false);
  98.             // unset flag
  99.             $request->getSession()->remove('sale_results');
  100.         }
  101.         $saleId $this->session->get('sale'null);
  102.         $eventId $this->session->get('event'null);
  103.         $currentSale null !== $saleId $this->saleRepository->findOneById($saleId) : null;
  104.         $currentEvent $eventId ?? null;
  105.         $limitForm $this->formFactory->create(PageLimitType::class);
  106.         $limitForm->handleRequest($request);
  107.         if ($limitForm->isSubmitted() && $limitForm->isValid()) {
  108.             $this->session->set('max_per_page'$limitForm['nbItems']->getData());
  109.         }
  110.         if ($request->isXmlHttpRequest()) {
  111.             if (null !== $request->query->get('page'null)) {
  112.                 $this->searchConverter->convertToDBCriteria($this->session->get('form_values'));
  113.             } else {
  114.                 $search $request->query->all();
  115.                 if (!isset($search['sale'])) {
  116.                     $this->session->set('sale'null);
  117.                 }
  118.                 if (!isset($search['event'])) {
  119.                     $this->session->set('event'null);
  120.                 }
  121.                 $this->session->set('form_values'$search);
  122.                 $this->session->set('badges'$this->searchConverter->convertToDisplayableValues($search));
  123.                 $this->searchConverter->convertToDBCriteria($search);
  124.             }
  125.             return new Response();
  126.         }
  127.         $freeSearch $this->session->get('freesearch_criteria');
  128.         $criteria = [];
  129.         if (null !== $freeSearch) {
  130.             $criteria $freeSearch;
  131.             $this->session->set('form_values'$criteria['form']);
  132.             $this->session->set('badges'$this->searchConverter->convertToDisplayableValues($criteria['form']));
  133.             unset($criteria['form']);
  134.             $request->getSession()->remove('freesearch_criteria');
  135.         } elseif (null !== $this->session->get('form_values'null)) {
  136.             $criteria $this->searchConverter->convertToDBCriteria($this->session->get('form_values'));
  137.         }
  138.         if (
  139.             isset($search['estimated'])
  140.             || (isset($criteria['estimated']) && $criteria['estimated'])
  141.         ) {
  142.             $estimated true;
  143.         } else {
  144.             $estimated false;
  145.         }
  146.         $user $this->getUser();
  147.         $pager $this->vehicleRepository->getPagerVehicles(
  148.             $criteria,
  149.             $user,
  150.             $estimated,
  151.             $this->offsets,
  152.             $currentSale,
  153.             $this->session->get('dimension''private')
  154.         );
  155.         $pager->setMaxPerPage($this->session->get('max_per_page'Vehicle::PAGER_MAX));
  156.         $currentPage $request->query->get('page'1);
  157.         if ($pager->getNbPages() < $request->query->get('page'1)) {
  158.             if ($pager->getNbResults() > 0) {
  159.                 $currentPage $pager->getNbPages();
  160.             }
  161.         }
  162.         $pager->setCurrentPage($currentPage);
  163.         if (null !== $user) {
  164.             $selections $selectionRepository->findActiveVehicleIdsByUser($user);
  165.         } else {
  166.             $selections $this->session->get('selections', []);
  167.         }
  168.         $sortForm $this->formFactory->create(VehicleListSortType::class, null, [
  169.             'currentSale' => $currentSale,
  170.         ]);
  171.         $nbVehiclesSales 0;
  172.         if (null === $user && 'private' === $this->session->get('dimension''private')) {
  173.             $nbVehiclesSales $this->saleRepository->countVehiclesForActiveByUserAndCustomer($user);
  174.             $sales $this->saleRepository->findActiveByUserAndCustomer($user);
  175.         } else {
  176.             $sales $this->saleRepository->findActiveByUser($user);
  177.         }
  178.         $this->session->set('sale'$currentSale);
  179.         $datesForCurrentSale = [];
  180.         if ($currentSale instanceof ExternalSale && SaleEventDataPersister::PROVIDER_NAME === $currentSale->getProvider()) {
  181.             $dates $auctionRepository->findMinAndMaxClosedAtForSale($currentSale);
  182.             $dates array_shift($dates);
  183.             if ($dates['max_closedAt'] != $dates['min_closedAt']) {
  184.                 $datesForCurrentSale = ['max_closedAt' => new \DateTime($dates['max_closedAt']), 'min_closedAt' => new \DateTime($dates['min_closedAt'])];
  185.             }
  186.         }
  187.         return $this->render('Frontend/vehicle/index.html.twig', [
  188.             'pager' => $pager,
  189.             'nbVehiclesSales' => $nbVehiclesSales,
  190.             'sales' => $sales,
  191.             'currentSale' => $currentSale,
  192.             'currentEvent' => $currentEvent,
  193.             'limitForm' => $limitForm->createView(),
  194.             'sortForm' => $sortForm->createView(),
  195.             'selections' => $selections,
  196.             'showRegistrationBanner' => !$request->cookies->has('registration-banner'),
  197.             'listType' => null !== $user $user->getVehicleListType() : $this->session->get('vehicle_list_type'null),
  198.             'datesForCurrentSale' => $datesForCurrentSale,
  199.         ]);
  200.     }
  201.     public function resultsAction(Request $requestUserRepository $userRepositoryProvider $parameterProvider)
  202.     {
  203.         // session cleanup
  204.         if (!$this->session->get('sale_results')) {
  205.             // first access to results page
  206.             // clear all form session values for a fresh start
  207.             $this->clearSearchFormSession($request->getSession());
  208.             // set flag
  209.             $this->session->set('sale_results'true);
  210.         }
  211.         // Limit form
  212.         $limitForm $this->formFactory->create(PageLimitType::class, null, ['show_96_choice' => false]);
  213.         $limitForm->handleRequest($request);
  214.         if ($limitForm->isSubmitted() && $limitForm->isValid()) {
  215.             $this->session->set('max_per_page'$limitForm['nbItems']->getData());
  216.         }
  217.         // $pagerMax = $this->session->get('max_per_page', Vehicle::PAGER_MAX);
  218.         $pagerMax Vehicle::RESULTS_MAX;
  219.         // search criteria
  220.         if ($request->isXmlHttpRequest()) {
  221.             $this->session->set('form_values'$request->query->all());
  222.             return new JsonResponse(true);
  223.         }
  224.         $criteria $this->searchConverter->convertToDBCriteria($this->session->get('form_values'), VehicleSearchType::FORM_SALE_RESULTS);
  225.         $excluded_winners = [];
  226.         $guaranteeUser $userRepository->findOneByEmail($parameterProvider->getParameterValue('vpautopro.guarantee.user.email'));
  227.         if ($guaranteeUser) {
  228.             $excluded_winners = [$guaranteeUser];
  229.         }
  230.         /**
  231.          * Get only 48 sold vehicules.
  232.          *
  233.          * @see Vehicle::RESULTS_MAX for the limit
  234.          */
  235.         $qb $this->vehicleRepository->getQueryBuilder($criterianulltrue, [], Vehicle::RESULTS_MAXVehicleSearchType::FORM_SALE_RESULTSnull'private'$excluded_winners);
  236.         // If we use “$pager = $this->vehicleRepository->getPaginator($qb);”,
  237.         // the limit of 48 won't be applied on results counter, so we fetch data and use an ArrayAdapter
  238.         $result $qb->getQuery()->getResult();
  239.         $adapter = new ArrayAdapter($result);
  240.         // pager
  241.         $pager = new Pagerfanta($adapter);
  242.         $pager->setMaxPerPage($pagerMax);
  243.         $currentPage $request->query->get('page'1);
  244.         if ($pager->getNbPages() < $currentPage) {
  245.             $currentPage $pager->getNbPages();
  246.         }
  247.         $pager->setCurrentPage($currentPage);
  248.         $sortForm $this->formFactory->create(VehicleListSortType::class, null);
  249.         return $this->render('Frontend/vehicle/results.html.twig', [
  250.             'pager' => $pager,
  251.             'limitForm' => $limitForm->createView(),
  252.             'selections' => [],
  253.             'displayAsResult' => true,
  254.             'sortForm' => $sortForm->createView(),
  255.         ]);
  256.     }
  257.     public function storeSearchAction(Request $requestSaleEventRepository $saleEventRepository)
  258.     {
  259.         // Request coming from the homepage's form (with free search)
  260.         if ('POST' === $request->getMethod()) {
  261.             $form $this->formFactory->create(VehicleFreeSearchType::class, null, ['user' => $this->getUser()]);
  262.             $form->handleRequest($request);
  263.             if ($form->isSubmitted() && $form->isValid()) {
  264.                 $maxKm $form->get('maxKm')->getData() ? (' '.$form->get('maxKm')->getData().'km') : '';
  265.                 $maxPrice $form->get('maxPrice')->getData() ? (' '.$form->get('maxPrice')->getData().'euros') : '';
  266.                 $criteria $this->handleFreeSearch(
  267.                     $form->get('freeSearch')->getData().$maxKm.$maxPrice,
  268.                     $form->get('sale')->getData()
  269.                 );
  270.                 $badges $this->searchConverter->convertToDisplayableValues($criteria['form']);
  271.                 $this->session->set('freesearch_criteria'$criteria);
  272.                 $this->session->set('form_values'$criteria['form']);
  273.                 $this->session->set('badges'$badges);
  274.                 $this->session->set('sale'$form->get('sale')->getData());
  275.             }
  276.         } elseif ('GET' === $request->getMethod()) {
  277.             $search str_replace('-'' '$request->attributes->get('terms'''));
  278.             $sale null;
  279.             $event null;
  280.             $request->getSession()->remove('freesearch_criteria');
  281.             $request->getSession()->remove('form_values');
  282.             $request->getSession()->remove('badges');
  283.             $request->getSession()->remove('sale');
  284.             $request->getSession()->remove('event');
  285.             if ($saleData $request->attributes->get('sale'false)) {
  286.                 if (null !== $sale $this->saleRepository->findVeryNextPhysicalSaleByName($saleData)) {
  287.                     $this->denyAccessUnlessGranted('view'$sale'test');
  288.                     $this->session->set('sale'$sale);
  289.                 } elseif (
  290.                     (null !== $sale $this->saleRepository->findOneById($saleData))
  291.                     && in_array($sale$this->saleRepository->findAllActive())
  292.                 ) {
  293.                     $this->session->set('sale'$sale);
  294.                 } else {
  295.                     $sale = -1;
  296.                 }
  297.             }
  298.             // Fix for unlogged users reaching the vehicle list from an email invitation link, without going through homepage
  299.             if ($sale instanceof Sale && !$request->getSession()->has('dimension')) {
  300.                 $request->getSession()->set('dimension'Sale::TYPE_ONLINE === $sale->getType() ? 'pro' 'private');
  301.             }
  302.             if ($eventData $request->attributes->get('event'false)) {
  303.                 if (null !== $event $saleEventRepository->findOneById($eventData)) {
  304.                     if (in_array($event->getSale(), $this->saleRepository->findAllActive())) {
  305.                         $this->session->set('sale'$event->getSale());
  306.                         $this->session->set('event'$event->getId());
  307.                     }
  308.                 }
  309.             }
  310.             $criteria $this->handleFreeSearch($search$sale$event $event->getId() : null);
  311.             $this->session->set('freesearch_criteria'$criteria);
  312.             $this->session->set('form_values'$criteria['form']);
  313.             $this->session->set('badges'$this->searchConverter->convertToDisplayableValues($criteria['form']));
  314.         }
  315.         return $this->redirect($this->generateUrl('frontend_vehicle_list'));
  316.     }
  317.     public function storeSearchFromAlertAction(Request $requestSearchAlertConverter $searchAlertConverter$alertIdEntityManagerInterface $em)
  318.     {
  319.         /** @var SearchAlert $searchAlert */
  320.         $searchAlert $this->findEntityOr404(SearchAlert::class, [
  321.             'id' => $alertId,
  322.         ], $em);
  323.         $formValues $searchAlertConverter->convertFromSearchAlert($searchAlert);
  324.         $this->session->set('form_values'$formValues);
  325.         $this->session->set('badges'$this->searchConverter->convertToDisplayableValues($formValues));
  326.         return $this->redirect($this->generateUrl('frontend_vehicle_list'));
  327.     }
  328.     public function searchAction(Request $requestCategoryRepository $categoryRepository$formType VehicleSearchType::FORM_DEFAULT)
  329.     {
  330.         $categories $categoryRepository->findAll();
  331.         foreach ($categories as $key => $category) {
  332.             $categories[$category->getNameCanonical()] = $category->getId();
  333.             unset($categories[$key]);
  334.         }
  335.         return $this->render('/Frontend/search.html.twig', [
  336.             'freesearch_form' => $this->formFactory->create(VehicleFreeSearchType::class)->createView(),
  337.             'search_form' => $this->getSearchForm($request->getSession()->get('form_values', []), ['form_type' => $formType])->createView(),
  338.             'categories' => $categories,
  339.             'form_values' => $this->session->get('form_values'),
  340.             'form_type' => $formType,
  341.         ]);
  342.     }
  343.     public function removeCriteriaAction(Request $requestEnergyRepository $energyRepository$field$value)
  344.     {
  345.         $values $this->session->get('form_values', []);
  346.         if ('sale' === $field) {
  347.             $request->getSession()->remove('sale');
  348.             $request->getSession()->remove('event');
  349.             if (isset($values['event'])) {
  350.                 unset($values['event']);
  351.             }
  352.         }
  353.         if ('event' === $field) {
  354.             $request->getSession()->remove('event');
  355.         }
  356.         // Need to reduce keys so that i.e. 'year' empties 'yearMin' AND 'yearMax'
  357.         foreach ($values as $key => $v) {
  358.             if (
  359.                 sprintf('%sMin'$field) === $key
  360.                 || sprintf('%sMax'$field) === $key
  361.                 || sprintf('%sOffset'$field) === $key
  362.             ) {
  363.                 $values[$field] = $values[$key];
  364.                 unset($values[$key]);
  365.             }
  366.         }
  367.         // special case
  368.         if ('energy' === $field
  369.             && ($energy $energyRepository->find($value))) {
  370.             $value $energy->getName();
  371.         }
  372.         if ('driveWheel' === $field) {
  373.             unset($values['fourwheeldrive']);
  374.         }
  375.         // We either want to remove a value among others, or empty the field if there's only one value
  376.         if (isset($values[$field])) {
  377.             if (is_string($values[$field]) && str_contains($values[$field], ',')) {
  378.                 $valueCanonical trim(strtolower((string) $value));
  379.                 $removeValueFct = fn ($val) => trim(strtolower((string) $val)) !== $valueCanonical;
  380.                 $values[$field] = implode(','array_filter(explode(','$values[$field]), $removeValueFct));
  381.             } else {
  382.                 unset($values[$field]);
  383.             }
  384.         }
  385.         $this->session->set('form_values'$values);
  386.         $this->session->set('badges'$this->searchConverter->convertToDisplayableValues($values));
  387.         return $this->redirect($request->headers->get('referer') ?? $this->generateUrl('frontend_vehicle_list'));
  388.     }
  389.     // Vehicle accept a null value in order to avoid exception:
  390.     // “VPAutoBundle\Entity\Vehicle object not found by the ParamConverter annotation.”
  391.     public function viewAction(Request $request,
  392.         EntityManagerInterface $entityManager,
  393.         SocketNotifier $socketNotifier,
  394.         Provider $parameterProvider,
  395.         SelectionRepository $selectionRepository,
  396.         PurchaseInstructionRepository $purchaseInstructionRepository,
  397.         WikiRepository $wikiRepository,
  398.         EventDispatcherInterface $eventDispatcher,
  399.         ?Vehicle $vehicle)
  400.     {
  401.         // ToDo: code should be deleted after optimization
  402.         // $startTime = microtime(true);
  403.         if (!($vehicle instanceof Vehicle)) {
  404.             throw new NotFoundHttpException();
  405.         }
  406.         $this->denyAccessUnlessGranted('view'$vehicle);
  407.         $eventDispatcher->dispatch(new FilterBehaviourEvent($this->getUser(), $vehicle), FilterBehaviourEvent::VEHICLE_BEHAVIOUR);
  408.         $sale $vehicle->getEvent()->getSale();
  409.         if (null === $sale) {
  410.             $request->getSession()->getFlashBag()->add('error''frontend.vehicle.view.notfound.flash.error');
  411.             return $this->redirect($this->generateUrl('frontend_vehicle_list'));
  412.         }
  413.         $this->session->set('vp_auto_active_sale_type'$vehicle->getEvent()->getSale()->getType());
  414.         // For statistics
  415.         $socketNotifier->disable();
  416.         $vehicle->addOneToNbViews();
  417.         $entityManager->flush();
  418.         // Criteo: get the user id
  419.         $userId $this->getUser() ? $this->getUser()->getId() : null;
  420.         $bodyDetails $parameterProvider->getParameterValue('vehicle.body_details''bool');
  421.         if (null !== $this->getUser()) {
  422.             $selection $selectionRepository->findOneBy([
  423.                 'user' => $this->getUser(),
  424.                 'vehicle' => $vehicle,
  425.             ]);
  426.         } else {
  427.             $selection in_array($vehicle->getId(), $this->session->get('selections', [])) ?: null;
  428.         }
  429.         $pi null;
  430.         if ($this->session->get('pi.success'false)) {
  431.             $pi $purchaseInstructionRepository->findOneByUserAndVehicle($this->getUser(), $vehicle);
  432.             $request->getSession()->remove('pi.success');
  433.         }
  434.         $wikis array_filter([
  435.             'maker' => $wikiRepository->findOneBy(['reference' => $vehicle->getMaker(), 'published' => true]),
  436.             'model' => $wikiRepository->findOneByMakerAndReferenceInModel($vehicle->getMaker(), $vehicle->getModelGroup()),
  437.         ]);
  438.         $sales $this->saleRepository->findActiveByUser($this->getUser());
  439.         $annotation $entityManager->getRepository(Annotation::class)->findOneBy([
  440.             'user' => $this->getUser(),
  441.             'vehicle' => $vehicle,
  442.         ]);
  443.         // ToDo: code should be deleted after optimization
  444.         // $endTime = microtime(true);
  445.         // $executionTime = $endTime - $startTime;
  446.         // $this->saleLogger->debug('viewAction execution time is : '.$executionTime.' seconds.');
  447.         return $this->render('Frontend/vehicle/view.html.twig', [
  448.             'bodyDetails' => $bodyDetails,
  449.             'vehicle' => $vehicle,
  450.             'selection' => $selection,
  451.             'userId' => $userId,
  452.             'sales' => $sales,
  453.             'currentSale' => $sale,
  454.             'pi' => $pi,
  455.             'wikis' => $wikis,
  456.             'annotation' => $annotation,
  457.         ]);
  458.     }
  459.     public function printAction(Vehicle $vehicle)
  460.     {
  461.         return $this->render(
  462.             'Frontend/vehicle/pdf_view.html.twig',
  463.             [
  464.                 'vehicle' => $vehicle,
  465.                 'nextVehicle' => null,
  466.                 'prevVehicle' => null,
  467.             ]
  468.         );
  469.     }
  470.     public function makerAutocompleteAction(Request $request)
  471.     {
  472.         if (!$request->isXmlHttpRequest()) {
  473.             throw new AccessDeniedHttpException('Request must be ajax');
  474.         }
  475.         $term $request->request->get('term');
  476.         $formType $this->isReferer($this->generateUrl('frontend_vehicle_results', [], UrlGeneratorInterface::ABSOLUTE_URL)) ?
  477.             VehicleSearchType::FORM_SALE_RESULTS VehicleSearchType::FORM_DEFAULT;
  478.         $criteria $this->searchConverter->convertToDBCriteria($this->session->get('form_values'), $formType);
  479.         unset($criteria['makerSlug']);
  480.         unset($criteria['modelGroupSlug']);
  481.         unset($criteria['sortedby']);
  482.         $results $this->vehicleRepository->getAutocompleteMakerQuery($term$criteria$formType);
  483.         return new JsonResponse($results);
  484.     }
  485.     public function pdfAction(Request $requestPdf $pdf$id$slug)
  486.     {
  487.         $vehicle $this->findVehicle($request$id);
  488.         if (null === $vehicle) {
  489.             return $this->redirect($this->generateUrl('frontend_vehicle_list'));
  490.         }
  491.         $html $this->renderView(
  492.             'Frontend/vehicle/pdf_view.html.twig',
  493.             [
  494.                 'vehicle' => $vehicle,
  495.                 'nextVehicle' => null,
  496.                 'prevVehicle' => null,
  497.             ]
  498.         );
  499.         return new Response(
  500.             $pdf->getOutputFromHtml($html),
  501.             200,
  502.             [
  503.                 'Content-Type' => 'application/pdf',
  504.                 'Content-Disposition' => 'attachment; filename="vehicle.pdf"',
  505.             ]
  506.         );
  507.     }
  508.     public function filteredListPdfAction(Pdf $pdfTranslatorInterface $translator)
  509.     {
  510.         $criteria $this->searchConverter->convertToDBCriteria($this->session->get('form_values'));
  511.         if (isset($criteria['estimated']) && $criteria['estimated']) {
  512.             $estimated true;
  513.         } else {
  514.             $estimated false;
  515.         }
  516.         $vehicles $this->vehicleRepository
  517.             ->getQueryBuilder($criteria$this->getUser(), $estimated$this->offsets)
  518.             ->getQuery()
  519.             ->getResult();
  520.         $html $this->renderView(
  521.             '/Pdf/filtered_list.html.twig',
  522.             [
  523.                 'vehicles' => $vehicles,
  524.             ]
  525.         );
  526.         return new Response(
  527.             $pdf->getOutputFromHtml($html),
  528.             200,
  529.             [
  530.                 'Content-Type' => 'application/pdf',
  531.                 'Content-Disposition' => 'attachment; filename='.$translator->trans('pdf.export.search.filename').'.pdf',
  532.             ]
  533.         );
  534.     }
  535.     public function compareAction(Request $requestTranslatorInterface $translatorPdf $pdf)
  536.     {
  537.         $ids json_decode((string) $request->get('vp_auto_vehicle_select', []));
  538.         $vehicles = [];
  539.         if (count($ids) > 0) {
  540.             foreach ($ids as $id) {
  541.                 $vehicles[] = $this->findVehicle($request$id);
  542.             }
  543.             $options = [];
  544.             foreach ($vehicles as $vehicle) {
  545.                 $vehicleOptions = [];
  546.                 foreach ($vehicle->getOptions() as $vehicleOption) {
  547.                     $vehicleOptions[] = '<p>'.$vehicleOption->getName().'</p>';
  548.                 }
  549.                 $vehicleOptions implode(''$vehicleOptions);
  550.                 $options[$vehicle->getId()] = [
  551.                     $translator->trans('frontend.vehicle.view.saleDate') => $vehicle->getEvent()->getSale()->getStartDate() ? $vehicle->getEvent()->getSale()->getStartDate()->format('d/m/Y') : 'N/C',
  552.                     $translator->trans('frontend.vehicle.view.saleLocation') => $vehicle->getEvent()->getSale()->getRoom()->getName(),
  553.                     $translator->trans('frontend.vehicle.index.order') => $vehicle->getLot(),
  554.                     $translator->trans('frontend.vehicle.view.folderId') => $vehicle->getEtincelleId(),
  555.                     $translator->trans('frontend.vehicle.index.makerAndGroup') => $vehicle->getMaker(),
  556.                     $translator->trans('frontend.vehicle.estimate.form.version') => $vehicle->getModelGroup(),
  557.                     $translator->trans('frontend.vehicle.information.type') => $vehicle->getClass(),
  558.                     $translator->trans('frontend.vehicle.estimate.form.first_registration') => $vehicle->getRegistrationDate() ? $vehicle->getRegistrationDate()->format('d/m/Y') : 'N/C',
  559.                     $translator->trans('frontend.vehicle.view.mileage') => $vehicle->getKilometers().' km',
  560.                     $translator->trans('frontend.vehicle.view.vehicleBody') => $vehicle->getSegmentBody(),
  561.                     $translator->trans('frontend.vehicle.view.energy') => $vehicle->getEnergy() ? $vehicle->getEnergy()->getName() : 'NC',
  562.                     $translator->trans('frontend.vehicle.view.horsepower') => $vehicle->getTaxHorsePower(),
  563.                     $translator->trans('frontend.vehicle.information.gearbox') => $vehicle->getGearbox(),
  564.                     'Norme euro' => $vehicle->getValueOfOption('normeeuro'),
  565.                     'Couleur' => $vehicle->getColor() ? $vehicle->getColor()->getName() : 'NC',
  566.                     'Nb. places' => $vehicle->getValueOfOption('places'),
  567.                     'Equipement' => $vehicleOptions,
  568.                     $translator->trans('frontend.vehicle.view.estimate') => != $vehicle->getValueOfOption('cpquote') ? $vehicle->getValueOfOption('cpquote') : '',
  569.                     $translator->trans('frontend.vehicle.index.price') => $vehicle->getStartingPrice().' €', ];
  570.                 if ('' == $options[$vehicle->getId()][$translator->trans('frontend.vehicle.view.estimate')]) {
  571.                     $options[$vehicle->getId()][$translator->trans('frontend.vehicle.index.price')] = '';
  572.                 }
  573.             }
  574.             if ($request->isXmlHttpRequest()) {
  575.                 return $this->render('Frontend/vehicle/compare.html.twig', [
  576.                     'ids' => $ids,
  577.                     'vehicles' => $vehicles,
  578.                     'firstvehicle' => $vehicle,
  579.                     'options' => $options,
  580.                 ]);
  581.             }
  582.             return new Response(
  583.                 $pdf->getOutputFromHtml(
  584.                     $this->renderView(
  585.                         'Frontend/vehicle/compare.html.twig', [
  586.                             'vehicles' => $vehicles,
  587.                             'firstvehicle' => $vehicle,
  588.                             'options' => $options,
  589.                         ])
  590.                 ),
  591.                 200,
  592.                 [
  593.                     'Content-Type' => 'application/pdf',
  594.                     'Content-Disposition' => 'attachment; filename="comparatif.pdf"',
  595.                 ]
  596.             );
  597.         }
  598.         return new Response(''500);
  599.     }
  600.     public function getMakersAction(Request $request$formType VehicleSearchType::FORM_DEFAULT)
  601.     {
  602.         return $this->handleAjaxSearchForm($request$formTypeVehicleMakerType::class, 'maker''maker');
  603.     }
  604.     public function getCityLocationsAction(Request $request$formType VehicleSearchType::FORM_DEFAULT)
  605.     {
  606.         if ($request->isXmlHttpRequest()) {
  607.             $form $this->formFactory->create(
  608.                 VehicleCityLocationType::class,
  609.                 [],
  610.                 ['form_type' => intval($formType)]
  611.             );
  612.             return $this->render('Frontend/vehicle/popin_search.html.twig', [
  613.                 'search_form' => $form->createView(),
  614.             ]);
  615.         }
  616.         throw new AccessDeniedHttpException('Request must be ajax');
  617.     }
  618.     public function getModelsAction(Request $request$formType VehicleSearchType::FORM_DEFAULT)
  619.     {
  620.         return $this->handleAjaxSearchForm($request$formTypeVehicleGroupType::class, 'group''modelGroup');
  621.     }
  622.     public function getSegmentTaillesAction(Request $request$formType VehicleSearchType::FORM_DEFAULT)
  623.     {
  624.         return $this->handleAjaxSearchForm($request$formTypeVehicleSegmenttailleType::class, 'segmenttaille''segmentSize');
  625.     }
  626.     /**
  627.      * Method called through Ajax.
  628.      *
  629.      * @return Response The corresponding options
  630.      */
  631.     public function getOptionsAction(Request $request)
  632.     {
  633.         if ($request->isXmlHttpRequest()) {
  634.             $form $this->formFactory->create(VehicleOptionType::class);
  635.             return $this->render('Frontend/vehicle/popin_search.html.twig', [
  636.                 'search_form' => $form->createView(),
  637.             ]);
  638.         }
  639.         throw new AccessDeniedHttpException();
  640.     }
  641.     public function getEnergiesAction(Request $request$formType VehicleSearchType::FORM_DEFAULT)
  642.     {
  643.         return $this->handleAjaxSearchForm($request$formTypeVehicleEnergyType::class, 'energy''energy');
  644.     }
  645.     public function similarAction(Vehicle $vehicle)
  646.     {
  647.         $vehicles $this->vehicleRepository->findSimilarVehicles($vehicle);
  648.         return $this->render('Frontend/vehicle/similar.html.twig', [
  649.             'vehicles' => $vehicles,
  650.         ]);
  651.     }
  652.     // Used for the first footer column.
  653.     public function getTopMakersAction()
  654.     {
  655.         $sales $this->saleRepository->findActiveByUser($this->getUser());
  656.         $vehicles $this->vehicleRepository->findTopMakersByNbViews(8$sales);
  657.         $items = [];
  658.         foreach ($vehicles as $v) {
  659.             $imageMaker str_replace(' ''-'strtolower((string) $v['maker'])).'.webp';
  660.             $items[] = [
  661.                 'maker' => $v['makerSlug'],
  662.                 'group' => null,
  663.                 'name' => $this->truncateText(ucfirst(strtolower((string) $v['maker'])), 7),
  664.                 'image' => $imageMaker,
  665.                 'cnt' => $this->vehicleRepository->countByMakerAndModel($v['maker'], $sales),
  666.             ];
  667.         }
  668.         return $this->render('frontend/footer_lists.html.twig', [
  669.             'items' => $items,
  670.             'breakpoint' => 4,
  671.             'images' => true,
  672.             'horizontal' => true,
  673.         ]);
  674.     }
  675.     // Used for the second footer column.
  676.     public function getTopModelsAction()
  677.     {
  678.         $sales $this->saleRepository->findActiveByUser($this->getUser());
  679.         $vehicles $this->vehicleRepository->findTopModelsByNbViews(10$sales);
  680.         $items = [];
  681.         foreach ($vehicles as $v) {
  682.             $items[] = [
  683.                 'maker' => $v['makerSlug'],
  684.                 'group' => $v['modelGroupSlug'],
  685.                 'name' => $this->truncateText($v['modelGroup'], 15),
  686.                 'cnt' => $this->vehicleRepository->countByMakerAndModel($v['maker'], $sales$v['modelGroup']),
  687.             ];
  688.         }
  689.         return $this->render('frontend/footer_lists.html.twig', [
  690.             'items' => $items,
  691.             'breakpoint' => 5,
  692.             'images' => false,
  693.             'horizontal' => false,
  694.         ]);
  695.     }
  696.     public function recommandAction(Request $requestTranslatorInterface $translatorVehicle $vehicle)
  697.     {
  698.         $form $this->formFactory->create(RecommandVehicleType::class);
  699.         $form->handleRequest($request);
  700.         if ($form->isSubmitted() && $form->isValid()) {
  701.             $data $form->getData();
  702.             $this->mailer->send(
  703.                 $this->renderView(
  704.                     '/Mail/recommand.html.twig',
  705.                     [
  706.                         'data' => $data,
  707.                         'vehicle' => $vehicle,
  708.                     ]
  709.                 ),
  710.                 '[VPAuto] '.$data['senderName'].' vous recommande ce véhicule',
  711.                 $data['recipientEmail'],
  712.                 $this->defaultFrom
  713.             );
  714.             $this->session->getFlashbag()->add('success'$translator->trans('frontend.vehicle.recommand.success'));
  715.             return $this->redirect($this->generateUrl('frontend_vehicle_view', [
  716.                 'id' => $vehicle->getId(),
  717.                 'slug' => $vehicle->getSlug(),
  718.             ]));
  719.         }
  720.         return $this->render('Frontend/vehicle/recommand.html.twig', [
  721.             'vehicle' => $vehicle,
  722.             'form' => $form->createView(),
  723.         ]);
  724.     }
  725.     public function switchListTypeAction(Request $requestEntityManagerInterface $entityManager$type)
  726.     {
  727.         $user $this->getUser();
  728.         if (!in_array($type, ['Liste''Mosaique'])) {
  729.             throw new \Exception('Unknown list type');
  730.         }
  731.         if (null !== $user) {
  732.             $user->setVehicleListType($type);
  733.             $entityManager->flush();
  734.         } else {
  735.             $this->session->set('vehicle_list_type'$type);
  736.         }
  737.         if (null !== $request->headers->get('referer')) {
  738.             return $this->redirect($request->headers->get('referer'));
  739.         }
  740.         return $this->redirect($this->generateUrl('frontend_vehicle_list'));
  741.     }
  742.     public function view3dAction(Vehicle $vehicle)
  743.     {
  744.         return $this->render('/Frontend/landing/index-3d.html.twig', [
  745.             'vehicle' => $vehicle,
  746.         ]);
  747.     }
  748.     public function salesCounterAction(SalesCounter $salesCounter)
  749.     {
  750.         $salesCount $salesCounter->getSalesCount();
  751.         return $this->render('Frontend/vehicle/sales_counter.html.twig', [
  752.             'salesCount' => $salesCount,
  753.         ]);
  754.     }
  755.     private function handleAjaxSearchForm(Request $request$formType$formClassstring $queryKeystring $sessionKey)
  756.     {
  757.         if ($request->isXmlHttpRequest()) {
  758.             if (VehicleSearchType::FORM_ALERT_RESULTS == $formType) {
  759.                 $criteria $this->session->get('searchalert_values');
  760.                 $criteria[$sessionKey] = $request->query->get($queryKey);
  761.                 $this->session->set('searchalert_values'$criteria);
  762.             }
  763.             $form $this->formFactory->create(
  764.                 $formClass,
  765.                 [],
  766.                 ['form_type' => intval($formType)]
  767.             );
  768.             return $this->render('Frontend/vehicle/popin_search.html.twig', [
  769.                 'search_form' => $form->createView(),
  770.             ]);
  771.         }
  772.         throw new AccessDeniedHttpException('Request must be ajax');
  773.     }
  774.     private function getSearchForm(array $values = [], array $options = [])
  775.     {
  776.         $form $this->formFactory->create(VehicleSearchType::class, null$options);
  777.         $form->setData($values);
  778.         return $form;
  779.     }
  780.     /**
  781.      * This method handles the free search form on the homepage
  782.      * You can consider it a V1, dirty and non-optimized, but hey, it works.
  783.      *
  784.      * @param string     $freeSearch The sentence-like search
  785.      * @param string     $saleId     The sale's id
  786.      * @param mixed|null $eventId
  787.      *
  788.      * @return array Criterias for the search
  789.      */
  790.     private function handleFreeSearch($freeSearch$saleId null$eventId null)
  791.     {
  792.         $criteria = [];
  793.         $criteria['form'] = [];
  794.         $found = [];
  795.         if (!empty($saleId)) {
  796.             if (null !== $sale $this->saleRepository->findOneById($saleId)) {
  797.                 $criteria['sale'] = $sale;
  798.                 $criteria['form']['sale'] = $sale;
  799.                 if ($sale instanceof ExternalSale && SaleEventDataPersister::PROVIDER_NAME === $sale->getProvider()) {
  800.                     $criteria['sortedby'] = 'a.closedAt:ASC';
  801.                     $criteria['form']['sort'] = 'a.closedAt:ASC';
  802.                 }
  803.             } else {
  804.                 $criteria['sale'] = -1;
  805.             }
  806.         }
  807.         if (
  808.             !empty($eventId)
  809.             && null !== $event $this->saleEventRepository->findOneById($eventId)
  810.         ) {
  811.             $criteria['event'] = $eventId;
  812.             $criteria['form']['event'] = $eventId;
  813.         }
  814.         if (null === $freeSearch or '' === $freeSearch) {
  815.             return $criteria;
  816.         }
  817.         // Let's... handle... the free search...
  818.         /**
  819.          * This will replace synonyms by their db equivalent (ex: "Diesel" becomes "Gazole")
  820.          * Why is this here? Well, we're comparing user input to exact, fixed words, so...
  821.          *
  822.          * Check the search_alias table for current content / adding new aliases
  823.          */
  824.         $freeSearch $this->aliasHandler->handle($freeSearch);
  825.         /*
  826.          * This will check if any of the terms are enclosed with "",
  827.          * meaning they represent a single term and should be read as such
  828.          * The trick is to replace any space within with a dash
  829.          */
  830.         preg_match_all('#"[\w\s-]+"#'$freeSearch$matches);
  831.         foreach ($matches[0] as $compound) {
  832.             $freeSearch str_replace($compoundstr_replace(['"'' '], ['''-'], $compound), $freeSearch);
  833.         }
  834.         /*
  835.          * Pattern for: "de 1000 euros à 2000 euros", "de 1000€ à 2000€"...
  836.          */
  837.         if (preg_match('#de\s([0-9]+)\s?(?:€|euros?)?\s(?:a|à)\s([0-9]+)\s?(?:€|euros?)#i'$freeSearch$matches)) {
  838.             $criteria['price']['min'] = (int) $matches[1];
  839.             $criteria['price']['max'] = (int) $matches[2];
  840.             $criteria['form']['budget']['min'] = (int) $matches[1];
  841.             $criteria['form']['budget']['max'] = (int) $matches[2];
  842.             $criteria['form']['budgetMax'] = (int) $matches[2];
  843.             $found['budget'] = true;
  844.             $freeSearch str_replace($matches[0], ''$freeSearch);
  845.             $freeSearch str_replace('  '' '$freeSearch);
  846.         }
  847.         /*
  848.          * Pattern for: "1000€", "1000euros", "1000 euro"...
  849.          */
  850.         if (!isset($found['budget']) && preg_match('#([0-9]+)\s?(?:€|euros?)#i'$freeSearch$matches)) {
  851.             $criteria['price']['min'] = 0;
  852.             $criteria['price']['max'] = (int) $matches[1];
  853.             $criteria['form']['budget']['min'] = 0;
  854.             $criteria['form']['budget']['max'] = (int) $matches[1];
  855.             $criteria['form']['budgetMax'] = (int) $matches[1];
  856.             $found['budget'] = true;
  857.             $freeSearch str_replace($matches[0], ''$freeSearch);
  858.             $freeSearch str_replace('  '' '$freeSearch);
  859.         }
  860.         /*
  861.          * Pattern for: "50000km", "50000 kilomètres", "50000 kms"...
  862.          */
  863.         if (preg_match('#([0-9]+)\s?k(?:ilo)?m(?:(?:e|è)tre)?s?#i'$freeSearch$matches)) {
  864.             $criteria['kilometers']['min'] = 0;
  865.             $criteria['kilometers']['max'] = (int) $matches[1];
  866.             $criteria['form']['kilometers']['min'] = 0;
  867.             $criteria['form']['kilometers']['max'] = (int) $matches[1];
  868.             $criteria['form']['kilometersMax'] = (int) $matches[1];
  869.             $found['kilometers'] = true;
  870.             $freeSearch str_replace($matches[0], ''$freeSearch);
  871.             $freeSearch str_replace('  '' '$freeSearch);
  872.         }
  873.         /*
  874.          * Pattern for: "#564512"...
  875.          */
  876.         if (preg_match('/\#([0-9]+)/i'$freeSearch$matches)) {
  877.             $criteria['etincelleId'] = (int) $matches[1];
  878.             $found['etincelleId'] = true;
  879.             $freeSearch str_replace($matches[0], ''$freeSearch);
  880.             $freeSearch str_replace('  '' '$freeSearch);
  881.         }
  882.         /*
  883.          * Pattern for: "lot(s) phare(s)"
  884.          */
  885.         if (preg_match('/lots? phares?/i'$freeSearch$matches)) {
  886.             $criteria['highlight'] = true;
  887.             $criteria['form']['highlight'] = true;
  888.             $found['highlight'] = true;
  889.             $freeSearch str_replace($matches[0], ''$freeSearch);
  890.             $freeSearch str_replace('  '' '$freeSearch);
  891.         }
  892.         /*
  893.          * Pattern for: "non roulant"
  894.          */
  895.         if (false !== stripos($freeSearch'non roulant')) {
  896.             $criteria['broken'] = true;
  897.             $criteria['form']['broken'] = true;
  898.             $found['broken'] = true;
  899.             $freeSearch str_replace('non roulant'''$freeSearch);
  900.             $freeSearch str_replace('  '' '$freeSearch);
  901.         }
  902.         /*
  903.          * Pattern for: "critair1, critair2, ..."
  904.          */
  905.         if (preg_match('/critair([0-5])/i'$freeSearch$matches)) {
  906.             $criteria['critair'] = (int) $matches[1];
  907.             $criteria['form']['critair'] = (int) $matches[1];
  908.             $found['critair'] = true;
  909.             $freeSearch str_replace($matches[0], ''$freeSearch);
  910.             $freeSearch str_replace('  '' '$freeSearch);
  911.         }
  912.         /*
  913.          * Pattern for location location in the north of france
  914.          * /rechercher/north-france
  915.          */
  916.         if (preg_match('/north france/i'$freeSearch$matches)) {
  917.             $arr array_column($this->vehicleRepository->findDistinctCitiesByZipCode(['59''62''80''02''08''51''76''60''27''95''78''91''77''93''94''92''75']), 'cityLocation');
  918.             $criteria['cityLocation'] = $arr;
  919.             $criteria['form']['cityLocation'] = implode(','$arr);
  920.             $found['cityLocation'] = true;
  921.             $freeSearch str_replace($matches[0], ''$freeSearch);
  922.             $freeSearch str_replace('  '' '$freeSearch);
  923.         }
  924.         $terms array_filter(explode(' 'trim($freeSearch)));
  925.         foreach ($terms as $key => $term) {
  926.             $terms[$key] = str_replace('-'' '$term);
  927.         }
  928.         if ('' !== $freeSearch) {
  929.             foreach ($terms as $term) {
  930.                 // Discard terms too short to be of importance
  931.                 if (strlen($term)) {
  932.                     continue;
  933.                 }
  934.                 // Category
  935.                 if (
  936.                     !isset($found['category'])
  937.                     && (null !== $category $this->vehicleRepository->findDistinctActiveCategoryLike(Urlizer::urlize($term)))
  938.                 ) {
  939.                     $criteria['category'] = [$category['id']];
  940.                     $criteria['form']['category'] = $category['id'];
  941.                     $found['category'] = true;
  942.                     continue;
  943.                 }
  944.                 // Maker
  945.                 if (
  946.                     !isset($found['maker'])
  947.                     && (null !== $maker $this->vehicleRepository->findDistinctActiveMakerLike(Urlizer::urlize($term)))
  948.                 ) {
  949.                     $criteria['makerSlug'] = [$maker['makerSlug']];
  950.                     $criteria['form']['maker'] = Slugger::prettify($maker['makerSlug']);
  951.                     $found['maker'] = true;
  952.                     continue;
  953.                 }
  954.                 // Group
  955.                 if (
  956.                     [] !== $dbGroups $this->vehicleRepository->findDistinctActiveGroupsLike(Urlizer::urlize($term))
  957.                 ) {
  958.                     // Flatten the returned array of arrays and filter it for maker/model mismatch if any
  959.                     $groups array_filter(array_map(function ($group) use ($criteria) {
  960.                         if (isset($criteria['makerSlug']) && !in_array($group['makerSlug'], $criteria['makerSlug'])) {
  961.                             return null;
  962.                         }
  963.                         return $group['modelGroupSlug'];
  964.                     }, $dbGroups));
  965.                     if (isset($found['modelGroup'])) {
  966.                         $criteria['modelGroupSlug'] = array_intersect($criteria['modelGroupSlug'], $groups);
  967.                     } else {
  968.                         $criteria['modelGroupSlug'] = $groups;
  969.                     }
  970.                     $criteria['form']['modelGroup'] = implode(','array_map(fn ($group) => Slugger::prettify($group), $criteria['modelGroupSlug']));
  971.                     if ([] === $criteria['modelGroupSlug']) {
  972.                         $found['modelGroup'] = false;
  973.                         continue;
  974.                     }
  975.                     $makers array_unique(array_map(fn ($group) => $group['makerSlug'], $dbGroups));
  976.                     if (isset($found['maker'])) {
  977.                         $criteria['makerSlug'] = array_intersect($criteria['makerSlug'], $makers);
  978.                     } else {
  979.                         $criteria['makerSlug'] = $makers;
  980.                         $found['maker'] = true;
  981.                     }
  982.                     $criteria['form']['maker'] = implode(','array_map(fn ($maker) => Slugger::prettify($maker), $criteria['makerSlug']));
  983.                     $found['modelGroup'] = true;
  984.                     continue;
  985.                 }
  986.                 // Segmenttransmission
  987.                 if (
  988.                     !isset($found['segmentTransmission'])
  989.                     && null !== $transmission $this->transmissionRepository->findOneByName(ucfirst(strtolower($term)))
  990.                 ) {
  991.                     $criteria['segmentTransmission'][] = $transmission->getId();
  992.                     $criteria['form']['segmentTransmission'][] = $transmission->getId();
  993.                     $found['segmentTransmission'] = true;
  994.                     continue;
  995.                 }
  996.                 // Segmenttaille
  997.                 if (
  998.                     !isset($found['segmentSize'])
  999.                     && [] !== $dbSegmenttaille $this->vehicleRepository->findDistinctActiveSegmenttailleLike(Urlizer::urlize($term))
  1000.                 ) {
  1001.                     $segmenttailles array_map(fn ($segmenttaille) => $segmenttaille['segmentSizeSlug'], $dbSegmenttaille);
  1002.                     $criteria['segmentSizeSlug'] = $segmenttailles;
  1003.                     $criteria['form']['segmentSize'] = implode(','array_map(fn ($segmenttaille) => Slugger::prettify($segmenttaille), $segmenttailles));
  1004.                     $found['segmentSize'] = true;
  1005.                     continue;
  1006.                 }
  1007.                 // Energy - there can be several, we don't need to stop at one
  1008.                 if (
  1009.                     null !== $energy $this->energyRepository->findOneByName(ucfirst(strtolower($term)))
  1010.                 ) {
  1011.                     $criteria['energy'][] = $energy->getCode();
  1012.                     $criteria['form']['energy'][] = $energy->getName();
  1013.                     $found['energy'] = true;
  1014.                 }
  1015.                 // Options - there can be several, we don't need to stop at one
  1016.                 if (
  1017.                     null !== $option $this->optionRepository->findOneByNameLike($term)
  1018.                 ) {
  1019.                     $criteria['options'][] = $option->getCodeSlug();
  1020.                     $criteria['form']['options'][] = Slugger::prettify($option->getCodeSlug());
  1021.                     $found['options'] = true;
  1022.                 }
  1023.             }
  1024.             if (isset($found['energy'])) {
  1025.                 $criteria['form']['energy'] = implode(', '$criteria['form']['energy']);
  1026.             }
  1027.             if (isset($found['options'])) {
  1028.                 $criteria['form']['options'] = implode(', '$criteria['form']['options']);
  1029.             }
  1030.         }
  1031.         if ([] === $found) {
  1032.             $criteria['sale'] = -1;
  1033.         }
  1034.         return $criteria;
  1035.     }
  1036.     private function findVehicle(Request $request$id)
  1037.     {
  1038.         $vehicle $this->vehicleRepository->find($id);
  1039.         if (null === $vehicle
  1040.             || Vehicle::SALING_STATE_EXCLUS === $vehicle->getSalingState()
  1041.             || Vehicle::SALING_STATE_NO_STARTING_PRICE === $vehicle->getSalingState()
  1042.             || Vehicle::SALING_STATE_SUPPRIME === $vehicle->getSalingState()
  1043.         ) {
  1044.             $request->getSession()->getFlashBag()->add('error''frontend.vehicle.view.notfound.flash.error');
  1045.             return null;
  1046.         }
  1047.         return $vehicle;
  1048.     }
  1049.     /**
  1050.      * If length of $text is higher than or equal to $nb, truncates it and add "...".
  1051.      *
  1052.      * @param string $text
  1053.      * @param int    $nb
  1054.      *
  1055.      * @return string
  1056.      */
  1057.     private function truncateText($text$nb)
  1058.     {
  1059.         return strlen($text) <= $nb $text trim(substr($text0$nb)).'..';
  1060.     }
  1061.     // Clear all search form parameters in session
  1062.     private function clearSearchFormSession(SessionInterface $sessionbool $removeSaleAndEvent true): void
  1063.     {
  1064.         if (true === $removeSaleAndEvent) {
  1065.             $session->remove('sale');
  1066.             $session->remove('event');
  1067.         }
  1068.         $session->remove('form_values');
  1069.         $session->remove('badges');
  1070.     }
  1071. }