<?php
namespace App\Controller;
use App\DataMapper\Pagination\PaginatorDataMapper;
use App\DataMapper\Product\ProductDetailDataMapper;
use App\DataMapper\Product\ProductListingDataMapper;
use App\Model\Product\Product;
use App\Repository\ProductRepository;
use App\Website\LinkGenerator\ProductLinkGenerator;
use Knp\Component\Pager\Pagination\SlidingPagination;
use Knp\Component\Pager\PaginatorInterface;
use Pimcore\Bundle\EcommerceFrameworkBundle\Factory;
use Pimcore\Bundle\EcommerceFrameworkBundle\FilterService\ListHelper;
use Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\ProductList\ElasticSearch\AbstractElasticSearch;
use Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\ProductList\ProductListInterface;
use Pimcore\Config;
use Pimcore\Controller\FrontendController;
use App\Model\Product\Category;
use Pimcore\Model\DataObject\ClassDefinition;
use Pimcore\Model\DataObject\FilterDefinition;
use Pimcore\Model\DataObject\ProductGroup;
use Pimcore\Model\DataObject\ProductLine;
use Pimcore\Translation\Translator;
use Pimcore\Twig\Extension\Templating\HeadTitle;
use Pimcore\Twig\Extension\Templating\Placeholder;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class ProductController extends FrontendController
{
public function __construct(
private ProductRepository $productRepository
){
}
/**
* @param Request $request
* @return Response
*/
public function indexAction(Request $request, HeadTitle $headTitleHelper, Factory $ecommerceFactory, ListHelper $listHelper, PaginatorInterface $paginator): Response
{
$params = array_merge($request->query->all(), $request->attributes->all());
$indexService = $ecommerceFactory->getIndexService();
$productListing = $indexService->getProductListForCurrentTenant();
if(!isset($params['sort'])){
$params['sort'] = 'o_id-desc';
}
$elements = explode("-", $params['sort']);
$productListing->setOrder(strtoupper($elements[1]));
$productListing->setOrderKey($elements[0]);
$productListing->setVariantMode(ProductListInterface::VARIANT_MODE_HIDE);
$params['productListing'] = $productListing;
if ($request->get('filterdefinition') instanceof FilterDefinition) {
$filterDefinition = $request->get('filterdefinition');
}
if (empty($filterDefinition)) {
$filterDefinition = Config::getWebsiteConfig()['fallbackFilterdefinition'];
}
$filterService = $ecommerceFactory->getFilterService();
$listHelper->setupProductList($filterDefinition, $productListing, $params, $filterService, true);
$params['filterService'] = $filterService;
$params['filterDefinition'] = $filterDefinition;
/** @var SlidingPagination $paginator */
// init pagination
$paginator = $paginator->paginate(
$productListing,
$request->get('page', 1),
$filterDefinition->getPageLimit()
);
$params['results'] = $paginator;
$params['paginationVariables'] = $paginator->getPaginationData();
return $this->render('product/index.html.twig', $params);
}
/**
*
* @Route("/{_locale}/product-category/{categoryname}-{category}", name="shop-category", requirements={"categoryname"="[\w-]+", "category"="\d+"})
*
* @param Request $request
* @param HeadTitle $headTitleHelper
* @param Factory $ecommerceFactory
* @param ListHelper $listHelper
*
* @return Response
*/
public function listingAction(Request $request, Factory $ecommerceFactory, ListHelper $listHelper, PaginatorInterface $paginator)
{
$params = array_merge($request->query->all(), $request->attributes->all());
//needed to make sure category filter filters for active category
$params['parentCategoryIds'] = $params['category'] ?? null;
$category = Category::getById($params['category'] ?? -1);
$params['category'] = $category;
$indexService = $ecommerceFactory->getIndexService();
$productListing = $indexService->getProductListForCurrentTenant();
if(!isset($params['sort'])){
$params['sort'] = 'o_id-desc';
}
$elements = explode("-", $params['sort']);
$productListing->setOrder(strtoupper($elements[1]));
$productListing->setOrderKey($elements[0]);
$productListing->setVariantMode(ProductListInterface::VARIANT_MODE_HIDE);
$params['productListing'] = $productListing;
// load current filter
if ($category) {
$filterDefinition = $category->getFilterDefinition();
$trackingManager = $ecommerceFactory->getTrackingManager();
$trackingManager->trackCategoryPageView($category->getName(), null);
}
if ($request->get('filterdefinition') instanceof FilterDefinition) {
$filterDefinition = $request->get('filterdefinition');
}
if (empty($filterDefinition)) {
$filterDefinition = Config::getWebsiteConfig()['fallbackFilterdefinition'];
}
$filterService = $ecommerceFactory->getFilterService();
$listHelper->setupProductList($filterDefinition, $productListing, $params, $filterService, true);
$params['filterService'] = $filterService;
$params['filterDefinition'] = $filterDefinition;
/** @var SlidingPagination $paginator */
// init pagination
$paginator = $paginator->paginate(
$productListing,
$request->get('page', 1),
$filterDefinition->getPageLimit()
);
$params['results'] = $paginator;
$params['paginationVariables'] = $paginator->getPaginationData();
if ($request->attributes->get('noLayout')) {
return $this->render('product/listing_content.html.twig', $params);
}
// track product impressions
$trackingManager = $ecommerceFactory->getTrackingManager();
foreach ($paginator as $product) {
$trackingManager->trackProductImpression($product, 'grid');
}
return $this->render('product/index.html.twig', $params);
}
/**
*
* @Route("/{_locale}/product-line/{productlinename}-{productline}", name="shop-productline", requirements={"productlinename"="[\w-]+", "productline"="\d+"})
*
* @param Request $request
* @param HeadTitle $headTitleHelper
* @param Factory $ecommerceFactory
* @param ListHelper $listHelper
*
* @return Response
*/
public function listingProductLines(Request $request, Factory $ecommerceFactory, ListHelper $listHelper, PaginatorInterface $paginator): Response
{
$params = array_merge($request->query->all(), $request->attributes->all());
$productLine = ProductLine::getById($params['productline'] ?? -1);
$indexService = $ecommerceFactory->getIndexService();
$productListing = $indexService->getProductListForCurrentTenant();
if(!isset($params['sort'])){
$params['sort'] = 'o_id-desc';
}
$elements = explode("-", $params['sort']);
$productListing->setOrder(strtoupper($elements[1]));
$productListing->addRelationCondition("productLine", "dest IN (".$productLine->getId().")");
$productListing->setOrderKey($elements[0]);
$productListing->setVariantMode(ProductListInterface::VARIANT_MODE_HIDE);
$params['productListing'] = $productListing;
if ($request->get('filterdefinition') instanceof FilterDefinition) {
$filterDefinition = $request->get('filterdefinition');
}
if (empty($filterDefinition)) {
$filterDefinition = Config::getWebsiteConfig()['fallbackFilterdefinition'];
}
$filterService = $ecommerceFactory->getFilterService();
$listHelper->setupProductList($filterDefinition, $productListing, $params, $filterService, true);
$params['filterService'] = $filterService;
$params['filterDefinition'] = $filterDefinition;
/** @var SlidingPagination $paginator */
// init pagination
$paginator = $paginator->paginate(
$productListing,
$request->get('page', 1),
$filterDefinition->getPageLimit()
);
$params['results'] = $paginator;
$params['paginationVariables'] = $paginator->getPaginationData();
if ($request->attributes->get('noLayout')) {
return $this->render('product/listing_content.html.twig', $params);
}
// track product impressions
$trackingManager = $ecommerceFactory->getTrackingManager();
foreach ($paginator as $product) {
$trackingManager->trackProductImpression($product, 'grid');
}
return $this->render('product/index.html.twig', $params);
}
/**
*
* @Route("/{_locale}/product-group/{productgroupname}-{productgroup}", name="shop-productgroup", requirements={"productgroupname"="[\w-]+", "productgroup"="\d+"})
*
* @param Request $request
* @param HeadTitle $headTitleHelper
* @param Factory $ecommerceFactory
* @param ListHelper $listHelper
*
* @return Response
*/
public function listingProductGroups(Request $request, Factory $ecommerceFactory, ListHelper $listHelper, PaginatorInterface $paginator): Response
{
$params = array_merge($request->query->all(), $request->attributes->all());
$productGroup = ProductGroup::getById($params['productgroup'] ?? -1);
$indexService = $ecommerceFactory->getIndexService();
$productListing = $indexService->getProductListForCurrentTenant();
if(!isset($params['sort'])){
$params['sort'] = 'o_id-desc';
}
$elements = explode("-", $params['sort']);
$productListing->setOrder(strtoupper($elements[1]));
$productListing->addRelationCondition("productGroup", "dest IN (".$productGroup->getId().")");
$productListing->setOrderKey($elements[0]);
$productListing->setVariantMode(ProductListInterface::VARIANT_MODE_HIDE);
$params['productListing'] = $productListing;
if ($request->get('filterdefinition') instanceof FilterDefinition) {
$filterDefinition = $request->get('filterdefinition');
}
if (empty($filterDefinition)) {
$filterDefinition = Config::getWebsiteConfig()['fallbackFilterdefinition'];
}
$filterService = $ecommerceFactory->getFilterService();
$listHelper->setupProductList($filterDefinition, $productListing, $params, $filterService, true);
$params['filterService'] = $filterService;
$params['filterDefinition'] = $filterDefinition;
/** @var SlidingPagination $paginator */
// init pagination
$paginator = $paginator->paginate(
$productListing,
$request->get('page', 1),
$filterDefinition->getPageLimit()
);
$params['results'] = $paginator;
$params['paginationVariables'] = $paginator->getPaginationData();
if ($request->attributes->get('noLayout')) {
return $this->render('product/listing_content.html.twig', $params);
}
// track product impressions
$trackingManager = $ecommerceFactory->getTrackingManager();
foreach ($paginator as $product) {
$trackingManager->trackProductImpression($product, 'grid');
}
return $this->render('product/index.html.twig', $params);
}
/**
* @Route ("/{_locale}/products/{slug}-{productId}", name="product_show", requirements={"slug"="[\w-]+", "productId"="\d+"})
* @param Request $request
* @param int $productId
* @return Response
* @throws \Exception
*/
public function showAction(Request $request, int $productId, ProductLinkGenerator $productLinkGenerator, Translator $translator): Response
{
$product = $this->productRepository->find($productId);
if(empty($product)){
throw $this->createNotFoundException('Product not found');
}
$otherLanguageUrl = str_replace("/" . $request->getLocale() . "/", "/" . ($request->getLocale() == "de" ? "en" : "de") . "/", $request->getRequestUri());
return $this->render('product/show.html.twig', [
'otherLanguageUrl' => $otherLanguageUrl,
'product' => (new ProductDetailDataMapper($product, $translator))->toArray($request),
]);
}
/**
* @Route("/{_locale}/search", name="search", methods={"GET"})
*/
public function searchAction(Request $request, ListHelper $listHelper, Factory $ecommerceFactory, ProductLinkGenerator $productLinkGenerator, Translator $translator, HeadTitle $headTitleHelper, Placeholder $placeholder, PaginatorInterface $paginator): Response
{
$params = $request->query->all();
$indexService = $ecommerceFactory->getIndexService();
$productListing = $indexService->getProductListForCurrentTenant();
$productListing->setVariantMode(ProductListInterface::VARIANT_MODE_HIDE);
$term = strip_tags($request->query->get('term'));
if ($productListing instanceof AbstractElasticSearch) {
// simple elastic search query - uses multi-match query on all defined search_attributes
// $productListing->addQueryCondition($term);
//sample for a more specific elastic search query - not considers search_attributes but provides full flexibility
// this query weights cars more that accessories
$query = [
'function_score' => [
'query' => [
'multi_match' => [
'query' => $term,
'type' => 'cross_fields',
'operator' => 'and',
'fields' => [
'attributes.name^4',
'attributes.name.analyzed',
'attributes.name.analyzed_ngram',
'attributes.manufacturer_name^3',
'attributes.manufacturer_name.analyzed',
'attributes.manufacturer_name.analyzed_ngram',
'attributes.code',
'attributes.code.analyzed',
'attributes.code.analyzed_ngram',
'attributes.carClass',
'attributes.carClass.analyzed',
'attributes.carClass.analyzed_ngram'
]
]
],
'boost_mode' => 'multiply'
]
];
$productListing->addQueryCondition($query, 'searchTerm');
} else {
//default mysql search query condition - would also work for elastic search in that way
$term = trim(preg_replace('/\s+/', ' ', $term));
if (!empty($term)) {
foreach (explode(' ', $term) as $t) {
if(strpos($t, '/') !== false){
$productListing->addCondition('code LIKE "%'.$t.'%"');
}else{
$productListing->addQueryCondition($t);
}
}
}
}
//$productListing->addCondition('code LIKE "%12/5510%"');
if (isset($params['autocomplete'])) {
$resultset = [];
$productListing->setLimit(10);
foreach ($productListing as $product) {
$result['href'] = $productLinkGenerator->generate($product, []);
$result['product'] = $product->getOSName() ?? '';
if ($product instanceof Product) {
$result['product'] .= ' ' . $product->getColor()[0] . ', ' . $product->getShoeForm();
}
$resultset[] = $result;
}
return $this->json($resultset);
}
if ($request->get('filterdefinition') instanceof FilterDefinition) {
$filterDefinition = $params['filterDefinition'] = $request->get('filterdefinition');
}
if (empty($filterDefinition)) {
$filterDefinition = $params['filterDefinition'] = Config::getWebsiteConfig()['fallbackFilterdefinition'];
}
// create and init filter service
$filterService = Factory::getInstance()->getFilterService();
$listHelper->setupProductList($filterDefinition, $productListing, $params, $filterService, true);
$params['filterService'] = $filterService;
$params['productListing'] = $productListing;
// init pagination
$paginator = $paginator->paginate(
$productListing,
$request->query->getInt('page', 1),
$filterDefinition->getPageLimit()
);
$params['results'] = $paginator;
$params['paginationVariables'] = $paginator->getPaginationData();
$trackingManager = $ecommerceFactory->getTrackingManager();
foreach ($paginator as $product) {
$trackingManager->trackProductImpression($product, 'search-results');
}
$params['language'] = $request->getLocale();
$params['term'] = $term;
//$breadcrumbHelperService->enrichGenericDynamicPage($translator->trans('shop.search-result', [$term]));
$headTitleHelper($translator->trans('shop.search-result', [$term]));
return $this->render('product/index.html.twig', $params);
}
}