src/Controller/ProductController.php line 75

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under two different licenses:
  6.  * - GNU General Public License version 3 (GPLv3)
  7.  * - Pimcore Enterprise License (PEL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  *  @license    http://www.pimcore.org/license     GPLv3 and PEL
  13.  */
  14. namespace App\Controller;
  15. use App\Model\Product\AbstractProduct;
  16. use App\Model\Product\AccessoryPart;
  17. use App\Model\Product\Car;
  18. use App\Model\Product\Category;
  19. use App\Services\SegmentTrackingHelperService;
  20. use App\Website\LinkGenerator\ProductLinkGenerator;
  21. use App\Website\Navigation\BreadcrumbHelperService;
  22. use Knp\Component\Pager\Pagination\SlidingPagination;
  23. use Knp\Component\Pager\PaginatorInterface;
  24. use Pimcore\Bundle\EcommerceFrameworkBundle\Factory;
  25. use Pimcore\Bundle\EcommerceFrameworkBundle\FilterService\ListHelper;
  26. use Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\ProductList\DefaultMysql;
  27. use Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\ProductList\ElasticSearch\AbstractElasticSearch;
  28. use Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\ProductList\ProductListInterface;
  29. use Pimcore\Config;
  30. use Pimcore\Model\DataObject\AbstractObject;
  31. use Pimcore\Model\DataObject\Concrete;
  32. use Pimcore\Model\DataObject\Data\UrlSlug;
  33. use Pimcore\Model\DataObject\FilterDefinition;
  34. use Pimcore\Translation\Translator;
  35. use Pimcore\Twig\Extension\Templating\HeadTitle;
  36. use Pimcore\Twig\Extension\Templating\Placeholder;
  37. use Symfony\Component\HttpFoundation\JsonResponse;
  38. use Symfony\Component\HttpFoundation\Request;
  39. use Symfony\Component\HttpFoundation\Response;
  40. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  41. use Symfony\Component\Routing\Annotation\Route;
  42. class ProductController extends BaseController
  43. {
  44.     /**
  45.      * @param Request $request
  46.      * @param AbstractObject $object
  47.      * @param UrlSlug $urlSlug
  48.      *
  49.      * @return Response
  50.      */
  51.     public function productDetailSlugAction(Request $requestAbstractObject $objectUrlSlug $urlSlug)
  52.     {
  53.         return $this->forward('App\Controller\ProductController::detailAction', ['product' => $object]);
  54.     }
  55.     /**
  56.      * @Route("/shop/{path}{productname}~p{product}", name="shop-detail", defaults={"path"=""}, requirements={"path"=".*?", "productname"="[\w-]+", "product"="\d+"})
  57.      *
  58.      * @param Request $request
  59.      * @param HeadTitle $headTitleHelper
  60.      * @param BreadcrumbHelperService $breadcrumbHelperService
  61.      * @param Factory $ecommerceFactory
  62.      * @param SegmentTrackingHelperService $segmentTrackingHelperService
  63.      * @param Concrete $product built-in parameter conversion, please see https://github.com/pimcore/pimcore/pull/5554
  64.      * @param ProductLinkGenerator $productLinkGenerator
  65.      *
  66.      * @return Response
  67.      *
  68.      * @throws \Exception
  69.      */
  70.     public function detailAction(
  71.         Request $request,
  72.         HeadTitle $headTitleHelper,
  73.         BreadcrumbHelperService $breadcrumbHelperService,
  74.         Factory $ecommerceFactory,
  75.         SegmentTrackingHelperService $segmentTrackingHelperService,
  76.         Concrete $product,
  77.         ProductLinkGenerator $productLinkGenerator
  78.     ) {
  79.         if (!(
  80.                 $product && ($product->isPublished() && (($product instanceof Car && $product->getObjectType() == Car::OBJECT_TYPE_ACTUAL_CAR) || $product instanceof AccessoryPart) || $this->verifyPreviewRequest($request$product))
  81.             )
  82.         ) {
  83.             throw new NotFoundHttpException('Product not found.');
  84.         }
  85.         //redirect to main url
  86.         $generatorUrl $productLinkGenerator->generate($product);
  87.         if ($generatorUrl != $request->getPathInfo()) {
  88.             $queryString $request->getQueryString();
  89.             return $this->redirect($generatorUrl . ($queryString '?' $queryString ''));
  90.         }
  91.         $breadcrumbHelperService->enrichProductDetailPage($product);
  92.         $headTitleHelper($product->getOSName());
  93.         $paramBag $this->getAllParameters($request);
  94.         $paramBag['product'] = $product;
  95.         //track segments for personalization
  96.         $segmentTrackingHelperService->trackSegmentsForProduct($product);
  97.         $trackingManager $ecommerceFactory->getTrackingManager();
  98.         $trackingManager->trackProductView($product);
  99.         if ($product instanceof Car) {
  100.             foreach ($product->getAccessories() as $accessory) {
  101.                 $trackingManager->trackProductImpression($accessory'crosssells');
  102.             }
  103.             return $this->render('product/detail.html.twig'$paramBag);
  104.         } elseif ($product instanceof AccessoryPart) {
  105.             // get all compatible products
  106.             $productList $ecommerceFactory->getIndexService()->getProductListForCurrentTenant();
  107.             $productList->setVariantMode(ProductListInterface::VARIANT_MODE_VARIANTS_ONLY);
  108.             if ($productList instanceof DefaultMysql) {
  109.                 $productList->addCondition('o_id IN (' implode(','$product->getCompatibleToProductIds()) . ')''o_id');
  110.             } elseif ($productList instanceof AbstractElasticSearch) {
  111.                 $productList->addCondition(['terms' => ['system.o_id' => $product->getCompatibleToProductIds()]], 'o_id');
  112.             }
  113.             foreach ($productList as $compatibleProduct) {
  114.                 $trackingManager->trackProductImpression($compatibleProduct'crosssells');
  115.             }
  116.             $paramBag['compatibleTo'] = $productList;
  117.             return $this->render('product/detail_accessory.html.twig'$paramBag);
  118.         }
  119.     }
  120.     /**
  121.      * @Route("/shop/{path}{categoryname}~c{category}", name="shop-category", defaults={"path"=""}, requirements={"path"=".*?", "categoryname"="[\w-]+", "category"="\d+"})
  122.      *
  123.      * @param Request $request
  124.      * @param HeadTitle $headTitleHelper
  125.      * @param BreadcrumbHelperService $breadcrumbHelperService
  126.      * @param Factory $ecommerceFactory
  127.      * @param SegmentTrackingHelperService $segmentTrackingHelperService
  128.      * @param ListHelper $listHelper
  129.      *
  130.      * @return Response
  131.      */
  132.     public function listingAction(Request $requestHeadTitle $headTitleHelperBreadcrumbHelperService $breadcrumbHelperServiceFactory $ecommerceFactorySegmentTrackingHelperService $segmentTrackingHelperServiceListHelper $listHelperPaginatorInterface $paginator)
  133.     {
  134.         $params array_merge($request->query->all(), $request->attributes->all());
  135.         //needed to make sure category filter filters for active category
  136.         $params['parentCategoryIds'] = $params['category'] ?? null;
  137.         $category Category::getById($params['category'] ?? null);
  138.         $params['category'] = $category;
  139.         if ($category) {
  140.             $headTitleHelper($category->getName());
  141.             $breadcrumbHelperService->enrichCategoryPage($category);
  142.         }
  143.         $indexService $ecommerceFactory->getIndexService();
  144.         $productListing $indexService->getProductListForCurrentTenant();
  145.         $productListing->setVariantMode(ProductListInterface::VARIANT_MODE_VARIANTS_ONLY);
  146.         $params['productListing'] = $productListing;
  147.         // load current filter
  148.         if ($category) {
  149.             $filterDefinition $category->getFilterdefinition();
  150.             //track segments for personalization
  151.             $segmentTrackingHelperService->trackSegmentsForCategory($category);
  152.             $trackingManager $ecommerceFactory->getTrackingManager();
  153.             $trackingManager->trackCategoryPageView($category->getName(), null);
  154.         }
  155.         if ($request->get('filterdefinition') instanceof FilterDefinition) {
  156.             $filterDefinition $request->get('filterdefinition');
  157.         }
  158.         if (empty($filterDefinition)) {
  159.             $filterDefinition Config::getWebsiteConfig()->get('fallbackFilterdefinition');
  160.         }
  161.         $filterService $ecommerceFactory->getFilterService();
  162.         $listHelper->setupProductList($filterDefinition$productListing$params$filterServicetrue);
  163.         $params['filterService'] = $filterService;
  164.         $params['filterDefinition'] = $filterDefinition;
  165.         /** @var SlidingPagination $paginator */
  166.         // init pagination
  167.         $paginator $paginator->paginate(
  168.             $productListing,
  169.             $request->get('page'1),
  170.             $filterDefinition->getPageLimit()
  171.         );
  172.         $params['results'] = $paginator;
  173.         $params['paginationVariables'] = $paginator->getPaginationData();
  174.         if ($request->attributes->get('noLayout')) {
  175.             return $this->render('product/listing_content.html.twig'$params);
  176.         }
  177.         // track product impressions
  178.         $trackingManager $ecommerceFactory->getTrackingManager();
  179.         foreach ($paginator as $product) {
  180.             $trackingManager->trackProductImpression($product'grid');
  181.         }
  182.         return $this->render('product/listing.html.twig'$params);
  183.     }
  184.     /**
  185.      * @param Request $request
  186.      * @param Factory $ecommerceFactory
  187.      *
  188.      * @return Response
  189.      */
  190.     public function productTeaserAction(Request $requestFactory $ecommerceFactory)
  191.     {
  192.         $paramsBag = [];
  193.         if ($request->get('type') == 'object') {
  194.             AbstractObject::setGetInheritedValues(true);
  195.             $product AbstractProduct::getById($request->get('id'));
  196.             $paramsBag['product'] = $product;
  197.             //track product impression
  198.             $trackingManager $ecommerceFactory->getTrackingManager();
  199.             $trackingManager->trackProductImpression($product'teaser');
  200.             return $this->render('product/product_teaser.html.twig'$paramsBag);
  201.         }
  202.         throw new NotFoundHttpException('Product not found.');
  203.     }
  204.     /**
  205.      * @Route("/search", name="search")
  206.      *
  207.      * @param Request $request
  208.      * @param ListHelper $listHelper
  209.      * @param Factory $ecommerceFactory
  210.      * @param ProductLinkGenerator $productLinkGenerator
  211.      * @param Translator $translator
  212.      * @param BreadcrumbHelperService $breadcrumbHelperService
  213.      * @param HeadTitle $headTitleHelper
  214.      * @param Placeholder $placeholder
  215.      *
  216.      * @return Response|JsonResponse
  217.      */
  218.     public function searchAction(Request $requestListHelper $listHelperFactory $ecommerceFactoryProductLinkGenerator $productLinkGeneratorTranslator $translatorBreadcrumbHelperService $breadcrumbHelperServiceHeadTitle $headTitleHelperPlaceholder $placeholderPaginatorInterface $paginator)
  219.     {
  220.         $params $request->query->all();
  221.         $params['category'] = Category::getById($params['category'] ?? null);
  222.         $indexService $ecommerceFactory->getIndexService();
  223.         $productListing $indexService->getProductListForCurrentTenant();
  224.         $productListing->setVariantMode(ProductListInterface::VARIANT_MODE_VARIANTS_ONLY);
  225.         $term strip_tags($request->get('term'));
  226.         if ($productListing instanceof AbstractElasticSearch) {
  227.             // simple elastic search query - uses multi-match query on all defined search_attributes
  228. //            $productListing->addQueryCondition($term);
  229.             //sample for a more specific elastic search query - not considers search_attributes but provides full flexibility
  230.             // this query weights cars more that accessories
  231.             $query = [
  232.                 'function_score' => [
  233.                     'query' => [
  234.                         'multi_match' => [
  235.                             'query' => $term,
  236.                             'type' => 'cross_fields',
  237.                             'operator' => 'and',
  238.                             'fields' => [
  239.                                 'attributes.name^4',
  240.                                 'attributes.name.analyzed',
  241.                                 'attributes.name.analyzed_ngram',
  242.                                 'attributes.manufacturer_name^3',
  243.                                 'attributes.manufacturer_name.analyzed',
  244.                                 'attributes.manufacturer_name.analyzed_ngram',
  245.                                 'attributes.color',
  246.                                 'attributes.color.analyzed',
  247.                                 'attributes.color.analyzed_ngram',
  248.                                 'attributes.carClass',
  249.                                 'attributes.carClass.analyzed',
  250.                                 'attributes.carClass.analyzed_ngram'
  251.                             ]
  252.                         ]
  253.                     ],
  254.                     'functions' => [
  255.                         [
  256.                             'filter' => ['match' => ['system.o_classId' => 'AP']],
  257.                             'weight' => 1
  258.                         ],
  259.                         [
  260.                             'filter' => ['match' => ['system.o_classId' => 'CAR']],
  261.                             'weight' => 2
  262.                         ]
  263.                     ],
  264.                     'boost_mode' => 'multiply'
  265.                 ]
  266.             ];
  267.             $productListing->addQueryCondition($query'searchTerm');
  268.         } else {
  269.             //default mysql search query condition - would also work for elastic search in that way
  270.             $term trim(preg_replace('/\s+/'' '$term));
  271.             if (!empty($term)) {
  272.                 foreach (explode(' '$term) as $t) {
  273.                     $productListing->addQueryCondition($t);
  274.                 }
  275.             }
  276.         }
  277.         if (isset($params['autocomplete'])) {
  278.             $resultset = [];
  279.             $productListing->setLimit(10);
  280.             foreach ($productListing as $product) {
  281.                 $result['href'] = $productLinkGenerator->generateWithMockup($product, []);
  282.                 if ($product instanceof Car) {
  283.                     $result['product'] = $product->getOSName() . ' ' $product->getColor()[0] . ', ' $product->getCarClass();
  284.                 } else {
  285.                     $result['product'] = $product->getOSName();
  286.                 }
  287.                 $resultset[] = $result;
  288.             }
  289.             return $this->json($resultset);
  290.         }
  291.         $filterDefinition $params['filterDefinition'] = Config::getWebsiteConfig()->get('fallbackFilterdefinition');
  292.         // create and init filter service
  293.         $filterService Factory::getInstance()->getFilterService();
  294.         $listHelper->setupProductList($filterDefinition$productListing$params$filterServicetrue);
  295.         $params['filterService'] = $filterService;
  296.         $params['products'] = $productListing;
  297.         // init pagination
  298.         $paginator $paginator->paginate(
  299.             $productListing,
  300.             $request->get('page'1),
  301.             $filterDefinition->getPageLimit()
  302.         );
  303.         $params['results'] = $paginator;
  304.         $params['paginationVariables'] = $paginator->getPaginationData();
  305.         $trackingManager $ecommerceFactory->getTrackingManager();
  306.         foreach ($paginator as $product) {
  307.             $trackingManager->trackProductImpression($product'search-results');
  308.         }
  309.         //breadcrumbs
  310.         $placeholder('addBreadcrumb')->append([
  311.             'parentId' => $this->document->getId(),
  312.             'id' => 'search-result',
  313.             'label' => $translator->trans('shop.search-result', [$term])
  314.         ]);
  315.         $params['language'] = $request->getLocale();
  316.         $params['term'] = $term;
  317.         $breadcrumbHelperService->enrichGenericDynamicPage($translator->trans('shop.search-result', [$term]));
  318.         $headTitleHelper($translator->trans('shop.search-result', [$term]));
  319.         return $this->render('product/search.html.twig'$params);
  320.     }
  321. }