<?php
// src/Controller/Api/Catalog/GetCatalogPdfControllerV3.php
namespace App\Controller\Api\Catalog;
/**
* @author rbellorin@sumagroups.com
* @version 2026-03-24
*/
use App\Catalog\Application\DTO\CatalogPage;
use App\Catalog\Application\UseCase\BuildCatalogPagesV2;
use App\Catalog\Application\UseCase\GenerateCatalogPdf;
use App\Catalog\Application\UseCase\GetMakesList;
use App\Shared\Pdf\CatalogBrandConfig;
use App\Shared\Utils\UrlHelper;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
final class GetCatalogPdfControllerV3 extends AbstractController
{
public function __construct(
private Security $security,
private GenerateCatalogPdf $generatePdf,
private CatalogBrandConfig $catalogBrandConfig,
private readonly GetMakesList $getMakesList
) {}
/**
* @Route("/api/catalogo/v3/catalogo.pdf", name="catalog_v3_pdf", methods={"GET"})
*/
public function __invoke(
Request $request,
BuildCatalogPagesV2 $useCase
): JsonResponse|BinaryFileResponse {
ini_set('memory_limit', '2048M');
if (function_exists('set_time_limit')) {
@set_time_limit(180);
}
$params = $request->query->all();
$data = ($useCase)($params);
$marcaList = ($this->getMakesList)($params);
// $marcaList = ($this->getMakesList)($params);
// dd($params, $marcaList);
$marcaList = array_map(
fn($e) => (object)[
'id' => $e->id,
'name' => $e->name,
'categories' => $e->categories,
'links' => $this->catalogBrandConfig->getByMarca($e->name),
],
$marcaList
);
$marcaList = array_map(function ($e) use ($data) {
// 1. Filtrar productos por marca
$e->products = array_filter(
$data,
fn($f) => $f->marca === $e->name
);
$e->products = array_values($e->products);
// 2. Filtrar productos por categorías válidas de la marca
$e->products = array_filter(
$e->products,
fn($f) => in_array($f->categoria, $e->categories, true)
);
$e->products = array_values($e->products);
// 3. Ordenar productos según el orden de categorías definido en la marca
usort($e->products, function ($a, $b) use ($e) {
$posA = array_search($a->categoria, $e->categories, true);
$posB = array_search($b->categoria, $e->categories, true);
$posA = $posA === false ? PHP_INT_MAX : $posA;
$posB = $posB === false ? PHP_INT_MAX : $posB;
return $posA <=> $posB;
});
// 4. Crear páginas de catálogo por categoría, agrupando productos de 2 en 2
$e->grouped = [];
foreach ($e->categories as $categoriaNombre) {
$productosDeCategoria = array_filter(
$e->products,
fn($f) => $f->categoria === $categoriaNombre
);
$productosDeCategoria = array_values($productosDeCategoria);
usort(
$productosDeCategoria,
fn($a, $b) => strcmp((string) $a->comercialName, (string) $b->comercialName)
);
$agrupados = array_chunk($productosDeCategoria, 2);
foreach ($agrupados as $chunk) {
$chunkArray = [];
foreach($chunk as $item) {
$chunkArray[] = [
'm_product_id' => $item->m_product_id
,'code' => $item->code
,'sku' => $item->sku
,'name' => $item->name
,'comercialName' => $item->comercialName ?? '*'
,'allegationHtml' => $item->allegationHtml ?? ''
,'promo' => $item->promo
,'new' => $item->new
,'image' => UrlHelper::deprefixSite($item->image)
,'price' => $item->price
,'categoria' => $item->categoria
,'marca' => $item->marca
,'sm_marca_id' => $item->sm_marca_id
,'m_product_category_id' => $item->m_product_category_id
];
}
$e->grouped[] = new CatalogPage(
$categoriaNombre,
$e->name,
$chunkArray
);
}
}
return $e;
}, $marcaList);
// dd($marcaList[0]);
// Armar un único arreglo de páginas multimarca
$catalogPages = [];
foreach ($marcaList as $marcaItem) {
foreach ($marcaItem->grouped as $page) {
$catalogPages[] = $page;
}
}
if (empty($catalogPages)) {
return new JsonResponse([
'message' => 'No hay productos para generar el catálogo multimarca.'
], 404);
}
$disp = ResponseHeaderBag::DISPOSITION_ATTACHMENT;
// Un solo PDF final multimarca
$path = $this->generatePdf->handle($catalogPages, [
'title' => 'Catálogo Multimarca',
'brand' => 'viotto',
'sm_marca' => null,
'output' => 'F',
'linkPage' => null,
'linkSocial' => null,
'with_prices' => $this->toBool($params['with_prices'] ?? true),
]);
$resp = new BinaryFileResponse($path);
$resp->headers->set('Content-Type', 'application/pdf');
$resp->setContentDisposition($disp, 'catalogo-multimarca.pdf');
$resp->headers->set('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
$resp->headers->set('Pragma', 'no-cache');
$resp->headers->set('Expires', '0');
$resp->setPrivate();
$resp->headers->remove('ETag');
if (@is_file($path)) {
$resp->headers->set('Content-Length', (string) @filesize($path));
}
$resp->deleteFileAfterSend(true);
return $resp;
}
private function toBool(mixed $value): bool
{
if (is_bool($value)) return $value;
if (is_int($value)) return $value !== 0;
return !in_array(strtolower((string) $value), ['0', 'false', 'no', 'n'], true);
}
}