src/Controller/Frontend/RankingController.php line 56

Open in your IDE?
  1. <?php
  2. namespace App\Controller\Frontend;
  3. use App\Controller\Frontend\Ranking\RankingPdfData;
  4. use App\Controller\Shared\Club\ResolveClubLogoSvgUrl;
  5. use App\Controller\Shared\Ranking\CalculateRankingCached;
  6. use App\Entity\Backend\Club;
  7. use App\Entity\Backend\Competicion;
  8. use App\Entity\Backend\Jugador;
  9. use App\Entity\Gestion\BonusRanking;
  10. use App\Entity\Gestion\ClasificadoRanking;
  11. use App\Entity\Gestion\PenalidadRanking;
  12. use App\Entity\Gestion\Ranking;
  13. use App\Util\PrintPdf;
  14. use Doctrine\Persistence\ManagerRegistry;
  15. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  16. use Symfony\Component\HttpFoundation\RedirectResponse;
  17. use Symfony\Component\HttpFoundation\Request;
  18. use Symfony\Component\HttpFoundation\Response;
  19. use Symfony\Component\HttpFoundation\ResponseHeaderBag;
  20. use Symfony\Component\Routing\Annotation\Route;
  21. class RankingController extends AbstractController {
  22.     private $em;
  23.     public function __construct(ManagerRegistry $doctrine){
  24.         $this->em $doctrine->getManager();
  25.     }
  26.     /**
  27.      * F-122 Visualización del ranking
  28.      *
  29.      * @Route("/ranking/{id}", name="ranking", options={"expose"=true})
  30.      */
  31.     public function ranking($idCalculateRankingCached $calculateRankingCached): RedirectResponse|Response
  32.     {
  33.         $id intval($id);
  34.         $ranking $this->em->getRepository(Ranking::class)->find($id);
  35.         if (!$ranking) {
  36.             $this->addFlash(
  37.                     'error'"El ranking seleccionado no existe");
  38.             return $this->redirect($this->generateUrl('portada'));
  39.         }
  40.         $params $calculateRankingCached->__invoke($id);
  41.         return $this->render("frontend/Ranking/ranking.html.twig"$params);
  42.     }
  43.     /**
  44.      * @Route("/ranking/{id}/pdf/{filename}", name="ranking_pdf", requirements={"id": "\d+", "filename": ".+\.pdf"}, defaults={"filename"=null})
  45.      */
  46.     public function rankingPdf(
  47.         $id,
  48.         ?string $filename,
  49.         CalculateRankingCached $calculateRankingCached,
  50.         RankingPdfData $rankingPdfData,
  51.         ResolveClubLogoSvgUrl $resolveClubLogoSvgUrl,
  52.         PrintPdf $printPdf,
  53.     ): RedirectResponse|Response {
  54.         $id intval($id);
  55.         $ranking $this->em->getRepository(Ranking::class)->find($id);
  56.         if (!$ranking) {
  57.             $this->addFlash('error'"El ranking seleccionado no existe");
  58.             return $this->redirect($this->generateUrl('portada'));
  59.         }
  60.         if (!$ranking->getActivo() && !$this->isGranted('ROLE_ADMIN') && !$this->isGranted('ROLE_FEDE') && !$this->isGranted('ROLE_CLUB')) {
  61.             throw $this->createAccessDeniedException();
  62.         }
  63.         $pdfFilename $this->pdfFilename($ranking);
  64.         if ($filename !== $pdfFilename) {
  65.             return $this->redirectToRoute('ranking_pdf', [
  66.                 'id' => $ranking->getId(),
  67.                 'filename' => $pdfFilename,
  68.             ]);
  69.         }
  70.         $html $this->renderRankingPdfHtml($id$ranking$calculateRankingCached$rankingPdfData$resolveClubLogoSvgUrl);
  71.         $pdf $this->generateRankingPdfWithGotenberg($html$printPdf);
  72.         $response = new Response($pdf);
  73.         $response->headers->set('Content-Type''application/pdf');
  74.         $response->headers->set(
  75.             'Content-Disposition',
  76.             $response->headers->makeDisposition(ResponseHeaderBag::DISPOSITION_INLINE$pdfFilename)
  77.         );
  78.         $response->headers->set('X-Robots-Tag''noindex');
  79.         return $response;
  80.     }
  81.     private function generateRankingPdfWithGotenberg(string $htmlPrintPdf $printPdf): string
  82.     {
  83.         $temporaryBaseFile tempnam(sys_get_temp_dir(), 'ranking_pdf_');
  84.         if ($temporaryBaseFile === false) {
  85.             throw new \RuntimeException('No se pudo crear el archivo temporal del ranking');
  86.         }
  87.         $htmlFile $temporaryBaseFile '.html';
  88.         $pdfFile $temporaryBaseFile '.pdf';
  89.         try {
  90.             if (file_exists($temporaryBaseFile)) {
  91.                 unlink($temporaryBaseFile);
  92.             }
  93.             file_put_contents($htmlFile$html);
  94.             $printPdf->sendHtmlToGeneratePdf($temporaryBaseFile);
  95.             $pdf file_get_contents($pdfFile);
  96.             if ($pdf === false) {
  97.                 throw new \RuntimeException('No se pudo leer el PDF generado del ranking');
  98.             }
  99.             return $pdf;
  100.         } finally {
  101.             if (file_exists($temporaryBaseFile)) {
  102.                 unlink($temporaryBaseFile);
  103.             }
  104.             if (file_exists($htmlFile)) {
  105.                 unlink($htmlFile);
  106.             }
  107.             if (file_exists($pdfFile)) {
  108.                 unlink($pdfFile);
  109.             }
  110.         }
  111.     }
  112.     private function renderRankingPdfHtml(
  113.         int $id,
  114.         Ranking $ranking,
  115.         CalculateRankingCached $calculateRankingCached,
  116.         RankingPdfData $rankingPdfData,
  117.         ResolveClubLogoSvgUrl $resolveClubLogoSvgUrl,
  118.     ): string {
  119.         $params $calculateRankingCached->__invoke($id);
  120.         $ownerCode $rankingPdfData->ownerCode($ranking);
  121.         $pdfData $rankingPdfData->__invoke($params);
  122.         return $this->renderView('frontend/Ranking/ranking-pdf.html.twig'array_merge($params, [
  123.             'rankingPdf' => $pdfData,
  124.             'rankingPdfColors' => $rankingPdfData->colors($ownerCode),
  125.             'rankingPdfLogoUrl' => $this->pdfLogoDataUri($resolveClubLogoSvgUrl((string)$ownerCode)),
  126.         ]));
  127.     }
  128.     private function pdfLogoDataUri(string $logoUrl): string
  129.     {
  130.         $logoUrl $this->preferPngLogoUrl($logoUrl);
  131.         $contents = @file_get_contents($logoUrl);
  132.         if ($contents === false || $contents === '') {
  133.             return $logoUrl;
  134.         }
  135.         $extension strtolower((string)pathinfo(parse_url($logoUrlPHP_URL_PATH) ?: ''PATHINFO_EXTENSION));
  136.         $mimeType = match ($extension) {
  137.             'png' => 'image/png',
  138.             'jpg''jpeg' => 'image/jpeg',
  139.             'webp' => 'image/webp',
  140.             'svg' => 'image/svg+xml',
  141.             default => 'image/png',
  142.         };
  143.         return 'data:' $mimeType ';base64,' base64_encode($contents);
  144.     }
  145.     private function preferPngLogoUrl(string $logoUrl): string
  146.     {
  147.         $pngLogoUrl str_replace(['/SVG/''.svg'], ['/PNG/''.png'], $logoUrl);
  148.         return $pngLogoUrl !== $logoUrl && $this->remoteFileExists($pngLogoUrl) ? $pngLogoUrl $logoUrl;
  149.     }
  150.     private function remoteFileExists(string $url): bool
  151.     {
  152.         $headers = @get_headers($url);
  153.         $statusLine $headers[0] ?? '';
  154.         return $statusLine !== '' && (
  155.             str_contains($statusLine'200')
  156.             || str_contains($statusLine'301')
  157.             || str_contains($statusLine'302')
  158.         );
  159.     }
  160.     private function pdfFilename(Ranking $ranking): string
  161.     {
  162.         $filename iconv('UTF-8''ASCII//TRANSLIT', (string)$ranking->getNombre());
  163.         $filename preg_replace('/[^A-Za-z0-9._-]+/''-'$filename ?: '');
  164.         $filename trim((string)$filename'-');
  165.         if ($filename === '') {
  166.             $filename 'ranking-' $ranking->getId();
  167.         }
  168.         return $filename '.pdf';
  169.     }
  170.     /**
  171.      * F-123 Visualización de los rankings de un club
  172.      *
  173.      * @Route("/rankings-club/{id}", name="rankings_club")
  174.      */
  175.     public function rankingsClub($id): RedirectResponse|Response
  176.     {
  177.         $club $this->em->getRepository(Club::class)->find($id);
  178.         if (!$club) {
  179.             $this->addFlash('error'"No encontramos el Club que nos ha solicitado");
  180.             return $this->redirect($this->generateUrl('provincias'));
  181.         }
  182.         $isFederation $club->isFederationClub();
  183.         $clientId $isFederation $club->getCliente()->getId() : null;
  184.         $rankings $this->em->getRepository(Ranking::class)->obtainRankingsByClubOrClient($club->getId(), $clientId$isFederationfalse);
  185.         return $this->render("frontend/Ranking/rankingsClub.html.twig", [
  186.             "rankings" => $rankings,
  187.             "club" => $club
  188.         ]);
  189.     }
  190. }