src/Controller/InfoautoController.php line 137

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\Car;
  4. use App\Repository\InfoautoTokensRepository;
  5. use Doctrine\Persistence\ManagerRegistry;
  6. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  7. use Symfony\Component\HttpFoundation\JsonResponse;
  8. use Symfony\Component\HttpFoundation\Response;
  9. use Symfony\Component\Routing\Annotation\Route;
  10. use Symfony\Component\HttpFoundation\Request;
  11. use App\Entity\InfoautoTokens;
  12. /**
  13.  * @Route("/infoauto")
  14.  */
  15. class InfoautoController extends AbstractController
  16. {
  17.     #[Route('/'name'infoauto_index')]
  18.     public function index(): Response
  19.     {
  20.         $dataJson json_decode(file_get_contents$this->getParameter('kernel.project_dir').'/public/brands-groups.json' ), true);
  21.         /*---- List brands [BEGIN] ----*/
  22.         $aBrands = [];
  23.         $aModels = [];
  24.         foreach ($dataJson as $brand) {
  25.             $aTemp = [];
  26.             $aTemp['brandId'] = $brand['id'];
  27.             $aTemp['brandName'] = $brand['name'];
  28.             $aTemp['brandListPrice'] = $brand['list_price'];
  29.             $aTemp['brandPrices'] = $brand['prices'];
  30.             $aTemp['brandPriceFrom'] = $brand['prices_from'];
  31.             $aTemp['brandPriceTo'] = $brand['prices_to'];
  32.             $aTemp['brandGroups'] = $brand['groups'];
  33.             /*foreach ($brand['groups'] as $group) {
  34.             }*/
  35.             $aBrands[] = $aTemp;
  36.         }
  37.         /*---- List brands [BEGIN] ----*/
  38.         //dump($aBrands); exit();
  39.         return $this->render('infoauto/index.html.twig', [
  40.             'currentMenu' => 'car',
  41.             'brands' => $aBrands,
  42.         ]);
  43.     }
  44.     #[Route('/getModal'name'infoauto_get_modal')]
  45.     public function getModal(): Response
  46.     {
  47.         $dataJson json_decode(file_get_contents$this->getParameter('kernel.project_dir').'/public/brands-groups.json' ), true);
  48.         /*---- List brands [BEGIN] ----*/
  49.         $aBrands = [];
  50.         $aModels = [];
  51.         foreach ($dataJson as $brand) {
  52.             $aTemp = [];
  53.             $aTemp['brandId'] = $brand['id'];
  54.             $aTemp['brandName'] = $brand['name'];
  55.             $aTemp['brandListPrice'] = $brand['list_price'];
  56.             $aTemp['brandPrices'] = $brand['prices'];
  57.             $aTemp['brandPriceFrom'] = $brand['prices_from'];
  58.             $aTemp['brandPriceTo'] = $brand['prices_to'];
  59.             $aTemp['brandGroups'] = $brand['groups'];
  60.             /*foreach ($brand['groups'] as $group) {
  61.             }*/
  62.             $aBrands[] = $aTemp;
  63.         }
  64.         /*---- List brands [BEGIN] ----*/
  65.         //dump($aBrands); exit();
  66.         return $this->render('car/get_infoauto_modal.html.twig', [
  67.             'currentMenu' => 'car',
  68.             'brands' => $aBrands,
  69.         ]);
  70.     }
  71.     #[Route('/brands/download'name'infoauto_brands_download'methods: ['GET'])]
  72.     public function downloadBrands(ManagerRegistry $doctrineInfoautoTokensRepository $infoautoTokens): JsonResponse
  73.     {
  74.         $token $this->getInfoautoToken($doctrine$infoautoTokens);
  75.         $ch curl_init();
  76.         curl_setopt_array($ch, [
  77.             CURLOPT_URL => 'https://api.infoauto.com.ar/cars/pub/brands/download/',
  78.             CURLOPT_RETURNTRANSFER => true,
  79.             CURLOPT_FOLLOWLOCATION => true,
  80.             CURLOPT_TIMEOUT => 60,
  81.             CURLOPT_ENCODING => 'gzip',
  82.             CURLOPT_HTTPHEADER => [
  83.                 'Authorization: Bearer '.$token,
  84.                 'Accept: application/json',
  85.                 'Accept-Encoding: gzip',
  86.             ],
  87.         ]);
  88.         $body curl_exec($ch);
  89.         $httpCode = (int) curl_getinfo($chCURLINFO_HTTP_CODE);
  90.         $curlErr curl_error($ch);
  91.         curl_close($ch);
  92.         if ($body === false || $httpCode >= 400) {
  93.             return new JsonResponse([
  94.                 'status' => 'error',
  95.                 'message' => 'No se pudo descargar brands-groups.',
  96.                 'httpCode' => $httpCode,
  97.                 'curlError' => $curlErr,
  98.                 'response' => $body mb_substr($body02000) : null,
  99.             ], 502);
  100.         }
  101.         $path $this->getParameter('kernel.project_dir').'/public/brands-groups.json';
  102.         file_put_contents($path$body);
  103.         return new JsonResponse([
  104.             'status' => 'success',
  105.             'message' => 'brands-groups.json actualizado.',
  106.             'path' => $path,
  107.         ]);
  108.     }
  109.     /**
  110.      * @Route("/getVersions", name="infoauto_get_versions", methods={"GET"})
  111.      */
  112.     public function getVersions(ManagerRegistry $doctrineRequest $requestInfoautoTokensRepository $infoautoTokens): JsonResponse
  113.     {
  114.         $data['brandId'] = $request->get('brandId');
  115.         $data['modelId'] = $request->get('modelId');
  116.         $data['modelYear'] = $request->get('modelYear');
  117.         $token $this->getInfoautoToken($doctrine$infoautoTokens);
  118.         // Define los encabezados necesarios
  119.         $headers = [
  120.             'Authorization: Bearer '.$token,
  121.         ];
  122.         $url 'https://api.infoauto.com.ar/cars/pub//brands/'.$data['brandId'].'/groups/'.$data['modelId'].'/models?&prices=true&prices_from='.$data['modelYear'].'&prices_to='.$data['modelYear'].'&page_size=100';
  123.         if ($data['modelYear'] == '0km') {
  124.             $url 'https://api.infoauto.com.ar/cars/pub//brands/'.$data['brandId'].'/groups/'.$data['modelId'].'/models?&list_price=true&page_size=100';
  125.         }
  126.         /*---- Buscar modelos por año [BEGIN] ----*/
  127.         $curl curl_init();
  128.         curl_setopt_array($curl, array(
  129.             CURLOPT_URL => $url,
  130.             CURLOPT_RETURNTRANSFER => true,
  131.             CURLOPT_ENCODING => '',
  132.             CURLOPT_MAXREDIRS => 10,
  133.             CURLOPT_TIMEOUT => 0,
  134.             CURLOPT_FOLLOWLOCATION => true,
  135.             CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  136.             CURLOPT_CUSTOMREQUEST => 'GET',
  137.             CURLOPT_HTTPHEADER => $headers,   // Encabezados de la petición
  138.             CURLOPT_SSL_VERIFYPEER => false,
  139.             CURLOPT_SSL_VERIFYHOST => false,
  140.         ));
  141.         $response curl_exec($curl);
  142.         // Verifica si ocurrió un error de cURL
  143.         if ($response === false) {
  144.             $error curl_error($curl);
  145.             curl_close($curl);
  146.             die("Error en cURL: $error");
  147.         }
  148.         // Obtiene el código HTTP de la respuesta
  149.         $httpCode curl_getinfo($curlCURLINFO_HTTP_CODE);
  150.         curl_close($curl);
  151.         $modelsJson json_decode($responsetrue);
  152.         /*---- Buscar modelos por año [END] ----*/
  153.         //dump($modelsJson); exit();
  154.         /*---- Buscar precios [BEGIN] ----*/
  155.         // Recopilar CODIA
  156.         $aCodia null;
  157.         foreach ($modelsJson as $item) {
  158.             $aCodia[] = ['name' => $item['description'], 'codia' => $item['codia']];
  159.         }
  160.         if ($aCodia) {
  161.             $rateLimitReached false;
  162.             foreach ($aCodia as $key => $item) {
  163.                 $priceFloat null;
  164.                 // Primero intenta precio en payload /models.
  165.                 if (isset($modelsJson[$key]) && is_array($modelsJson[$key])) {
  166.                     $priceFloat $this->extractInfoautoPrice($modelsJson[$key], $data['modelYear']);
  167.                 }
  168.                 // Fallback al endpoint por CODIA (comportamiento del controller anterior).
  169.                 if (($priceFloat === null || $priceFloat <= 0) && !$rateLimitReached) {
  170.                     $priceUrl 'https://api.infoauto.com.ar/cars/pub/models/'.$item['codia'].'/prices';
  171.                     if ($data['modelYear'] == '0km') {
  172.                         $priceUrl 'https://api.infoauto.com.ar/cars/pub/models/'.$item['codia'].'/list_price';
  173.                     }
  174.                     $priceCurl curl_init();
  175.                     curl_setopt_array($priceCurl, array(
  176.                         CURLOPT_URL => $priceUrl,
  177.                         CURLOPT_RETURNTRANSFER => true,
  178.                         CURLOPT_ENCODING => '',
  179.                         CURLOPT_MAXREDIRS => 10,
  180.                         CURLOPT_TIMEOUT => 0,
  181.                         CURLOPT_FOLLOWLOCATION => true,
  182.                         CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  183.                         CURLOPT_CUSTOMREQUEST => 'GET',
  184.                         CURLOPT_HTTPHEADER => $headers,
  185.                         CURLOPT_SSL_VERIFYPEER => false,
  186.                         CURLOPT_SSL_VERIFYHOST => false,
  187.                     ));
  188.                     $priceResponse curl_exec($priceCurl);
  189.                     $priceHttpCode = (int) curl_getinfo($priceCurlCURLINFO_HTTP_CODE);
  190.                     if ($priceResponse === false) {
  191.                         curl_close($priceCurl);
  192.                     } else {
  193.                         curl_close($priceCurl);
  194.                         $aPrices json_decode($priceResponsetrue);
  195.                         if ($priceHttpCode === 403 && is_array($aPrices) && (($aPrices['code'] ?? null) == 403)) {
  196.                             $rateLimitReached true;
  197.                         } elseif (is_array($aPrices)) {
  198.                             if ($data['modelYear'] == '0km') {
  199.                                 foreach ($aPrices as $item2) {
  200.                                     if (is_numeric($item2)) {
  201.                                         $priceFloat = (float) $item2;
  202.                                         break;
  203.                                     }
  204.                                     if (is_array($item2)) {
  205.                                         foreach (['list_price''price''official_price''amount''value'] as $priceKey) {
  206.                                             if (isset($item2[$priceKey]) && is_numeric($item2[$priceKey])) {
  207.                                                 $priceFloat = (float) $item2[$priceKey];
  208.                                                 break 2;
  209.                                             }
  210.                                         }
  211.                                     }
  212.                                 }
  213.                             } else {
  214.                                 foreach ($aPrices as $item2) {
  215.                                     if (is_array($item2) && isset($item2['year']) && (int) $item2['year'] === (int) $data['modelYear']) {
  216.                                         if (isset($item2['price']) && is_numeric($item2['price'])) {
  217.                                             $priceFloat = (float) $item2['price'];
  218.                                             break;
  219.                                         }
  220.                                     }
  221.                                 }
  222.                             }
  223.                             if ($priceFloat === null || $priceFloat <= 0) {
  224.                                 $priceFloat $this->extractInfoautoPrice($aPrices$data['modelYear']);
  225.                             }
  226.                         }
  227.                     }
  228.                 }
  229.                 $aCodia[$key]['price'] = $priceFloat !== null number_format($priceFloat0'''.') : 0;
  230.             }
  231.             /*---- Buscar precios [END] ----*/
  232.         } else {
  233.             $data['status'] = 'error';
  234.             $data['type'] = 'version';
  235.             $data['message'] = 'No se encontró versión para el modelo';
  236.             echo '<h3>'.$data['message'].'</h3>';
  237.             exit();
  238.         }
  239.         //dump($aCodia); exit();
  240.         return $this->json($aCodia);
  241.     }
  242.     #[Route('/getVersionPrice'name'infoauto_get_version_price'methods: ['GET'])]
  243.     public function getVersionPrice(ManagerRegistry $doctrineRequest $requestInfoautoTokensRepository $infoautoTokens): JsonResponse
  244.     {
  245.         $codia $request->get('codia');
  246.         $modelYear = (string) $request->get('modelYear');
  247.         if (!$codia || $modelYear === '') {
  248.             return $this->json([
  249.                 'status' => 'error',
  250.                 'message' => 'Parámetros inválidos.',
  251.             ], 400);
  252.         }
  253.         $token $this->getInfoautoToken($doctrine$infoautoTokens);
  254.         $headers = [
  255.             'Authorization: Bearer '.$token,
  256.         ];
  257.         $url 'https://api.infoauto.com.ar/cars/pub/models/'.$codia.'/prices';
  258.         if ($modelYear === '0km') {
  259.             $url 'https://api.infoauto.com.ar/cars/pub/models/'.$codia.'/list_price';
  260.         }
  261.         $curl curl_init();
  262.         curl_setopt_array($curl, array(
  263.             CURLOPT_URL => $url,
  264.             CURLOPT_RETURNTRANSFER => true,
  265.             CURLOPT_ENCODING => '',
  266.             CURLOPT_MAXREDIRS => 10,
  267.             CURLOPT_TIMEOUT => 0,
  268.             CURLOPT_FOLLOWLOCATION => true,
  269.             CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  270.             CURLOPT_CUSTOMREQUEST => 'GET',
  271.             CURLOPT_HTTPHEADER => $headers,
  272.             CURLOPT_SSL_VERIFYPEER => false,
  273.             CURLOPT_SSL_VERIFYHOST => false,
  274.         ));
  275.         $response curl_exec($curl);
  276.         $httpCode = (int) curl_getinfo($curlCURLINFO_HTTP_CODE);
  277.         if ($response === false) {
  278.             $error curl_error($curl);
  279.             curl_close($curl);
  280.             return $this->json([
  281.                 'status' => 'error',
  282.                 'message' => 'Error al consultar precio.',
  283.                 'detail' => $error,
  284.             ], 502);
  285.         }
  286.         curl_close($curl);
  287.         $pricesPayload json_decode($responsetrue);
  288.         $priceFloat $this->extractInfoautoPrice($pricesPayload$modelYear);
  289.         $price $priceFloat !== null number_format($priceFloat0'''.') : 0;
  290.         return $this->json([
  291.             'status' => 'success',
  292.             'codia' => $codia,
  293.             'modelYear' => $modelYear,
  294.             'price' => $price,
  295.             'httpCode' => $httpCode,
  296.         ]);
  297.     }
  298.     /**
  299.      * @Route("/getFeatures", name="infoauto_get_features", methods={"GET"})
  300.      */
  301.     public function getFeatures(ManagerRegistry $doctrineRequest $requestInfoautoTokensRepository $infoautoTokens): JsonResponse
  302.     {
  303.         $data['codia'] = $request->get('codia');
  304.         $token $this->getInfoautoToken($doctrine$infoautoTokens);
  305.         // Define los encabezados necesarios
  306.         $headers = [
  307.             'Authorization: Bearer '.$token,
  308.         ];
  309.         /*---- Buscar modelos por año [BEGIN] ----*/
  310.         $curl curl_init();
  311.         curl_setopt_array($curl, array(
  312.             CURLOPT_URL => 'https://api.infoauto.com.ar/cars/pub/models/'.$data['codia'].'/features/',
  313.             CURLOPT_RETURNTRANSFER => true,
  314.             CURLOPT_ENCODING => '',
  315.             CURLOPT_MAXREDIRS => 10,
  316.             CURLOPT_TIMEOUT => 0,
  317.             CURLOPT_FOLLOWLOCATION => true,
  318.             CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  319.             CURLOPT_CUSTOMREQUEST => 'GET',
  320.             CURLOPT_HTTPHEADER => $headers
  321.             CURLOPT_SSL_VERIFYPEER => false,
  322.             CURLOPT_SSL_VERIFYHOST => false,
  323.         ));
  324.         $response curl_exec($curl);
  325.         curl_close($curl);
  326.         // Verifica si ocurrió un error de cURL
  327.         if ($response === false) {
  328.             $error curl_error($curl);
  329.             curl_close($curl);
  330.             die("Error en cURL: $error");
  331.         }
  332.         // Obtiene el código HTTP de la respuesta
  333.         $httpCode curl_getinfo($curlCURLINFO_HTTP_CODE);
  334.         //$featuresJson = json_decode($response, true);
  335.         $featuresJson $response;
  336.         /*---- Buscar modelos por año [END] ----*/
  337.         //dump($featuresJson); exit();
  338.         return $this->json($featuresJson);
  339.     }
  340.     private function getInfoautoToken(ManagerRegistry $doctrineInfoautoTokensRepository $infoautoTokens)
  341.     {
  342.         $tokenTmp $infoautoTokens->findOneBy([], ['id' => 'DESC'], 1);
  343.         if ($tokenTmp) {
  344.             $hourMore = clone $tokenTmp->getCreatedAt();
  345.             $hourMore = ($hourMore)->add(new \DateInterval('PT1H'));
  346.             if (new \DateTime() > $hourMore) {
  347.                 $tokenOk false;
  348.             } else {
  349.                 $tokenOk true;
  350.             }
  351.         } else {
  352.             $tokenOk false;
  353.         }
  354.         if ($tokenOk) {
  355.             return $tokenTmp->getAccessToken();
  356.         } else {
  357.             $headers = [
  358.                 'Content-Type: application/json',
  359.                 'Authorization: Basic 'base64_encode('rwaybar@yahoo.com.ar:NQlv38cCxODlq42f'),
  360.                 'Content-Length: 0'
  361.             ];
  362.             $curl curl_init();
  363.             curl_setopt_array($curl, array(
  364.                 CURLOPT_URL => 'https://api.infoauto.com.ar/cars/auth/login',
  365.                 CURLOPT_RETURNTRANSFER => true,
  366.                 CURLOPT_ENCODING => '',
  367.                 CURLOPT_MAXREDIRS => 10,
  368.                 CURLOPT_TIMEOUT => 0,
  369.                 CURLOPT_FOLLOWLOCATION => true,
  370.                 CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  371.                 CURLOPT_CUSTOMREQUEST => 'POST',
  372.                 CURLOPT_HTTPHEADER => $headers,  
  373.                 CURLOPT_SSL_VERIFYPEER => false,
  374.                 CURLOPT_SSL_VERIFYHOST => false,
  375.             ));
  376.             $response curl_exec($curl);
  377.             curl_close($curl);
  378.             if ($response === false) {
  379.                 $error curl_error($curl);
  380.                 curl_close($curl);
  381.                 die("Error en cURL: $error");
  382.             }
  383.             $json json_decode($response);
  384.             $tokens = new InfoautoTokens();
  385.             $tokens->setAccessToken($json->access_token);
  386.             $tokens->setRefreshToken($json->refresh_token);
  387.             $tokens->setCreatedAt(new \DateTime);
  388.             $doctrine->getManager()->persist($tokens);
  389.             $doctrine->getManager()->flush();
  390.             return $json->access_token;
  391.         }
  392.     }
  393.     private function extractInfoautoPrice($payloadstring $modelYear): ?float
  394.     {
  395.         if ($payload === null) {
  396.             return null;
  397.         }
  398.         $targetYear $modelYear === '0km' null : (int) $modelYear;
  399.         $queue = [$payload];
  400.         $zeroCandidate null;
  401.         while (!empty($queue)) {
  402.             $node array_shift($queue);
  403.             if (!is_array($node)) {
  404.                 $candidate $this->normalizeNumericValue($node);
  405.                 if ($targetYear === null && $candidate !== null) {
  406.                     if ($candidate 0) {
  407.                         return $candidate;
  408.                     }
  409.                     $zeroCandidate $zeroCandidate ?? 0.0;
  410.                 }
  411.                 continue;
  412.             }
  413.             if ($targetYear !== null) {
  414.                 if (isset($node[(string) $targetYear])) {
  415.                     $candidate $this->normalizeNumericValue($node[(string) $targetYear]);
  416.                     if ($candidate !== null) {
  417.                         if ($candidate 0) {
  418.                             return $candidate;
  419.                         }
  420.                         $zeroCandidate $zeroCandidate ?? 0.0;
  421.                     }
  422.                 }
  423.                 if (isset($node[$targetYear])) {
  424.                     $candidate $this->normalizeNumericValue($node[$targetYear]);
  425.                     if ($candidate !== null) {
  426.                         if ($candidate 0) {
  427.                             return $candidate;
  428.                         }
  429.                         $zeroCandidate $zeroCandidate ?? 0.0;
  430.                     }
  431.                 }
  432.                 $lowerNode array_change_key_case($nodeCASE_LOWER);
  433.                 if (isset($lowerNode['year']) && (int) $lowerNode['year'] === $targetYear) {
  434.                     foreach (['price''list_price''value''amount''official_price''used_price'] as $priceKey) {
  435.                         if (isset($lowerNode[$priceKey])) {
  436.                             $candidate $this->normalizeNumericValue($lowerNode[$priceKey]);
  437.                             if ($candidate !== null) {
  438.                                 if ($candidate 0) {
  439.                                     return $candidate;
  440.                                 }
  441.                                 $zeroCandidate $zeroCandidate ?? 0.0;
  442.                             }
  443.                         }
  444.                     }
  445.                 }
  446.             } else {
  447.                 $lowerNode array_change_key_case($nodeCASE_LOWER);
  448.                 foreach (['list_price''price''official_price''amount''value'] as $priceKey) {
  449.                     if (isset($lowerNode[$priceKey])) {
  450.                         $candidate $this->normalizeNumericValue($lowerNode[$priceKey]);
  451.                         if ($candidate !== null) {
  452.                             if ($candidate 0) {
  453.                                 return $candidate;
  454.                             }
  455.                             $zeroCandidate $zeroCandidate ?? 0.0;
  456.                         }
  457.                     }
  458.                 }
  459.             }
  460.             foreach ($node as $key => $child) {
  461.                 if ($targetYear !== null && is_numeric($key) && (int) $key === $targetYear) {
  462.                     $candidate $this->normalizeNumericValue($child);
  463.                     if ($candidate !== null) {
  464.                         if ($candidate 0) {
  465.                             return $candidate;
  466.                         }
  467.                         $zeroCandidate $zeroCandidate ?? 0.0;
  468.                     }
  469.                 }
  470.                 if (is_array($child)) {
  471.                     $queue[] = $child;
  472.                 }
  473.             }
  474.         }
  475.         return $zeroCandidate;
  476.     }
  477.     private function normalizeNumericValue($value): ?float
  478.     {
  479.         if (is_int($value) || is_float($value)) {
  480.             return (float) $value;
  481.         }
  482.         if (!is_string($value)) {
  483.             return null;
  484.         }
  485.         $clean trim($value);
  486.         if ($clean === '') {
  487.             return null;
  488.         }
  489.         $clean preg_replace('/[^0-9.,-]/'''$clean);
  490.         if ($clean === null || $clean === '') {
  491.             return null;
  492.         }
  493.         if (preg_match('/^-?\d{1,3}([.,]\d{3})+$/'$clean)) {
  494.             $clean str_replace([',''.'], ''$clean);
  495.         } else {
  496.             $clean str_replace(',''.'$clean);
  497.         }
  498.         return is_numeric($clean) ? (float) $clean null;
  499.     }
  500. }