src/Controller/ProductController.php line 73

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