<?php
declare(strict_types=1);
namespace App\Controller\Blo;
use App\Entity\Blo\Shop;
use App\Repository\Blo\CategoryRepository;
use App\Repository\Blo\ProductRepository;
use App\Repository\Blo\ShopRepository;
use App\Service\Blo\ProductAttributesHelper;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
#[Route('/boutique')]
class CatalogController extends AbstractController
{
public function __construct(
private readonly ProductRepository $productRepository,
private readonly CategoryRepository $categoryRepository,
private readonly ShopRepository $shopRepository,
private readonly PaginatorInterface $paginator,
) {
}
#[Route('', name: 'blo_catalog', methods: ['GET'])]
public function index(Request $request): Response
{
$query = $request->query->get('q', '');
$categoryId = $request->query->getInt('category');
$minPrice = $request->query->get('min_price') ? (float) $request->query->get('min_price') : null;
$maxPrice = $request->query->get('max_price') ? (float) $request->query->get('max_price') : null;
$promoOnly = $request->query->getBoolean('promo');
$newestOnly = $request->query->getBoolean('newest');
$qb = $this->productRepository->createQueryBuilder('p')
->innerJoin('p.shop', 's')
->where('p.status = :status')
->andWhere('s.status = :shopStatus')
->setParameter('status', 'APPROVED')
->setParameter('shopStatus', Shop::STATUS_ACTIVE);
if ($promoOnly) {
$qb->andWhere('p.promoPrice IS NOT NULL')->andWhere('p.promoPrice > 0');
}
$qb->orderBy($newestOnly ? 'p.createdAt' : 'p.updatedAt', 'DESC');
if ($query !== '') {
$qb->andWhere('p.title LIKE :query OR p.description LIKE :query')
->setParameter('query', '%' . $query . '%');
}
if ($categoryId) {
$categoryIds = $this->categoryRepository->getCategoryAndDescendantIds($categoryId);
$qb->andWhere('p.category IN (:catIds)')->setParameter('catIds', $categoryIds);
}
if ($minPrice !== null) {
$qb->andWhere('p.price >= :min')->setParameter('min', (string) $minPrice);
}
if ($maxPrice !== null) {
$qb->andWhere('p.price <= :max')->setParameter('max', (string) $maxPrice);
}
$products = $this->paginator->paginate(
$qb,
$request->query->getInt('page', 1),
20
);
$categories = $this->categoryRepository->findRootCategories();
$categoriesTree = $this->categoryRepository->findTreeForParentSelect(null);
return $this->render('blo/catalog/index.html.twig', [
'products' => $products,
'categories' => $categories,
'categoriesTree' => $categoriesTree,
'query' => $query,
'promoOnly' => $promoOnly,
'newestOnly' => $newestOnly,
]);
}
#[Route('/product/{slug}', name: 'blo_product_show', methods: ['GET'])]
public function product(string $slug): Response
{
$product = $this->productRepository->findBySlug($slug);
if (!$product) {
throw $this->createNotFoundException('Produit introuvable');
}
if ($product->getShop() && $product->getShop()->getStatus() !== Shop::STATUS_ACTIVE) {
throw $this->createNotFoundException('Produit introuvable');
}
$similarProducts = $this->productRepository->findSimilar($product, 4);
return $this->render('blo/catalog/product.html.twig', [
'product' => $product,
'similarProducts' => $similarProducts,
'colorsMap' => ProductAttributesHelper::COLORS,
]);
}
#[Route('/shop/{slug}', name: 'blo_shop_show', methods: ['GET'])]
public function shop(string $slug, Request $request): Response
{
$shop = $this->shopRepository->findBySlug($slug);
if (!$shop) {
throw $this->createNotFoundException('Boutique introuvable');
}
$products = $this->paginator->paginate(
$this->productRepository->createQueryBuilder('p')
->where('p.shop = :shop')
->andWhere('p.status = :status')
->setParameter('shop', $shop)
->setParameter('status', 'APPROVED')
->orderBy('p.createdAt', 'DESC'),
$request->query->getInt('page', 1),
20
);
return $this->render('blo/catalog/shop.html.twig', [
'shop' => $shop,
'products' => $products,
]);
}
}