src/AtechNet/CoreBundle/Helper/Furbify/ProductHelper.php line 5847

Open in your IDE?
  1. <?php
  2. namespace App\AtechNet\CoreBundle\Helper\Furbify;
  3. use App\AtechNet\CoreBundle\Entity\Album;
  4. use App\AtechNet\CoreBundle\Entity\Attribute;
  5. use App\AtechNet\CoreBundle\Entity\AttributeOption;
  6. use App\AtechNet\CoreBundle\Entity\CategoryGroup;
  7. use App\AtechNet\CoreBundle\Entity\CategoryGroupMemberImage;
  8. use App\AtechNet\CoreBundle\Entity\CategoryPage;
  9. use App\AtechNet\CoreBundle\Entity\CategoryPagesPriorityProducts;
  10. use App\AtechNet\CoreBundle\Entity\Coupon;
  11. use App\AtechNet\CoreBundle\Entity\Customer;
  12. use App\AtechNet\CoreBundle\Entity\Domain;
  13. use App\AtechNet\CoreBundle\Entity\Language;
  14. use App\AtechNet\CoreBundle\Entity\Product;
  15. use App\AtechNet\CoreBundle\Entity\ProductAttribute;
  16. use App\AtechNet\CoreBundle\Entity\ProductContainer;
  17. use App\AtechNet\CoreBundle\Entity\ProductGroup;
  18. use App\AtechNet\CoreBundle\Entity\ProductGroupMember;
  19. use App\AtechNet\CoreBundle\Entity\ProductMeta;
  20. use App\AtechNet\CoreBundle\Entity\ProductModelMeta;
  21. use App\AtechNet\CoreBundle\Entity\ProductTag;
  22. use App\AtechNet\CoreBundle\Entity\ProductType;
  23. use App\AtechNet\CoreBundle\Entity\Review;
  24. use App\AtechNet\CoreBundle\Entity\ReviewSurveyMedia;
  25. use App\AtechNet\CoreBundle\Entity\Vendor;
  26. use App\AtechNet\CoreBundle\Service\HistoricalEuroRate;
  27. use App\AtechNet\CoreBundle\Sevices\ProductService;
  28. use App\AtechNet\CoreBundle\Translation\DatabaseTranslator;
  29. use Doctrine\ORM\EntityManager;
  30. use Doctrine\ORM\EntityManagerInterface;
  31. use Doctrine\ORM\Exception\NotSupported;
  32. use Doctrine\ORM\Query;
  33. use Doctrine\ORM\Tools\Pagination\Paginator;
  34. use Psr\Cache\CacheItemPoolInterface;
  35. use Symfony\Bridge\Twig\TokenParser\DumpTokenParser;
  36. use Symfony\Component\Filesystem\Filesystem;
  37. use Symfony\Component\HttpFoundation\Request;
  38. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  39. class ProductHelper
  40. {
  41.     public const CUSTOMER_TYPE_RETAIL = 1;
  42.     public const CUSTOMER_TYPE_WHOLESALE = 2;
  43.     public const AGE_0_to_2_YEARS = 0;
  44.     public const AGE_3_to_5_YEARS = 1;
  45.     public const AGE_6_PLUS_YEARS = 2;
  46.     public const LIST_ALLOWED_ATTRIBUTES_RETAIL = [
  47.         ProductType::PRODUCT_TYPE_ID_MONITOR => [
  48.             Attribute::ATTRIBUTE_ID_SCREEN_SIZE,
  49.             Attribute::ATTRIBUTE_ID_MAXIMUM_RESOLUTION,
  50.             Attribute::ATTRIBUTE_ID_RESPONSE_TIME,
  51.             Attribute::ATTRIBUTE_ID_DISPLAY_TECHNOLOGY,
  52.             Attribute::ATTRIBUTE_ID_ASPECT_RATIO,
  53.         ],
  54.         ProductType::PRODUCT_TYPE_ID_NOTEBOOK => [
  55.             Attribute::ATTRIBUTE_ID_CPU_NB,
  56.             Attribute::ATTRIBUTE_ID_RAM_NB,
  57.             Attribute::ATTRIBUTE_ID_SSD,
  58.             Attribute::ATTRIBUTE_ID_HDD_25,
  59.             Attribute::ATTRIBUTE_ID_SCREEN_SIZE,
  60.             Attribute::ATTRIBUTE_ID_MAXIMUM_RESOLUTION,
  61.             Attribute::ATTRIBUTE_ID_VGA,
  62.             Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM,
  63.         ],
  64.         ProductType::PRODUCT_TYPE_ID_PC => [
  65.             Attribute::ATTRIBUTE_ID_CPU_PC,
  66.             Attribute::ATTRIBUTE_ID_RAM_PC,
  67.             Attribute::ATTRIBUTE_ID_SSD,
  68.             Attribute::ATTRIBUTE_ID_HDD_25,
  69.             Attribute::ATTRIBUTE_ID_HDD_35,
  70.             Attribute::ATTRIBUTE_ID_VGA,
  71.             Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM,
  72.         ],
  73.         ProductType::PRODUCT_TYPE_ID_PC_SET => [
  74.             Attribute::ATTRIBUTE_ID_CPU_PC,
  75.             Attribute::ATTRIBUTE_ID_RAM_PC,
  76.             Attribute::ATTRIBUTE_ID_SSD,
  77.             Attribute::ATTRIBUTE_ID_HDD_35,
  78.             Attribute::ATTRIBUTE_ID_VGA,
  79.             Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM,
  80.         ],
  81.         ProductType::PRODUCT_TYPE_ID_ALL_IN_ONE => [
  82.             Attribute::ATTRIBUTE_ID_CPU_PC,
  83.             Attribute::ATTRIBUTE_ID_RAM_PC,
  84.             Attribute::ATTRIBUTE_ID_SSD,
  85.             Attribute::ATTRIBUTE_ID_HDD_35,
  86.             Attribute::ATTRIBUTE_ID_SCREEN_SIZE,
  87.             Attribute::ATTRIBUTE_ID_MAXIMUM_RESOLUTION,
  88.             Attribute::ATTRIBUTE_ID_VGA,
  89.             Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM,
  90.         ],
  91.         ProductType::PRODUCT_TYPE_ID_PRINTER => [
  92.             Attribute::ATTRIBUTE_ID_PRINTING_TECHNOLOGY,
  93.             Attribute::ATTRIBUTE_ID_PRINTING_COLOR,
  94.             Attribute::ATTRIBUTE_ID_DUPLEX,
  95.             Attribute::ATTRIBUTE_ID_PORTS,
  96.             Attribute::ATTRIBUTE_ID_PRINTING_SPEED,
  97.         ],
  98.         ProductType::PRODUCT_TYPE_ID_ELECTRIC_SCOOTER => [
  99.             Attribute::ATTRIBUTE_ID_MAX_SPEED,
  100.             Attribute::ATTRIBUTE_ID_MAX_RANGE,
  101.             Attribute::ATTRIBUTE_ID_POWER,
  102.         ],
  103.         ProductType::PRODUCT_TYPE_ID_SMARTPHONE => [
  104.             Attribute::ATTRIBUTE_ID_SCREEN_SIZE,
  105.             Attribute::ATTRIBUTE_ID_MAXIMUM_RESOLUTION,
  106.             Attribute::ATTRIBUTE_ID_MEMORY,
  107.             Attribute::ATTRIBUTE_ID_CPU_PHONE,
  108.             Attribute::ATTRIBUTE_ID_RAM,
  109.             Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM,
  110.         ],
  111.         ProductType::PRODUCT_TYPE_ID_TABLET => [
  112.             Attribute::ATTRIBUTE_ID_SCREEN_SIZE,
  113.             Attribute::ATTRIBUTE_ID_MAXIMUM_RESOLUTION,
  114.             Attribute::ATTRIBUTE_ID_MEMORY,
  115.             Attribute::ATTRIBUTE_ID_CPU_PHONE,
  116.             Attribute::ATTRIBUTE_ID_RAM,
  117.             Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM,
  118.         ],
  119.         ProductType::PRODUCT_TYPE_ID_SERVER => [
  120.             Attribute::ATTRIBUTE_ID_QUALITY,
  121.             Attribute::ATTRIBUTE_ID_CPU_PC,
  122.             Attribute::ATTRIBUTE_ID_RAM_PC,
  123.             Attribute::ATTRIBUTE_ID_SSD,
  124.             Attribute::ATTRIBUTE_ID_SSHD35,
  125.             Attribute::ATTRIBUTE_ID_HDD_25,
  126.             Attribute::ATTRIBUTE_ID_HDD_35,
  127.             Attribute::ATTRIBUTE_ID_VGA,
  128.             Attribute::ATTRIBUTE_ID_FORM,
  129.         ],
  130.         ProductType::PRODUCT_TYPE_ID_PROJECTOR => [
  131.             Attribute::ATTRIBUTE_ID_MAXIMUM_RESOLUTION,
  132.             Attribute::ATTRIBUTE_ID_ASPECT_RATIO,
  133.             Attribute::ATTRIBUTE_ID_CONTRAST,
  134.             Attribute::ATTRIBUTE_ID_PROJECTOR_REMOTE,
  135.             Attribute::ATTRIBUTE_ID_LUMEN,
  136.         ],
  137.         ProductType::PRODUCT_TYPE_ID_RAM_PC => [
  138.             Attribute::ATTRIBUTE_ID_MEMORY_TYPE,
  139.             Attribute::ATTRIBUTE_ID_MEMORY_SIZE,
  140.             Attribute::ATTRIBUTE_ID_MEMORY_FREQUENCY,
  141.         ],
  142.         ProductType::PRODUCT_TYPE_ID_CPU => [
  143.             Attribute::ATTRIBUTE_ID_CPU_PC_FAMILY,
  144.             Attribute::ATTRIBUTE_ID_CPU_GENERATION,
  145.             Attribute::ATTRIBUTE_ID_CLOCKSPEED,
  146.         ],
  147.         ProductType::PRODUCT_TYPE_ID_DOCKING_STATION => [
  148.             Attribute::ATTRIBUTE_ID_CONNECTORS,
  149.             Attribute::ATTRIBUTE_ID_DOCKING_STATION_POWER_INPUT,
  150.         ],
  151.         ProductType::PRODUCT_TYPE_ID_NOTEBOOK_BAG => [
  152.             Attribute::ATTRIBUTE_ID_NOTEBOOK_BAG_SIZE,
  153.             Attribute::ATTRIBUTE_ID_QUALITY,
  154.             Attribute::ATTRIBUTE_ID_COLOR,
  155.         ],
  156.     ];
  157.     public const LIST_ALLOWED_ATTRIBUTES_WHOLESALE = [
  158.         ProductType::PRODUCT_TYPE_ID_MONITOR => [
  159.             Attribute::ATTRIBUTE_ID_SCREEN_SIZE,
  160.             Attribute::ATTRIBUTE_ID_MAXIMUM_RESOLUTION,
  161.             Attribute::ATTRIBUTE_ID_RESPONSE_TIME,
  162.             Attribute::ATTRIBUTE_ID_DISPLAY_TECHNOLOGY,
  163.             Attribute::ATTRIBUTE_ID_ASPECT_RATIO,
  164.         ],
  165.         ProductType::PRODUCT_TYPE_ID_NOTEBOOK => [
  166.             Attribute::ATTRIBUTE_ID_CPU_NB,
  167.             Attribute::ATTRIBUTE_ID_RAM_NB,
  168.             Attribute::ATTRIBUTE_ID_SSD,
  169.             Attribute::ATTRIBUTE_ID_HDD_25,
  170.             Attribute::ATTRIBUTE_ID_SCREEN_SIZE,
  171.             Attribute::ATTRIBUTE_ID_MAXIMUM_RESOLUTION,
  172.             Attribute::ATTRIBUTE_ID_VGA,
  173.             Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM,
  174.         ],
  175.         ProductType::PRODUCT_TYPE_ID_PC => [
  176.             Attribute::ATTRIBUTE_ID_CPU_PC,
  177.             Attribute::ATTRIBUTE_ID_RAM_PC,
  178.             Attribute::ATTRIBUTE_ID_SSD,
  179.             Attribute::ATTRIBUTE_ID_HDD_25,
  180.             Attribute::ATTRIBUTE_ID_HDD_35,
  181.             Attribute::ATTRIBUTE_ID_VGA,
  182.             Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM,
  183.         ],
  184.         ProductType::PRODUCT_TYPE_ID_PC_SET => [
  185.             Attribute::ATTRIBUTE_ID_CPU_PC,
  186.             Attribute::ATTRIBUTE_ID_RAM_PC,
  187.             Attribute::ATTRIBUTE_ID_SSD,
  188.             Attribute::ATTRIBUTE_ID_HDD_35,
  189.             Attribute::ATTRIBUTE_ID_VGA,
  190.             Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM,
  191.         ],
  192.         ProductType::PRODUCT_TYPE_ID_ALL_IN_ONE => [
  193.             Attribute::ATTRIBUTE_ID_CPU_PC,
  194.             Attribute::ATTRIBUTE_ID_RAM_PC,
  195.             Attribute::ATTRIBUTE_ID_SSD,
  196.             Attribute::ATTRIBUTE_ID_HDD_35,
  197.             Attribute::ATTRIBUTE_ID_SCREEN_SIZE,
  198.             Attribute::ATTRIBUTE_ID_MAXIMUM_RESOLUTION,
  199.             Attribute::ATTRIBUTE_ID_VGA,
  200.             Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM,
  201.         ],
  202.         ProductType::PRODUCT_TYPE_ID_PRINTER => [
  203.             Attribute::ATTRIBUTE_ID_PRINTING_TECHNOLOGY,
  204.             Attribute::ATTRIBUTE_ID_PRINTING_COLOR,
  205.             Attribute::ATTRIBUTE_ID_DUPLEX,
  206.             Attribute::ATTRIBUTE_ID_PORTS,
  207.             Attribute::ATTRIBUTE_ID_PRINTING_SPEED,
  208.         ],
  209.         ProductType::PRODUCT_TYPE_ID_ELECTRIC_SCOOTER => [
  210.             Attribute::ATTRIBUTE_ID_MAX_SPEED,
  211.             Attribute::ATTRIBUTE_ID_MAX_RANGE,
  212.             Attribute::ATTRIBUTE_ID_POWER,
  213.         ],
  214.         ProductType::PRODUCT_TYPE_ID_SMARTPHONE => [
  215.             Attribute::ATTRIBUTE_ID_SCREEN_SIZE,
  216.             Attribute::ATTRIBUTE_ID_MAXIMUM_RESOLUTION,
  217.             Attribute::ATTRIBUTE_ID_MEMORY,
  218.             Attribute::ATTRIBUTE_ID_CPU_PHONE,
  219.             Attribute::ATTRIBUTE_ID_RAM,
  220.             Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM,
  221.         ],
  222.         ProductType::PRODUCT_TYPE_ID_TABLET => [
  223.             Attribute::ATTRIBUTE_ID_SCREEN_SIZE,
  224.             Attribute::ATTRIBUTE_ID_MAXIMUM_RESOLUTION,
  225.             Attribute::ATTRIBUTE_ID_MEMORY,
  226.             Attribute::ATTRIBUTE_ID_CPU_PHONE,
  227.             Attribute::ATTRIBUTE_ID_RAM,
  228.             Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM,
  229.         ],
  230.         ProductType::PRODUCT_TYPE_ID_SERVER => [
  231.             Attribute::ATTRIBUTE_ID_QUALITY,
  232.             Attribute::ATTRIBUTE_ID_CPU_PC,
  233.             Attribute::ATTRIBUTE_ID_RAM_PC,
  234.             Attribute::ATTRIBUTE_ID_SSD,
  235.             Attribute::ATTRIBUTE_ID_SSHD35,
  236.             Attribute::ATTRIBUTE_ID_HDD_25,
  237.             Attribute::ATTRIBUTE_ID_HDD_35,
  238.             Attribute::ATTRIBUTE_ID_VGA,
  239.             Attribute::ATTRIBUTE_ID_FORM,
  240.         ],
  241.         ProductType::PRODUCT_TYPE_ID_PROJECTOR => [
  242.             Attribute::ATTRIBUTE_ID_MAXIMUM_RESOLUTION,
  243.             Attribute::ATTRIBUTE_ID_ASPECT_RATIO,
  244.             Attribute::ATTRIBUTE_ID_CONTRAST,
  245.             Attribute::ATTRIBUTE_ID_PROJECTOR_REMOTE,
  246.             Attribute::ATTRIBUTE_ID_LUMEN,
  247.         ],
  248.         ProductType::PRODUCT_TYPE_ID_RAM_PC => [
  249.             Attribute::ATTRIBUTE_ID_MEMORY_TYPE,
  250.             Attribute::ATTRIBUTE_ID_MEMORY_SIZE,
  251.             Attribute::ATTRIBUTE_ID_MEMORY_FREQUENCY,
  252.         ],
  253.         ProductType::PRODUCT_TYPE_ID_CPU => [
  254.             Attribute::ATTRIBUTE_ID_CPU_PC_FAMILY,
  255.             Attribute::ATTRIBUTE_ID_CPU_GENERATION,
  256.             Attribute::ATTRIBUTE_ID_CLOCKSPEED,
  257.         ],
  258.         ProductType::PRODUCT_TYPE_ID_DOCKING_STATION => [
  259.             Attribute::ATTRIBUTE_ID_CONNECTORS,
  260.             Attribute::ATTRIBUTE_ID_DOCKING_STATION_POWER_INPUT,
  261.         ],
  262.         ProductType::PRODUCT_TYPE_ID_NOTEBOOK_BAG => [
  263.             Attribute::ATTRIBUTE_ID_NOTEBOOK_BAG_SIZE,
  264.             Attribute::ATTRIBUTE_ID_QUALITY,
  265.             Attribute::ATTRIBUTE_ID_COLOR,
  266.         ],
  267.     ];
  268.     public const DEFAULT_ACCESSORY_SELECTIONS = [
  269.         21 => 247,
  270.         105 => 1536, // USB wifi : I dont need
  271.         116 => 1535, // Speaker : I dont need
  272.         147 => 1522, // DDR 4 PC RAM : I dont need
  273.         149 => 1537, // Headphones : I dont need
  274.     ];
  275.     public const CPU_MARK_HELPER_MIN = [
  276.         1 => 0,
  277.         2 => 3_000,
  278.         3 => 6_000,
  279.         4 => 9_000,
  280.         5 => 11_000,
  281.     ];
  282.     public const CPU_MARK_HELPER_MAX = [
  283.         1 => 3_000,
  284.         2 => 6_000,
  285.         3 => 9_000,
  286.         4 => 11_000,
  287.         5 => 1_000_000,
  288.     ];
  289.     private $entityManager;
  290.     private $cloudFlareApiConfig;
  291.     private $translator;
  292.     private $multiStore;
  293.     private $customerType;
  294.     private $priceDomainId;
  295.     private $localeDomainId;
  296.     private $priceDomain;
  297.     private $localeDomain;
  298.     private $showWindows;
  299.     private $notebookSkinAttribute;
  300.     private $notebookSkinAttributeOptions;
  301.     private $router;
  302.     /**
  303.      * ProductHelper constructor.
  304.      * @param EntityManager $entityManager
  305.      * @param array $cloudFlareApiConfig
  306.      * @param $translator
  307.      * @param MultiStore $multiStore
  308.      * @param $router
  309.      * @throws \Exception
  310.      */
  311.     public function __construct(
  312.         EntityManager $entityManager,
  313.         array $cloudFlareApiConfig,
  314.         $translator,
  315.         MultiStore $multiStore,
  316.         $router,
  317.         private HistoricalEuroRate $historicalEuroRate,
  318.     )
  319.     {
  320.         $this->entityManager = $entityManager;
  321.         $this->cloudFlareApiConfig = $cloudFlareApiConfig;
  322.         $this->translator = $translator;
  323.         $this->multiStore = $multiStore;
  324.         $this->router = $router;
  325.         $this->customerType = $this->multiStore->getCustomerTypeId();
  326.         $this->priceDomainId = $this->multiStore->getDomainId();
  327.         $this->localeDomainId = $this->multiStore->getLocaleDomainId();
  328.         $this->priceDomain = $this->multiStore->getDomain();
  329.         if ($this->priceDomainId == $this->localeDomainId) {
  330.             $this->localeDomain = $this->priceDomain;
  331.         } else {
  332.             $this->localeDomain = $this->multiStore->getLocaleDomain();
  333.             $this->translator = new DatabaseTranslator($this->entityManager, $this->multiStore->getLocaleDomainId());
  334.         }
  335.         return '';
  336.         $this->showWindows = !($this->priceDomain->getType() == 'retail') && !in_array($this->priceDomainId, [Domain::DOMAIN_ID_RT_EU, Domain::DOMAIN_ID_WS_EU]);
  337.     }
  338.     /**
  339.      * @param Product $product
  340.      * @return array
  341.      */
  342.     public function getDataForProduct(Product $product): array
  343.     {
  344.         $priceProductMeta = $product->getDomainMeta($this->priceDomain);
  345.         if ($this->priceDomain->getId() == $this->localeDomain->getId()) {
  346.             $localeProductMeta = $priceProductMeta;
  347.         } else {
  348.             $localeProductMeta = $product->getDomainMeta($this->localeDomain);
  349.         }
  350.         return [
  351.             'product' => $product,
  352.             'priceMeta' => $priceProductMeta,
  353.             'localeMeta' => $localeProductMeta,
  354.         ];
  355.     }
  356.     /**
  357.      * @param int $productId
  358.      * @return array
  359.      */
  360.     public function getDataForProductById($productId)
  361.     {
  362.         $product = $this->entityManager->getRepository(Product::class)->findOneById($productId);
  363.         return $product ? $this->getDataForProduct($product) : null;
  364.     }
  365.     /**
  366.      * @param array $products
  367.      * @return array
  368.      */
  369.     public function getDataForProducts($products)
  370.     {
  371.         $productsData = [];
  372.         foreach ($products as $product) {
  373.             $productsData[$product->getId()] = $this->getDataForProduct($product);
  374.         }
  375.         return $productsData;
  376.     }
  377.     /**
  378.      * @param array $productIds
  379.      * @return array
  380.      */
  381.     public function getDataForProductsById($productIds)
  382.     {
  383.         $productsData = [];
  384.         foreach ($productIds as $productId) {
  385.             $productData = $this->getDataForProductById($productId);
  386.             if ($productData) {
  387.                 $productsData[$productId] = $productData;
  388.             }
  389.         }
  390.         return $productsData;
  391.     }
  392.     /**
  393.      * @param array $products
  394.      * @param bool $withMultiplePicture
  395.      *
  396.      * @return array
  397.      */
  398.     public function getStructuredDataForProducts($products, bool $withMultiplePicture = false): array
  399.     {
  400.         $structuredProducts = [];
  401.         if (count($products) < 1) {
  402.             return $structuredProducts;
  403.         }
  404.         foreach ($products as $product) {
  405.             $structuredProducts[$product->getId()] = $this->getStructuredDataForProduct($product, 'list', $withMultiplePicture);
  406.         }
  407.         return $structuredProducts;
  408.     }
  409.     /**
  410.      * @param array $products
  411.      * @param string $viewMode
  412.      * @return array
  413.      */
  414.     public function getStructuredDataForProductsById(array $products, $viewMode = 'details'): array
  415.     {
  416.         $structuredProducts = [];
  417.         $products = $this->entityManager->getRepository(Product::class)->findBy([
  418.             'id' => $products,
  419.         ]);
  420.         foreach ($products as $product) {
  421.             $structuredProducts[$product->getId()] = $this->getStructuredDataForProduct($product, $viewMode);
  422.         }
  423.         return $structuredProducts;
  424.     }
  425.     /**
  426.      * @param array $attributeList
  427.      * @return string
  428.      */
  429.     private function getAttributeInfoForProductDescription(array $attributeList): string
  430.     {
  431.         $attributeIdToIgnoreForTitle = [
  432.             Attribute::ATTRIBUTE_ID_CPU_GENERATION,
  433.             Attribute::ATTRIBUTE_ID_CPU_NB,
  434.             Attribute::ATTRIBUTE_ID_RAM_NB,
  435.             Attribute::ATTRIBUTE_ID_MAXIMUM_RESOLUTION,
  436.             Attribute::ATTRIBUTE_ID_HDD_35,
  437.             Attribute::ATTRIBUTE_ID_CPU_PC,
  438.             Attribute::ATTRIBUTE_ID_RAM_PC,
  439.             #Attribute::ATTRIBUTE_ID_VGA,
  440.             Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM,
  441.             Attribute::ATTRIBUTE_ID_SCREEN_SIZE,
  442.             Attribute::ATTRIBUTE_ID_RESPONSE_TIME,
  443.             Attribute::ATTRIBUTE_ID_ASPECT_RATIO,
  444.             Attribute::ATTRIBUTE_ID_DISPLAY_TECHNOLOGY,
  445.             Attribute::ATTRIBUTE_ID_CPU_PHONE,
  446.             Attribute::ATTRIBUTE_ID_DISPLAY_TECHNOLOGY,
  447.             Attribute::ATTRIBUTE_ID_MEMORY,
  448.             Attribute::ATTRIBUTE_ID_CONNECTORS,
  449.             Attribute::ATTRIBUTE_ID_QUALITY,
  450.             Attribute::ATTRIBUTE_ID_NOTEBOOK_BAG_SIZE,
  451.             Attribute::ATTRIBUTE_ID_COLOR,
  452.         ];
  453.         $attributeListText = '';
  454.         if (array_key_exists(Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM, $attributeList)) {
  455.             $operationSystem = $attributeList[Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM];
  456.             unset($attributeList[Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM]);
  457.             $attributeList[Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM] = $operationSystem;
  458.         }
  459.         foreach ($attributeList as $attribute) {
  460.             if ($attributeListText != '') {
  461.                 $attributeListText .= ', ';
  462.             }
  463.             foreach ($attribute['options'] as $option) {
  464.                 if (!in_array($attribute['id'], $attributeIdToIgnoreForTitle)) {
  465.                     $attributeTitle = $attribute['id'] == Attribute::ATTRIBUTE_ID_VGA
  466.                         ? ''
  467.                         : ' ' . $attribute['title'];
  468.                 } else {
  469.                     switch ($attribute['id']) {
  470.                         case Attribute::ATTRIBUTE_ID_RAM_NB:
  471.                         case Attribute::ATTRIBUTE_ID_RAM_PC:
  472.                             $attributeTitle = ' RAM';
  473.                             break;
  474.                         case Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM:
  475.                             $attributeTitle = $attribute['title'] . str_contains(strtolower($attribute['title']), 'windows') ? " OS" : '';
  476.                             break;
  477.                         case Attribute::ATTRIBUTE_ID_CPU_PC:
  478.                         case Attribute::ATTRIBUTE_ID_CPU_NB:
  479.                             $attributeListText .= " {$attribute['vendor']} {$option['title']}";
  480.                             $attributeListText = str_replace('AMD AMD', 'AMD', $attributeListText);
  481.                             break;
  482.                         case Attribute::ATTRIBUTE_ID_CONNECTORS:
  483.                             $attributeListText = implode(', ', array_map(
  484.                                 function ($option) {
  485.                                     return in_array($option['title'], ['Videokártya', 'Grafická karta']) ? $option['vendor'] : $option['title'];
  486.                                 },
  487.                                 $attribute['options']
  488.                             ));
  489.                             break;
  490.                         default:
  491.                             $attributeTitle = '';
  492.                             break;
  493.                     }
  494.                 }
  495.                 if (isset($attributeTitle)) {
  496.                     $attributeListText .= " {$option['title']}$attributeTitle";
  497.                 }
  498.             }
  499.         }
  500.         $attributeListText = str_replace('OS OS', 'OS', $attributeListText);
  501.         return $attributeListText;
  502.     }
  503.     /**
  504.      * @param Product $product
  505.      * @param string $viewMode
  506.      * @param bool $withMultipleImage
  507.      *
  508.      * @return array
  509.      * @throws NotSupported
  510.      */
  511.     public function getStructuredDataForProduct(Product $product, string $viewMode = 'details', bool $withMultipleImage = false): array
  512.     {
  513.         /**
  514.          * @var Product $product
  515.          * @var ProductMeta $priceDomain
  516.          * @var ProductMeta $localeDomain
  517.          * @var ProductAttribute $spec
  518.          * @var AttributeOption $option
  519.          * @var AttributeOption $acc
  520.          */
  521.         
  522.         $priceMeta = $product->getDomainMeta($this->priceDomain) ?: null;
  523.         $localeMeta = $product->getDomainMeta($this->localeDomain) ?: null;
  524.         $isCustomerTypeRetail = $this->priceDomain->getType() == 'retail';
  525.         $isWebcamIncluded = false;
  526.         $noWebcamBadge = false;
  527.         $images = [];
  528.         $categoryId = 0;
  529.         $accessoryAttributes = [];
  530.         $specificationAttributes = [];
  531.         $specificationAttributesListText = '';
  532.         $freeShipLimit = self::getFreeShipLimitsForDomain($this->priceDomainId)['freeShipLimit'];
  533.         $containerPartsSpecifications = [];
  534.         $connectedBatteries = [];
  535.         $batterAttributeOptionId = null;
  536.         $isContainer = $product->isContainer();
  537.         $containerWithSkin = false;
  538.         $translatedKeyboardReprint = $this->translator->trans('Keyboard reprint');
  539.         if ($viewMode == 'details') {
  540.             foreach ($product->getSpecificationProductAttributeOptions() as $spec) {
  541.                 $option = $spec->getOption();
  542.                 $attributeId = $option->getAttribute()->getId();
  543.                 if ($attributeId == Attribute::ATTRIBUTE_ID_QUALITY
  544.                     || ($attributeId == Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM && (in_array($this->priceDomainId, [Domain::DOMAIN_ID_RT_EU, Domain::DOMAIN_ID_WS_EU])))) {
  545.                     continue;
  546.                 }
  547.                 if (!isset($specificationAttributes[$attributeId])) {
  548.                     $specificationAttributes[$attributeId] = [
  549.                         'id' => $attributeId,
  550.                         'title' => $option->getAttribute()->getTitle($this->translator),
  551.                         'options' => [],
  552.                     ];
  553.                     if ($attributeId == Attribute::ATTRIBUTE_ID_WEBCAM) {
  554.                         $isWebcamIncluded = true;
  555.                     }
  556.                 }
  557.                 $title = $option->getLongTitle($this->translator);
  558.                 if (empty($title)) {
  559.                     $title = $option->getTitle($this->translator);
  560.                 }
  561.                 $specificationAttributes[$attributeId]['options'][$spec->getId()] = [
  562.                     'id' => $spec->getId(),
  563.                     'attributeOptionId' => $spec->getOption()->getId(),
  564.                     'title' => $spec->getId() == 1623398 ? $option->getTitle($this->translator) : $title,
  565.                     'shortTitle' => $option->getTitle($this->translator),
  566.                     'multiplier' => $spec->getMultiplier(),
  567.                 ];
  568.             }
  569.             if ($product->getType()->getId() == ProductType::PRODUCT_TYPE_ID_MONITOR) {
  570.                 if (isset($specificationAttributes[Attribute::ATTRIBUTE_ID_GRAPHIC_INPUTS])) {
  571.                     $sortOrder = [733, 628, 595];
  572.                     $options = $specificationAttributes[Attribute::ATTRIBUTE_ID_GRAPHIC_INPUTS]['options'];
  573.                     usort($options, function ($a, $b) use ($sortOrder) {
  574.                         return array_search($a['attributeOptionId'], $sortOrder) - array_search($b['attributeOptionId'], $sortOrder);
  575.                     });
  576.                     $formatedOptions = [];
  577.                     foreach ($options as $option) {
  578.                         $formatedOptions[$option['id']] = $option;
  579.                     }
  580.                     $specificationAttributes[Attribute::ATTRIBUTE_ID_GRAPHIC_INPUTS]['options'] = $formatedOptions;
  581.                 }
  582.             }
  583.             if ($isContainer && $product->getType()->getId() == ProductType::PRODUCT_TYPE_ID_PC_SET) {
  584.                 /**
  585.                  * @var ProductContainer $container
  586.                  */
  587.                 // get attributes from main product First
  588.                 $productId = $product->getId();
  589.                 $productTypeId = $product->getType()->getId();
  590.                 foreach ($product->getSpecificationProductAttributeOptions() as $spec) {
  591.                     $option = $spec->getOption();
  592.                     $attributeId = $option->getAttribute()->getId();
  593.                     if (($isCustomerTypeRetail && $attributeId == Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM) ||
  594.                         !in_array($productTypeId, [
  595.                             ProductType::PRODUCT_TYPE_ID_PC,
  596.                             ProductType::PRODUCT_TYPE_ID_NOTEBOOK,
  597.                             ProductType::PRODUCT_TYPE_ID_MONITOR,
  598.                             ProductType::PRODUCT_TYPE_ID_PC_SET,
  599.                         ])
  600.                     ) {
  601.                         continue;
  602.                     }
  603.                     if (!isset($containerPartsSpecifications[$productId])) {
  604.                         $containerPartsSpecifications[$productId]['title'] = $product->getVendor()->getName() . ' ' . $product->getName();
  605.                     }
  606.                     if (!isset($containerPartsSpecifications[$productId]['attributes'][$attributeId])) {
  607.                         $containerPartsSpecifications[$productId]['attributes'][$attributeId] = [
  608.                             'id' => $attributeId,
  609.                             'title' => $option->getAttribute()->getTitle($this->translator),
  610.                             'options' => [],
  611.                         ];
  612.                         if ($attributeId == Attribute::ATTRIBUTE_ID_WEBCAM) {
  613.                             $isWebcamIncluded = true;
  614.                         }
  615.                     }
  616.                     $title = $option->getLongTitle($this->translator);
  617.                     if (empty($title)) {
  618.                         $title = $option->getTitle($this->translator);
  619.                     }
  620.                     $containerPartsSpecifications[$productId]['attributes'][$attributeId]['options'][$spec->getId()] = [
  621.                         'id' => $spec->getId(),
  622.                         'title' => $title,
  623.                         'shortTitle' => $option->getTitle($this->translator),
  624.                         'multiplier' => $spec->getMultiplier(),
  625.                     ];
  626.                 }
  627.                 foreach ($product->getProductContainers() as $container) {
  628.                     $containerProduct = $container->getChild();
  629.                     $containerProductId = $containerProduct->getId();
  630.                     $containerProductTypeId = $containerProduct->getType()->getId();
  631.                     foreach ($containerProduct->getSpecificationProductAttributeOptions() as $spec) {
  632.                         $option = $spec->getOption();
  633.                         $attributeId = $option->getAttribute()->getId();
  634.                         if (($isCustomerTypeRetail && $attributeId == Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM) || !in_array($containerProductTypeId, [ProductType::PRODUCT_TYPE_ID_NOTEBOOK, ProductType::PRODUCT_TYPE_ID_MONITOR, ProductType::PRODUCT_TYPE_ID_PC_SET])) {
  635.                             continue;
  636.                         }
  637.                         if (!isset($containerPartsSpecifications[$containerProductId])) {
  638.                             $containerPartsSpecifications[$containerProductId]['title'] = $containerProduct->getVendor()->getName() . ' ' . $containerProduct->getName();
  639.                         }
  640.                         if (!isset($containerPartsSpecifications[$containerProductId]['attributes'][$attributeId])) {
  641.                             $containerPartsSpecifications[$containerProductId]['attributes'][$attributeId] = [
  642.                                 'id' => $attributeId,
  643.                                 'title' => $option->getAttribute()->getTitle($this->translator),
  644.                                 'options' => [],
  645.                             ];
  646.                             if ($attributeId == Attribute::ATTRIBUTE_ID_WEBCAM) {
  647.                                 $isWebcamIncluded = true;
  648.                             }
  649.                         }
  650.                         $title = $option->getLongTitle($this->translator);
  651.                         if (empty($title)) {
  652.                             $title = $option->getTitle($this->translator);
  653.                         }
  654.                         $containerPartsSpecifications[$containerProductId]['attributes'][$attributeId]['options'][$spec->getId()] = [
  655.                             'id' => $spec->getId(),
  656.                             'title' => $title,
  657.                             'shortTitle' => $option->getTitle($this->translator),
  658.                             'multiplier' => $spec->getMultiplier(),
  659.                         ];
  660.                     }
  661.                 }
  662.             }
  663.             if ($isContainer && $product->getType()->getId() == ProductType::PRODUCT_TYPE_ID_NOTEBOOK) {
  664.                 /** @var ProductContainer $productContainer */
  665.                 foreach ($product->getProductContainers() as $productContainer) {
  666.                     $childProduct = $productContainer->getChild();
  667.                     if ($childProduct) {
  668.                         if ($childProduct->getType()->getId() == ProductType::PRODUCT_TYPE_ID_SERVICE) {
  669.                             if (str_contains($childProduct->getName(), 'Notebook skin')) {
  670.                                 $containerWithSkin = true;
  671.                                 break;
  672.                             }
  673.                         }
  674.                     }
  675.                 }
  676.             }
  677.             if (!$isWebcamIncluded && $product->getType()->getId() == ProductType::PRODUCT_TYPE_ID_NOTEBOOK) {
  678.                 $specificationAttributes[Attribute::ATTRIBUTE_ID_WEBCAM] = [
  679.                     'id' => Attribute::ATTRIBUTE_ID_WEBCAM,
  680.                     'title' => 'Webcam',
  681.                     'options' => [],
  682.                 ];
  683.                 $specificationAttributes[Attribute::ATTRIBUTE_ID_WEBCAM]['options'][1773] = [
  684.                     'id' => 1773,
  685.                     'title' => $this->translator->trans('No'),
  686.                     'shortTitle' => $this->translator->trans('No'),
  687.                     'multiplier' => 0,
  688.                     'color' => '',
  689.                     'bold' => 0,
  690.                 ];
  691.             }
  692.             foreach ($product->getAccessoryAttributeOptions() as $acc) {
  693.                 if ($acc->hasChildren() || (!is_null($acc->getProduct()) && ($acc->getProduct()->getAvailable() < 1 && $acc->getAttribute()->getId() != 184))) {
  694.                     continue;
  695.                 }
  696.                 $attributeId = $acc->getAttribute()->getId();
  697.                 if (!isset($accessoryAttributes[$attributeId])) {
  698.                     $accessoryAttributes[$attributeId] = [
  699.                         'id' => $attributeId,
  700.                         'title' => $acc->getAttribute()->getTitle($this->translator),
  701.                         'options' => [],
  702.                     ];
  703.                 }
  704.                 if (in_array($acc->getTitle(), ["Don't need", "No OS"])) {
  705.                     continue;
  706.                 }
  707.                 //-> Suggested Price of Attribute
  708.                 $attrOptionProductSuggestedPrice = $attrOptionProductVatSuggestedPrice = 0;
  709.                 if ($this->customerType == self::CUSTOMER_TYPE_WHOLESALE) {
  710.                     $attrOptionProduct = $acc->getProduct();
  711.                     if ($attrOptionProduct) {
  712.                         $attrOptionProductPriceMeta = $attrOptionProduct->getDomainMeta($this->priceDomain);
  713.                         if ($attrOptionProductPriceMeta->getDomainSuggestedPrice() > 0 && $attrOptionProductPriceMeta->getDomainVatSuggestedPrice() > 0) {
  714.                             $attrOptionProductSuggestedPrice = $attrOptionProductPriceMeta->getDomainSuggestedPrice();
  715.                             $attrOptionProductVatSuggestedPrice = $attrOptionProductPriceMeta->getDomainVatSuggestedPrice();
  716.                         }
  717.                     }
  718.                     if ($attrOptionProductSuggestedPrice === 0 && $attrOptionProductVatSuggestedPrice === 0) {
  719.                         $attrOptionRetailPriceMeta = $acc->getDomainMeta($this->multiStore->getRetailDomainByWholesaleDomain());
  720.                         $attrOptionProductSuggestedPrice = $attrOptionRetailPriceMeta->getDomainPrice();
  721.                         $attrOptionProductVatSuggestedPrice = $attrOptionRetailPriceMeta->getDomainVatPrice();
  722.                     }
  723.                 }
  724.                 //<- Suggested Price of Attribute
  725.                 $attrOptionPriceMeta = $acc->getDomainMeta($this->priceDomain);
  726.                 $fixedTitle = $this->translator->trans($acc->getTitle());
  727.                 if ($attributeId == Attribute::ATTRIBUTE_ID_ACCESSORY_KEYBOARD_REPRINT && str_contains($fixedTitle, 'Keyboard reprint')) {
  728.                     $fixedTitle = str_replace('Keyboard reprint', $translatedKeyboardReprint, $fixedTitle);
  729.                 }
  730.                 $accessoryAttributes[$attributeId]['options'][$acc->getId()] = [
  731.                     'id'                => $acc->getId(),
  732.                     'title'             => $fixedTitle,
  733.                     'discounted'        => $attrOptionPriceMeta->isDiscounted(),
  734.                     'price'             => $attrOptionPriceMeta->getDomainPrice(),
  735.                     'vatPrice'          => $attrOptionPriceMeta->getDomainVatPrice(),
  736.                     'discount'          => $attrOptionPriceMeta->getDomainDiscount(),
  737.                     'vatDiscounted'     => $attrOptionPriceMeta->getDomainVatDiscount(),
  738.                     'suggestedPrice'    => $attrOptionProductSuggestedPrice,
  739.                     'vatSuggestedPrice' => $attrOptionProductVatSuggestedPrice,
  740.                     'productId'         => $acc->getProduct() != null ? $acc->getProduct()->getId() : 0,
  741.                 ];
  742.             }
  743.             $sortKeyboardPrintByIds = [5495, 2487];
  744.             if ($this->priceDomain->getId() == DOMAIN::DOMAIN_ID_RT_SK || $this->priceDomain->getId() == DOMAIN::DOMAIN_ID_WS_SK || $this->priceDomain->getId() == DOMAIN::DOMAIN_ID_RT_CZ || $this->priceDomain->getId() == DOMAIN::DOMAIN_ID_WS_CZ) {
  745.                 array_unshift($sortKeyboardPrintByIds, 2444);
  746.             }
  747.             
  748.             if ($this->priceDomain->getId() == DOMAIN::DOMAIN_ID_RT_HU || $this->priceDomain->getId() == DOMAIN::DOMAIN_ID_WS_HU) {
  749.                 array_unshift($sortKeyboardPrintByIds, 2446);
  750.             }
  751.             if (!is_null($accessoryAttributes) && isset($accessoryAttributes[184]['options']) && count($accessoryAttributes[184]['options']) > 0) {
  752.                 foreach ($sortKeyboardPrintByIds as $sortKeyboardPrintById) {
  753.                     if (array_key_exists($sortKeyboardPrintById, $accessoryAttributes[184]['options'])) {
  754.                         $val = $accessoryAttributes[184]['options'][$sortKeyboardPrintById];
  755.                         unset($accessoryAttributes[184]['options'][$sortKeyboardPrintById]);
  756.                         $accessoryAttributes[184]['options'][$sortKeyboardPrintById] = $val;
  757.                     }
  758.                 }
  759.                 foreach ($accessoryAttributes[184]['options'] as $attributeOption) {
  760.                     $foundId = false;
  761.                     foreach ($sortKeyboardPrintByIds as $sortKeyboardPrintById) {
  762.                         if ($attributeOption['id'] == $sortKeyboardPrintById) {
  763.                             $foundId = true;
  764.                         }
  765.                     }
  766.                     if (!$foundId) {
  767.                         $val = $accessoryAttributes[184]['options'][$attributeOption['id']];
  768.                         unset($accessoryAttributes[184]['options'][$attributeOption['id']]);
  769.                         $accessoryAttributes[184]['options'][$attributeOption['id']] = $val;
  770.                     }
  771.                 }
  772.             }
  773.             // Add skinnable accessory choices if conditions are met
  774.             if (!$containerWithSkin && self::hasBadgeByStructuredData('Skinable', $product->getBadges())) {
  775.                 if (!$this->notebookSkinAttributeOptions) {
  776.                     $this->notebookSkinAttribute = $this->entityManager->getRepository(Attribute::class)->findOneById(Attribute::ATTRIBUTE_ID_NOTEBOOK_SKIN);
  777.                     $this->notebookSkinAttributeOptions = $this->entityManager->createQuery("SELECT ao
  778.                             FROM AtechNetCoreBundle:AttributeOption ao
  779.                             LEFT JOIN ao.product p
  780.                             WHERE ao.product IS NOT NULL
  781.                                 AND p.available > 0
  782.                                 AND ao.id != 4062
  783.                                 AND ao.archived = 0
  784.                                 AND ao.attribute = " . $this->notebookSkinAttribute->getId())
  785.                         ->getResult();
  786.                 }
  787.                 $notebookSkinAttributeId = $this->notebookSkinAttribute->getId();
  788.                 $accessoryAttributes[$notebookSkinAttributeId] = [
  789.                     'id' => $notebookSkinAttributeId,
  790.                     'title' => $this->notebookSkinAttribute->getTitle($this->translator),
  791.                     'options' => [],
  792.                 ];
  793.                 foreach ($this->notebookSkinAttributeOptions as $notebookSkinAttributeOption) {
  794.                     $attrOptionPriceMeta = $notebookSkinAttributeOption->getDomainMeta($this->priceDomain);
  795.                     $accessoryAttributes[$notebookSkinAttributeId]['options'][$notebookSkinAttributeOption->getId()] = [
  796.                         'id' => $notebookSkinAttributeOption->getId(),
  797.                         'title' => $this->translator->trans($notebookSkinAttributeOption->getTitle()),
  798.                         'discounted' => $attrOptionPriceMeta->isDiscounted(),
  799.                         'price' => $attrOptionPriceMeta->getDomainPrice(),
  800.                         'vatPrice' => $attrOptionPriceMeta->getDomainVatPrice(),
  801.                         'discount' => $attrOptionPriceMeta->getDomainDiscount(),
  802.                         'vatDiscounted' => $attrOptionPriceMeta->getDomainVatDiscount(),
  803.                         'suggestedPrice' => 0,
  804.                         'vatSuggestedPrice' => 0,
  805.                         'productId' => $notebookSkinAttributeOption->getProduct() != null ? $notebookSkinAttributeOption->getProduct()->getId() : 0,
  806.                     ];
  807.                 }
  808.             }
  809.             
  810.             $accessoryOrder = [
  811.                 Attribute::ATTRIBUTE_ID_NOTEBOOK_SKIN,
  812.                 Attribute::ATTRIBUTE_ID_ACCESSORY_KEYBOARD_REPRINT,
  813.                 Attribute::ATTRIBUTE_ID_ACCESSORY_SSD,
  814.                 Attribute::ATTRIBUTE_ID_ACCESSORY_NB_RAM_DDR2,
  815.                 Attribute::ATTRIBUTE_ID_ACCESSORY_NB_RAM_DDR3,
  816.                 Attribute::ATTRIBUTE_ID_ACCESSORY_NB_RAM_DDR4,
  817.                 Attribute::ATTRIBUTE_ID_ACCESSORY_PC_RAM_DDR2,
  818.                 Attribute::ATTRIBUTE_ID_ACCESSORY_PC_RAM_DDR3,
  819.                 Attribute::ATTRIBUTE_ID_ACCESSORY_PC_RAM_DDR4,
  820.             ];
  821.             $accessoryAttributesBuffer = $accessoryAttributes;
  822.             $accessoryAttributes = [];
  823.             foreach ($accessoryOrder as $accId) {
  824.                 if (isset($accessoryAttributesBuffer[$accId])) {
  825.                     $accessoryAttributes[] = $accessoryAttributesBuffer[$accId];
  826.                 }
  827.             }
  828.             foreach ($accessoryAttributesBuffer as $key => $acc) {
  829.                 if (!in_array($key, $accessoryOrder)) {
  830.                     $accessoryAttributes[$key] = $accessoryAttributesBuffer[$key];
  831.                 }
  832.             }
  833.             // Sort Cable Accessory
  834.             if (isset($accessoryAttributes[Attribute::ATTRIBUTE_ID_ACCESSORY_CABLE])) {
  835.                 if ($product->getType()->getId() != ProductType::PRODUCT_TYPE_ID_MONITOR) {
  836.                     $sortOrder = [
  837.                         AttributeOption::ATTRIBUTE_OPTION_ID_DISPLAY_PORT_CABLE,
  838.                         AttributeOption::ATTRIBUTE_OPTION_ID_HDMI_CABLE,
  839.                         AttributeOption::ATTRIBUTE_OPTION_ID_VGA_CABLE,
  840.                         AttributeOption::ATTRIBUTE_OPTION_ID_DVI_CABLE,
  841.                     ];
  842.                     $accessoryAttributeOptionsBuffer = $accessoryAttributes[Attribute::ATTRIBUTE_ID_ACCESSORY_CABLE]['options'];
  843.                     $accessoryAttributes[Attribute::ATTRIBUTE_ID_ACCESSORY_CABLE]['options'] = [];
  844.                     foreach ($sortOrder as $index) {
  845.                         if (isset($accessoryAttributeOptionsBuffer[$index])) {
  846.                             $accessoryAttributes[Attribute::ATTRIBUTE_ID_ACCESSORY_CABLE]['options'][$index] = $accessoryAttributeOptionsBuffer[$index];
  847.                         }
  848.                     }
  849.                 }
  850.                 else {
  851.                     $sortOrder = [
  852.                         AttributeOption::ATTRIBUTE_OPTION_ID_DISPLAY_PORT_CABLE,
  853.                         AttributeOption::ATTRIBUTE_OPTION_ID_HDMI_CABLE,
  854.                         AttributeOption::ATTRIBUTE_OPTION_ID_VGA_CABLE,
  855.                     ];
  856.                     $accessoryAttributeOptionsBuffer = $accessoryAttributes[Attribute::ATTRIBUTE_ID_ACCESSORY_CABLE]['options'];
  857.                     $accessoryAttributes[Attribute::ATTRIBUTE_ID_ACCESSORY_CABLE]['options'] = [];
  858.                     foreach ($sortOrder as $index) {
  859.                         if (isset($accessoryAttributeOptionsBuffer[$index])) {
  860.                             $accessoryAttributes[Attribute::ATTRIBUTE_ID_ACCESSORY_CABLE]['options'][$index] = $accessoryAttributeOptionsBuffer[$index];
  861.                         }
  862.                     }
  863.                 }
  864.             }
  865.             // Remove attributes with no options
  866.             foreach ($accessoryAttributes as $key => $value) {
  867.                 if (count($value['options']) < 1) {
  868.                     unset($accessoryAttributes[$key]);
  869.                 }
  870.             }
  871.             $images = $product?->getAlbum()?->getPhotos() ?? [];
  872.             $connectedBatteries = $product?->getConnectedBatteries() ?? [];
  873.             if (!empty($connectedBatteries)) {
  874.                 $batterAttributeOptionId = ProductService::getBatteryAttributeOptionIdByPrice($product, $this->localeDomain, $this->historicalEuroRate);
  875.             }
  876.         }
  877.         else if ($viewMode == 'list') {
  878.             $listAllowedAttributes = $isCustomerTypeRetail ? self::LIST_ALLOWED_ATTRIBUTES_RETAIL : self::LIST_ALLOWED_ATTRIBUTES_WHOLESALE;
  879.             $specificationAttributes = [];
  880.             if (isset($listAllowedAttributes[$product->getType()->getId()])) {
  881.                 /**
  882.                  * @var ProductAttribute $spec
  883.                  */
  884.                 foreach ($product->getSpecificationProductAttributeOptions() as $spec) {
  885.                     $option = $spec->getOption();
  886.                     $attributeId = $option->getAttribute()->getId();
  887.                     $product = $spec->getProduct();
  888.                     if ($option->getId() == AttributeOption::ATTRIBUTE_OPTION_NO_WEBCAM) {
  889.                         $noWebcamBadge = true;
  890.                     }
  891.                     if (in_array($attributeId, $listAllowedAttributes[$product->getType()->getId()])) {
  892.                         if (!isset($specificationAttributes[$attributeId])) {
  893.                             $specificationAttributes[$attributeId] = [
  894.                                 'id' => $attributeId,
  895.                                 'title' => $option->getAttribute()->getTitle($this->translator),
  896.                                 'vendor' => strtok($option->getLongTitle(), ' '),
  897.                                 'options' => [],
  898.                             ];
  899.                         }
  900.                         $specificationAttributes[$attributeId]['options'][$spec->getId()] = [
  901.                             'id' => $spec->getId(),
  902.                             'title' => $option->getTitle($this->translator),
  903.                             'vendor' => strtok($option->getLongTitle(), ' '),
  904.                             'multiplier' => $spec->getMultiplier(),
  905.                         ];
  906.                     }
  907.                 }
  908.             } else {
  909.                 $specificationCount = 0;
  910.                 /**
  911.                  * @var ProductAttribute $spec
  912.                  */
  913.                 foreach ($product->getSpecificationProductAttributeOptions() as $spec) {
  914.                     $specificationCount++;
  915.                     $option = $spec->getOption();
  916.                     $attributeId = $option->getAttribute()->getId();
  917.                     if (!isset($specificationAttributes[$attributeId])) {
  918.                         $specificationAttributes[$attributeId] = [
  919.                             'id' => $attributeId,
  920.                             'title' => $option->getAttribute()->getTitle($this->translator),
  921.                             'vendor' => strtok($option->getLongTitle(), ' '),
  922.                             'options' => [],
  923.                         ];
  924.                     }
  925.                     $specificationAttributes[$attributeId]['options'][$spec->getId()] = [
  926.                         'id' => $spec->getId(),
  927.                         'title' => $option->getTitle($this->translator),
  928.                         'vendor' => strtok($option->getLongTitle(), ' '),
  929.                         'multiplier' => $spec->getMultiplier(),
  930.                     ];
  931.                     if ($specificationCount >= 4) {
  932.                         break;
  933.                     }
  934.                 }
  935.             }
  936.             $specificationAttributesListText = $this->getAttributeInfoForProductDescription($specificationAttributes);
  937.         }
  938.         if ($priceMeta && $priceMeta->isReady() && $priceMeta->isActive() && $product->isReservable() && ($viewMode == 'details' || $viewMode == 'list')) {
  939.             $outOfStockStatus = $product->getOutOfStockStatus();
  940.             if ($outOfStockStatus == Product::OutOfStockStatus_VisibleOrderable5Days) {
  941.                 $orderWaitingDays = 5;
  942.             } elseif ($outOfStockStatus == Product::OutOfStockStatus_VisibleOrderable10Days) {
  943.                 $orderWaitingDays = 10;
  944.             } elseif ($outOfStockStatus == Product::OutOfStockStatus_VisibleOrderable15Days) {
  945.                 $orderWaitingDays = 15;
  946.             } else {
  947.                 $orderWaitingDays = 0;
  948.             }
  949.         } else {
  950.             $orderWaitingDays = 0;
  951.         }
  952.         if ($viewMode == 'list' || $viewMode == 'cart' || $viewMode == 'popular') {
  953.             foreach ($product->getAlbum()->getPhotos() as $photo) {
  954.                 $images = $photo;
  955.                 break;
  956.             }
  957.         }
  958.         if ($viewMode == 'configurator') {
  959.             $images = $product->getAlbum()->getNthPhoto();
  960.         }
  961.         if ($withMultipleImage)
  962.         {
  963.             $images = $product->getAlbum()->getPhotos()->toArray();
  964.         }
  965.         $review_count = 0;
  966.         $review_stars_sum = 0;
  967.         $review_stars = 0;
  968.         $reviews = [];
  969.         $languageId = $this->multiStore->getDefaultLanguageId();
  970.         $languageIds = [$languageId];
  971.         $czReviewFirst = false;
  972.         if ($languageId == Language::LANGUAGE_ID_CZ) {
  973.             $languageIds[] = Language::LANGUAGE_ID_SK;
  974.             $czReviewFirst = true;
  975.         } elseif ($languageId == Language::LANGUAGE_ID_SK) {
  976.             $languageIds[] = Language::LANGUAGE_ID_CZ;
  977.         }
  978.         $productFamilyReviews = $this->entityManager->createQuery("SELECT r
  979.                     FROM AtechNetCoreBundle:Review r
  980.                     LEFT JOIN r.product p
  981.                     WHERE r.type = :reviewType AND p.name = :productName AND r.language IN(:languageIds) AND r.approved = 1
  982.                     ORDER BY" . ($czReviewFirst ? ' r.language DESC, ' : '') . " r.created DESC")
  983.             ->setParameters([
  984.                 'reviewType' => Review::TYPE_PRODUCT,
  985.                 'productName' => $product->getName(),
  986.                 'languageIds' => $languageIds,
  987.             ])
  988.             ->getResult();
  989.         $fs = new Filesystem();
  990.         /**
  991.          * @var Review $review
  992.          * @var ReviewSurveyMedia $reviewSurveyMedia
  993.          */
  994.         if (count($productFamilyReviews) > 0) {
  995.             foreach ($productFamilyReviews as $review) {
  996.                 $reviewProduct = $review->getProduct();
  997.                 $review_count = $review_count + 1;
  998.                 $review_stars_sum = $review_stars_sum + $review->getStars();
  999.                 $reviewImages = [];
  1000.                 if ($viewMode == 'details') {
  1001.                     foreach ($review->getSurveyMedias() as $reviewSurveyMedia) {
  1002.                         if ($reviewSurveyMedia->isApproved()) {
  1003.                             $surveyMedia = $reviewSurveyMedia->getSurveyMedia();
  1004.                             // CREATE FINAL PATH FOR RESIZED IMAGE
  1005.                             $thumbDirPath = 'storage/image/reviews/' . $review->getId() . '/thumbs';
  1006.                             $popupDirPath = 'storage/image/reviews/' . $review->getId() . '/popup';
  1007.                             $thumbPath = $thumbDirPath . '/' . $surveyMedia->getFilename();
  1008.                             $popupPath = $popupDirPath . '/' . $surveyMedia->getFilename();
  1009.                             if (!$fs->exists($thumbDirPath)) {
  1010.                                 $fs->mkdir($thumbDirPath, 0755);
  1011.                             }
  1012.                             if (!$fs->exists($popupDirPath)) {
  1013.                                 $fs->mkdir($popupDirPath, 0755);
  1014.                             }
  1015.                             // Cloudflare Image (thumb)
  1016.                             $imageThumbPath = $reviewSurveyMedia->getCdnUrl('ProductViewThumb');
  1017.                             // Internal Image (thumb)
  1018.                             if ($imageThumbPath == '' && !$fs->exists($thumbPath)) {
  1019.                                 $resize = new ResizeImage($surveyMedia->getDirectory() . '/' . $surveyMedia->getFilename());
  1020.                                 $resize->resizeTo(Image::PRODUCT_VIEW_THUMB_WIDTH, Image::PRODUCT_VIEW_THUMB_HEIGHT, 'maxWidth');
  1021.                                 $resize->saveImage($thumbPath);
  1022.                                 $imageThumbPath = '/' . $thumbPath;
  1023.                             } elseif ($imageThumbPath == '') {
  1024.                                 $imageThumbPath = '/' . $thumbPath;
  1025.                             }
  1026.                             // Cloudflare Image (popup)
  1027.                             $imagePopupPath = $reviewSurveyMedia->getCdnUrl('ProductViewPop');
  1028.                             // Internal Image (popup)
  1029.                             if ($imagePopupPath == '' && !$fs->exists($popupPath)) {
  1030.                                 $resize = new ResizeImage($surveyMedia->getDirectory() . '/' . $surveyMedia->getFilename());
  1031.                                 $resize->resizeTo(Image::PRODUCT_VIEW_POPUP_WIDTH, Image::PRODUCT_VIEW_POPUP_HEIGHT);
  1032.                                 $resize->saveImage($popupPath);
  1033.                                 $imagePopupPath = '/' . $popupPath;
  1034.                             } elseif ($imagePopupPath == '') {
  1035.                                 $imagePopupPath = '/' . $popupPath;
  1036.                             }
  1037.                             $reviewImages[] = [
  1038.                                 'thumb' => $imageThumbPath,
  1039.                                 'popup' => $imagePopupPath,
  1040.                             ];
  1041.                         }
  1042.                     }
  1043.                 }
  1044.                 if ($viewMode == 'details') {
  1045.                     $reviews[] = [
  1046.                         'stars' => $review->getStars(),
  1047.                         'message' => $review->getText(),
  1048.                         'name' => $review->getName(),
  1049.                         'product_name' => $reviewProduct->getVendor()->getName() . ' ' . $reviewProduct->getName(),
  1050.                         'product_config' => $reviewProduct->getDescription(),
  1051.                         'reviewDate' => $review->getCreated()->format('Y.m.d'),
  1052.                         'reviewImages' => $reviewImages,
  1053.                     ];
  1054.                 }
  1055.             }
  1056.             $review_stars = floor(($review_stars_sum / $review_count) * 2) / 2;
  1057.         }
  1058.         // Badges
  1059.         $badgesRaw = [];
  1060.         if ($noWebcamBadge) {
  1061.             $badgesRaw[] = 'no_webcam';
  1062.         }
  1063.         if ($viewMode == 'details') {
  1064.             $categoryPages = $product->getCategoryPages();
  1065.             foreach ($categoryPages as $categoryPage) {
  1066.                 $categoryId = $categoryPage->getId();
  1067.                 break;
  1068.             }
  1069.         }
  1070.         // Add Quality to Badges
  1071.         $quality = $product->getQualityStatus();
  1072.         if (in_array($quality, [Product::QualityStatus_New, Product::QualityStatus_Platinum, Product::QualityStatus_Gold, Product::QualityStatus_Silver, Product::QualityStatus_Bronze, Product::QualityStatus_Bazaar])) {
  1073.             $badgesRaw[] = strtolower(Product::getQualityStatusTitleForKey($quality));
  1074.         }
  1075.         // Windows
  1076.         $windows = false;
  1077.         $index = count($badgesRaw);
  1078.         $allowedBadges = [
  1079.             'bronze',
  1080.             'silver',
  1081.             'gold',
  1082.             'platinum',
  1083.             'new',
  1084.             'bazaar',
  1085.             'Chromebook',
  1086.             'Windows 11',
  1087.         ];
  1088.         if ($viewMode != 'list' && $viewMode != 'details') {
  1089.             foreach ($product->getBadges() as $badge) {
  1090.                 if (!in_array($badge, $allowedBadges)) {
  1091.                     continue;
  1092.                 }
  1093.                 $isItWindows = strpos($badge, 'win');
  1094.                 if (($isItWindows !== false && !$this->showWindows) || $badge == '24h') {
  1095.                     continue;
  1096.                 }
  1097.                 if ($isItWindows !== false) {
  1098.                     if ($windows !== false) {
  1099.                         unset($badgesRaw[$windows]);
  1100.                     }
  1101.                     $windows = $index;
  1102.                 }
  1103.                 $badgesRaw[$index] = $badge;
  1104.                 $index++;
  1105.             }
  1106.         } else {
  1107.             foreach ($product->getBadges() as $badge) {
  1108.                 if (in_array($badge, $allowedBadges)) {
  1109.                     $badgesRaw[$index] = $badge;
  1110.                 }
  1111.                 $index++;
  1112.             }
  1113.         }
  1114.         // Limit Badges
  1115.         $badges = array_slice($badgesRaw, 0, 4);
  1116.         // Bestseller
  1117.         if ( $product->hasBadgeItem(Product::PRODUCT_BADGE_BESTSELLER) ) {
  1118.             $isBestseller = true;
  1119.         } else {
  1120.             $isBestseller = false;
  1121.         }
  1122.         // Short description
  1123.         if ($this->showWindows) {
  1124.             $shortDescription = $product->getDescription();
  1125.         } else {
  1126.             $shortDescription = str_replace([' | Win 7 Pro COA', ' | Win 7 Home COA', ' | Win 10 Pro', ' | Win 10 Home', ' | MAR Win 10 Pro'], '', $product->getDescription());
  1127.         }
  1128.         $timedSalesStartDate = false;
  1129.         if (!is_null($priceMeta?->getSaleStart())) {
  1130.             $salesStartDate = $priceMeta->getSaleStart();
  1131.             $timedSalesStartDate = [
  1132.                 'year' => $salesStartDate->format('Y'),
  1133.                 'month' => $salesStartDate->format('M'),
  1134.                 'day' => $salesStartDate->format('d'),
  1135.                 'hour' => $salesStartDate->format('H'),
  1136.             ];
  1137.         }
  1138.         $timedSalesEndDate = false;
  1139.         if (!is_null($priceMeta?->getSaleEnd())) {
  1140.             $salesEndDate = $priceMeta->getSaleEnd();
  1141.             $timedSalesEndDate = [
  1142.                 'year' => $salesEndDate->format('Y'),
  1143.                 'month' => $salesEndDate->format('M'),
  1144.                 'day' => $salesEndDate->format('d'),
  1145.                 'hour' => $salesEndDate->format('H'),
  1146.             ];
  1147.         }
  1148.         $currentVatPrice = $priceMeta
  1149.             ? $priceMeta->isDiscounted() ? $priceMeta->getDomainVatDiscount() : $priceMeta->getDomainVatPrice()
  1150.             : null;
  1151.         $couponWithBatch = $this->entityManager->getRepository(Coupon::class)
  1152.             ->getCouponsWithBadge($this->multiStore->getDomainId(), [$product->getType()]);
  1153.         if (!$couponWithBatch?->isAvailableForDiscountedProducts() && $priceMeta->isDiscounted()) {
  1154.             $couponWithBatch = null;
  1155.         }
  1156.         /** @var Coupon $activeCoupon */
  1157.         $activeCoupon = $couponWithBatch ?? $product->getCoupons()->map(function (Coupon $coupon) {
  1158.             if ($coupon->isActive() && $coupon->getDomain()->getId() == $this->priceDomain->getId()) {
  1159.                 return $coupon;
  1160.             }
  1161.             return null;
  1162.         })->filter(function ($coupon) {
  1163.             return !is_null($coupon);
  1164.         })->first() ?: null;
  1165.         if ($activeCoupon?->getCode() == 'SUMMER10') {
  1166.             $activeCoupon = null;
  1167.         }
  1168.         if ($activeCoupon) {
  1169.             switch ($activeCoupon->getDiscountType()) {
  1170.                 case Coupon::DISCOUNT_TYPE_PERCENT:
  1171.                     $discountPercentage = (100 - $activeCoupon->getDiscount()) / 100;
  1172.                     $priceWithCoupon = $priceMeta->getDomainVatPrice() * $discountPercentage;
  1173.                     break;
  1174.                 case Coupon::DISCOUNT_TYPE_PRICE:
  1175.                     $priceWithCoupon = $priceMeta->getDomainVatPrice() - $activeCoupon->getDiscount();
  1176.                     break;
  1177.                 default:
  1178.                     $priceWithCoupon = 0;
  1179.                     break;
  1180.             }
  1181.         } else {
  1182.             $priceWithCoupon = 0;
  1183.         }
  1184.         if ($priceWithCoupon > 0) {
  1185.             if (in_array($this->priceDomain->getId(), [Domain::DOMAIN_ID_WS_HU, Domain::DOMAIN_ID_RT_HU])) {
  1186.                 $priceWithCoupon = ceil($priceWithCoupon / 10) * 10;
  1187.             } elseif (in_array($this->priceDomain->getId(), [Domain::DOMAIN_ID_WS_CZ, Domain::DOMAIN_ID_RT_CZ])) {
  1188.                 $priceWithCoupon = ceil($priceWithCoupon);
  1189.             }
  1190.         }
  1191.         $productModelMeta = $product?->getProductModel()?->getProductModelMetas()->filter(function (ProductModelMeta $productModelMeta) {
  1192.            return $productModelMeta->getLanguage()->getId() == $this->multiStore->getDefaultLanguageId();
  1193.         })->first();
  1194.         $warranties = null;
  1195.         if ($viewMode == 'details') {
  1196.             if (
  1197.                 in_array($product->getType()->getId(), [
  1198.                     ProductType::PRODUCT_TYPE_ID_ALL_IN_ONE,
  1199.                     ProductType::PRODUCT_TYPE_ID_MONITOR,
  1200.                     ProductType::PRODUCT_TYPE_ID_NOTEBOOK,
  1201.                     ProductType::PRODUCT_TYPE_ID_PC,
  1202.                     ProductType::PRODUCT_TYPE_ID_PC_SET,
  1203.                 ])
  1204.                 && $product->getQualityStatus() != Product::QualityStatus_Bazaar
  1205.             ) {
  1206.                 $warranties = [
  1207.                     AttributeOption::ATTRIBUTE_OPTION_1_YEAR_WARRANTY => [
  1208.                         'image'          => 'theme/images/1-year-warranty.svg',
  1209.                         'additionalInfo' => 'total 3 years',
  1210.                         'percentPrice'   => 10,
  1211.                     ],
  1212.                     AttributeOption::ATTRIBUTE_OPTION_2_YEAR_WARRANTY => [
  1213.                         'image'          => 'theme/images/2-year-warranty.svg',
  1214.                         'additionalInfo' => 'total 4 years',
  1215.                         'percentPrice'   => 20,
  1216.                     ],
  1217.                     AttributeOption::ATTRIBUTE_OPTION_3_YEAR_WARRANTY => [
  1218.                         'image'          => 'theme/images/3-year-warranty.svg',
  1219.                         'additionalInfo' => 'total 5 years',
  1220.                         'percentPrice'   => 30,
  1221.                     ],
  1222.                 ];
  1223.             }
  1224.         }
  1225.         return [
  1226.             'id'                      => $product->getId(),
  1227.             'code'                    => $product->getCode(),
  1228.             'partNumber'              => !is_null($product->getPartNumber()) && $product->getPartNumber() != '' ? $product->getPartNumber() : '',
  1229.             'active'                  => $priceMeta?->isActive() ?? false,
  1230.             'listed'                  => $priceMeta?->isListed() ?? false,
  1231.             'orderable'               => $priceMeta && $priceMeta->isActive() && ($product->isAvailable() || ($product->isReservable() && $priceMeta->isReady())) && $priceMeta->isOrderable(),
  1232.             'vendorId'                => $product->getVendor()->getId(),
  1233.             'vendor'                  => $product->getVendor()->getName(),
  1234.             'productTypeId'           => $product->getType()->getId(),
  1235.             'productType'             => $product->getType()->getTitle($this->translator),
  1236.             'title'                   => in_array($this->priceDomainId, [Domain::DOMAIN_ID_RT_EU, Domain::DOMAIN_ID_WS_EU]) ? str_replace([' (Quality: Bazar)', ' (Quality: Bazár)', ' (Quality: Bazár, '], ['', '', ' ('], $product->getName()) : $product->getName(),
  1237.             'width'                   => $product->getWidth(),
  1238.             'height'                  => $product->getHeight(),
  1239.             'length'                  => $product->getlength(),
  1240.             'weight'                  => $product->getWeight(),
  1241.             'shortDescription'        => $shortDescription,
  1242.             'isDiscounted'            => $priceMeta?->isDiscounted() ?? false,
  1243.             'vat'                     => $priceMeta?->getVat() ?? 0,
  1244.             'price'                   => $priceMeta?->getDomainPrice() ?? 0,
  1245.             'rawPrice'                => $priceMeta?->getPrice() ?? 0,
  1246.             'vatPrice'                => $priceMeta?->getDomainVatPrice() ?? 0,
  1247.             'vatPriceWithCoupon'      => $priceWithCoupon,
  1248.             'discountPrice'           => $priceMeta?->getDomainDiscount(),
  1249.             'couponId'                => $activeCoupon?->getId(),
  1250.             'couponCode'              => $activeCoupon?->getCode(),
  1251.             'couponDiscount'          => $activeCoupon?->getDiscount(),
  1252.             'showCouponBadgeOnWebsite'=> $activeCoupon?->isShowBadgeOnWebsite(),
  1253.             'rawDiscountPrice'        => $priceMeta?->getDiscount(),
  1254.             'discountVatPrice'        => $priceMeta?->getDomainVatDiscount(),
  1255.             'suggestedPrice'          => $priceMeta?->getDomainSuggestedPrice(),
  1256.             'suggestedVatPrice'       => $priceMeta?->getDomainVatSuggestedPrice(),
  1257.             'isAvailable'             => $product->isAvailable(),
  1258.             'availableQty'            => $product->getAvailable(),
  1259.             'additionalDetail'        => $localeMeta?->getDetail(),
  1260.             'warranty'                => $priceMeta?->getWarranty(),
  1261.             'bestSeller'              => $priceMeta?->isBestSeller(),
  1262.             'reLenovo'                => $priceMeta?->isReLenovo(),
  1263.             'hpRenew'                 => $priceMeta?->isHpRenew(),
  1264.             'specialPromotion'        => $priceMeta?->isSpecialPromotion(),
  1265.             'badges'                  => $badges, // .label-{badge}
  1266.             'badgesRaw'               => $badgesRaw,
  1267.             'attributes'              => $specificationAttributes,
  1268.             'isContainer'             => $isContainer,
  1269.             'containerParts'          => $containerPartsSpecifications,
  1270.             'attributesListTxt'       => $specificationAttributesListText,
  1271.             'accessories'             => $accessoryAttributes,
  1272.             'defaultOptions'          => self::DEFAULT_ACCESSORY_SELECTIONS,
  1273.             'images'                  => $images,
  1274.             'categoryId'              => $categoryId,
  1275.             'qualityTitle'            => $product->getQualityStatusTitle($product->getQualityStatus()),
  1276.             'qualityStatusId'         => $product->getQualityStatus(),
  1277.             'timedSalesActive'        => $priceMeta?->isTimedSaleActive(true),
  1278.             'timedSalesStartDate'     => $timedSalesStartDate,
  1279.             'timedSalesEndDate'       => $timedSalesEndDate,
  1280.             'reviewCount'             => $review_count,
  1281.             'reviewStars'             => $review_stars,
  1282.             'reviews'                 => $reviews,
  1283.             'freeShipping'            => $freeShipLimit < $currentVatPrice,
  1284.             'isReservable'            => ($priceMeta?->isReady() && $priceMeta?->isActive() && $product->isReservable()),
  1285.             'orderWaitingDays'        => $orderWaitingDays,
  1286.             'outOfStockStatus'        => $product->getOutOfStockStatus(),
  1287.             'productModelDescription' => $productModelMeta ? $this->getProductMetaText($productModelMeta->getText(), $specificationAttributes) : null,
  1288.             'warranties'              => $warranties,
  1289.             'connectedBatteries'      => $connectedBatteries,
  1290.             'batterAttributeOptionId' => $batterAttributeOptionId,
  1291.             'isBestseller'            => $isBestseller, 
  1292.             'isBazaar'                => $product->isBazaar()
  1293.         ];
  1294.     }
  1295.     /**
  1296.      * @param $productIds
  1297.      * @return array
  1298.      */
  1299.     public function getStructuredDataByProductIds($productIds)
  1300.     {
  1301.         $products = [];
  1302.         $productRepo = $this->entityManager->getRepository(Product::class);
  1303.         foreach ($productIds as $productId) {
  1304.             $product = $productRepo->findOneById($productId);
  1305.             if ($product) {
  1306.                 $products[] = $product;
  1307.             }
  1308.         }
  1309.         return $this->getStructuredDataForProducts($products);
  1310.     }
  1311.     /**
  1312.      * @param int $productId
  1313.      * @param string $viewMode
  1314.      * @return array
  1315.      */
  1316.     public function getStructuredDataByProductId(int $productId, string $viewMode = 'details'): array
  1317.     {
  1318.         $productRepo = $this->entityManager->getRepository(Product::class);
  1319.         $product = $productRepo->findOneById($productId);
  1320.         return $product ? $this->getStructuredDataForProduct($product, $viewMode) : [];
  1321.     }
  1322.     /**
  1323.      * @param array $filter
  1324.      * @param array $options
  1325.      * @return array
  1326.      */
  1327.     public function getStructuredDataByFilters(array $filter = [], array $options = []): array
  1328.     {
  1329.         $products = [];
  1330.         if (isset($filter['filterCategoryId'])) {
  1331.             $categoryPage = $this->entityManager->find(CategoryPage::class, $filter['filterCategoryId']);
  1332.             $priorityProducts = $this->getPriorityProductsForCategoryPages($filter, $categoryPage);
  1333.             /**
  1334.              * @var Product $product
  1335.              */
  1336.             foreach ($priorityProducts as $product) {
  1337.                 $products[$product->getId()] = $this->getStructuredDataForProduct($product, 'list');
  1338.             }
  1339.             if (!$this->isUserApplyingFilters($filter, $categoryPage) && count($priorityProducts) > 0) {
  1340.                 $filter['limit'] -= count($priorityProducts) - 1;
  1341.             }
  1342.         }
  1343.         $query = $this->getProductListingQuery($filter, $options);
  1344.         $results = new Paginator($query);
  1345.         /**
  1346.          * @var Product $product
  1347.          */
  1348.         foreach ($results as $product) {
  1349.             $products[$product->getId()] = $this->getStructuredDataForProduct($product, 'list');
  1350.         }
  1351.         return $products;
  1352.     }
  1353.     /**
  1354.      * @param SeoUrlHelper $seoUrlHelper
  1355.      * @param array $filter
  1356.      * @param $translator
  1357.      * @return array
  1358.      */
  1359.     public function getVariantsForFilter(SeoUrlHelper $seoUrlHelper, $translator, array $filter = []): array
  1360.     {
  1361.         $query = $this->getProductVariantsQuery($filter);
  1362.         $results = new Paginator($query, true);
  1363.         $products = [];
  1364.         $i = 0;
  1365.         foreach ($results as $product) {
  1366.             $productData = $this->getStructuredDataByProductId($product->getId(), 'list');
  1367.             $products[$i]['description'] = explode(' | ', $product->getDescription());
  1368.             $products[$i]['link'] = $seoUrlHelper->link('product', [
  1369.                 'product_id' => $product->getId(),
  1370.                 'isBazaar'   => $product->isBazaar()
  1371.             ]);
  1372.             $products[$i]['price'] = $filter['basePrice'] == MultiStore::BASE_PRICE_GROSS ?
  1373.                 ($productData['isDiscounted'] ? $productData['discountVatPrice'] : $productData['vatPrice']) :
  1374.                 ($productData['isDiscounted'] ? $productData['discountPrice'] : $productData['price']);
  1375.             $products[$i]['oldPriceForDiscount'] = $filter['basePrice'] == MultiStore::BASE_PRICE_GROSS ? $productData['vatPrice'] : $productData['price'];
  1376.             $products[$i]['isDiscounted'] = $productData['isDiscounted'];
  1377.             $products[$i]['qualityStatusId'] = $productData['qualityStatusId'];
  1378.             $products[$i]['quality'] = $translator->trans($productData['qualityTitle']);
  1379.             $products[$i]['title'] = $productData['vendor'] . ' ' . $productData['title'];
  1380.             $image = '';
  1381.             if (is_object($productData['images'])) {
  1382.                 $image = $this->getImage($productData['images'], 'product/' . $productData['code'])->resize(Image::PRODUCT_LIST_THUMB_WIDTH, Image::PRODUCT_LIST_THUMB_HEIGHT);
  1383.             }
  1384.             $products[$i]['image'] = $image;
  1385.             $i++;
  1386.         }
  1387.         return $products;
  1388.     }
  1389.     /**
  1390.      * @param array $filter
  1391.      * @return int
  1392.      */
  1393.     public function getTotalByFilters(array $filter = []): int
  1394.     {
  1395.         $query = $this->getProductListingQuery($filter, [
  1396.             'count' => true,
  1397.         ]);
  1398.         $result = $query->getSingleScalarResult();
  1399.         return (int)$result;
  1400.     }
  1401.     /**
  1402.      * @param array $filter
  1403.      * @param array $options
  1404.      * @return array
  1405.      */
  1406.     public function getStructuredDataForWizard(array $filter = [], array $options = []): array
  1407.     {
  1408.         $query = $this->getListForWizard($filter, $options);
  1409.         $results = new Paginator($query, $fetchJoinCollection = true);
  1410.         $products = [];
  1411.         foreach ($results as $result) {
  1412.             $products[$result->getId()] = $this->getStructuredDataForProduct($result, 'list');
  1413.         }
  1414.         return $products;
  1415.     }
  1416.     /**
  1417.      * @param array $filter
  1418.      * @return int
  1419.      */
  1420.     public function getCountForWizard(array $filter = []): int
  1421.     {
  1422.         $query = $this->getListForWizard($filter, [
  1423.             'count' => true,
  1424.         ]);
  1425.         $result = $query->getSingleScalarResult();
  1426.         return (int)$result;
  1427.     }
  1428.     /**
  1429.      * @param array $filter
  1430.      * @param array $options
  1431.      * @return array
  1432.      */
  1433.     public function buildFilterOptions(array $filter, array $options = []): array
  1434.     {
  1435.         // Attribute Filter by Category Page
  1436.         $attributeFilters = [];
  1437.         $vendors = $productTypes = [];
  1438.         // For Latest list page
  1439.         $categoryFilters = [];
  1440.         //Switch for notebook show
  1441.         $notebookShow = false;
  1442.         $useSalesFilters = in_array($filter['filterCategoryId'], CategoryPage::SALE_CATEGORY_PAGES) && !is_null($filter['filterProductType']);
  1443.         if (isset($filter['filterCategoryId']) && $useSalesFilters) {
  1444.             $isOtherCategory = false;
  1445.             if ($filter['filterProductType'] == ProductType::PRODUCT_TYPE_ID_NOTEBOOK) {
  1446.                 $notebookShow = true;
  1447.             } else if ($filter['filterProductType'] == ProductType::PRODUCT_TYPE_ID_OTHER) {
  1448.                 $categoryPage = $filter['filterCategoryId'] > 0
  1449.                     ? $this->entityManager->getRepository(CategoryPage::class)->findOneById($filter['filterCategoryId'])
  1450.                     : false;
  1451.                 if ($categoryPage) {
  1452.                     $isOtherCategory = true;
  1453.                     foreach ($categoryPage->getProducts() as $product) {
  1454.                         $productType = $product->getType();
  1455.                         if (in_array($productType->getId(), ProductType::getSaleProductTypeFilterTypes())) {
  1456.                             continue;
  1457.                         }
  1458.                         if (!isset($productTypes[$productType->getId()])) {
  1459.                             $productTypes[$productType->getId()] = [
  1460.                                 'id'   => $productType->getId(),
  1461.                                 'name' => $productType->getTitle($this->translator),
  1462.                             ];
  1463.                         }
  1464.                     }
  1465.                 }
  1466.             }
  1467.             $filterAttribute = [];
  1468.             if (isset($filter['filterAttribute'])) {
  1469.                 $filterAttribute = $filter['filterAttribute'];
  1470.             }
  1471.             $filters = $isOtherCategory
  1472.                 ? []
  1473.                 : CategoryPage::getFiltersForProductType((int)$filter['filterProductType']);
  1474.             foreach ($filters as $attributeId) {
  1475.                 $attributeFilter = $filterAttribute;
  1476.                 $attribute = $this->entityManager->getRepository(Attribute::class)
  1477.                     ->findOneById($attributeId);
  1478.                 if ($attribute) {
  1479.                     $attributeFilters[$attribute->getId()] = [
  1480.                         'id'      => $attribute->getId(),
  1481.                         'title'   => $attribute->getTitle($this->translator),
  1482.                         'filter'  => $attributeFilter,
  1483.                         'options' => [],
  1484.                     ];
  1485.                 }
  1486.             }
  1487.         }
  1488.         else if (isset($filter['filterCategoryId'])) {
  1489.             $categoryPage = $filter['filterCategoryId'] > 0
  1490.                 ? $this->entityManager->getRepository(CategoryPage::class)->findOneById($filter['filterCategoryId'])
  1491.                 : false;
  1492.             $filterAttribute = [];
  1493.             if (isset($filter['filterAttribute'])) {
  1494.                 $filterAttribute = $filter['filterAttribute'];
  1495.             }
  1496.             if ($categoryPage) {
  1497.                 foreach ($categoryPage->getFilterAttributes() as $categoryFilterAttribute) {
  1498.                     $attributeFilter = $filterAttribute;
  1499.                     $attribute = $categoryFilterAttribute->getAttribute();
  1500.                     $attributeFilters[$attribute->getId()] = [
  1501.                         'id'      => $attribute->getId(),
  1502.                         'title'   => $attribute->getTitle($this->translator),
  1503.                         'filter'  => $attributeFilter,
  1504.                         'options' => [],
  1505.                     ];
  1506.                 }
  1507.             }
  1508.             if ($categoryPage->getProductTypes()->count() == 1) {
  1509.                 $productType = $categoryPage->getProductTypes()->first();
  1510.                 if ($productType->getId() == ProductType::PRODUCT_TYPE_ID_NOTEBOOK) {
  1511.                     $notebookShow = true;
  1512.                 }
  1513.             }
  1514.             if ($categoryPage->getProductTypes()->count() > 1) {
  1515.                 foreach ($categoryPage->getProductTypes() as $key => $productType) {
  1516.                     if ($productType->getId() == ProductType::PRODUCT_TYPE_ID_NOTEBOOK) {
  1517.                         $notebookShow = true;
  1518.                     }
  1519.                     $productTypes[$productType->getId()] = [
  1520.                         'id'   => $productType->getId(),
  1521.                         'name' => $productType->getTitle($this->translator),
  1522.                     ];
  1523.                 }
  1524.             }
  1525.             //show productTypes always
  1526.             if (count($productTypes) == 0) {
  1527.                 /** @var Product $product */
  1528.                 foreach ($categoryPage->getProducts() as $product) {
  1529.                     $productType = $product->getType();
  1530.                     if ($productType->getId() == ProductType::PRODUCT_TYPE_ID_NOTEBOOK) {
  1531.                         $notebookShow = true;
  1532.                     }
  1533.                     $isSetShow = in_array($categoryPage->getId(), [217, 218, 219]) && $productType->getId() == ProductType::PRODUCT_TYPE_ID_NOTEBOOK;
  1534.                     $productTypes[$productType->getId()] = [
  1535.                         'id'   => $productType->getId(),
  1536.                         'name' => $isSetShow ? $this->translator->trans('notebook set') : $productType->getTitle($this->translator),
  1537.                     ];
  1538.                     if ($product->getTags()->count() > 0) {
  1539.                         $newBattery = $product->getTags()->exists(fn(int $index, ProductTag $tag) => $tag->getTitle() == 'New battery - Notebook');
  1540.                         if ($newBattery) {
  1541.                             $attributeFilters['newBattery'] = [
  1542.                                 'id'      => 'newBattery',
  1543.                                 'title'   => $this->translator->trans('New battery'),
  1544.                                 'filter'  => $filterAttribute,
  1545.                                 'options' => [
  1546.                                     [
  1547.                                         'id'        => '1',
  1548.                                         'title'     => $this->translator->trans('Yes'),
  1549.                                         'count'     => 0,
  1550.                                         'sortOrder' => 1,
  1551.                                     ],
  1552.                                 ],
  1553.                             ];
  1554.                         }
  1555.                     }
  1556.                 }
  1557.             }
  1558.             //EXTRA PAGES FOR NOOTEBOOK SHOW
  1559.             if (in_array($categoryPage->getId(), [615, 614, 217, 218, 219, 627, 625])) {
  1560.                 $notebookShow = true;
  1561.             }
  1562.             if ($notebookShow && !isset($attributeFilters['newBattery'])) {
  1563.                 $categoryProductTypes = $categoryPage->getProductTypes();
  1564.                 if ($categoryProductTypes->count() == 1 && $categoryProductTypes->first()->getId() == ProductType::PRODUCT_TYPE_ID_NOTEBOOK) {
  1565.                     $newBatteryProducts = $this->entityManager->getRepository(Product::class)->createQueryBuilder('p')
  1566.                         ->select('COUNT(p.id) as count')
  1567.                         ->innerJoin('p.tags', 't')
  1568.                         ->where('t.title = :newBattery')
  1569.                         ->setParameter('newBattery', 'New battery - Notebook')
  1570.                         ->getQuery()->getSingleScalarResult();
  1571.                     if ($newBatteryProducts > 0) {
  1572.                         $attributeFilters['newBattery'] = [
  1573.                             'id'      => 'newBattery',
  1574.                             'title'   => $this->translator->trans('New battery'),
  1575.                             'filter'  => '',
  1576.                             'options' => [
  1577.                                 [
  1578.                                     'id'        => 'newBattery',
  1579.                                     'title'     => $this->translator->trans('Yes'),
  1580.                                     "count"     => 0,
  1581.                                     'sortOrder' => 1,
  1582.                                 ],
  1583.                             ],
  1584.                         ];
  1585.                     }
  1586.                 }
  1587.             }
  1588.         }
  1589.         else if (isset($filter['isNewProductPage'])) {
  1590.             $categoryFilters = $this->getCategoryFilters($filter['language']);
  1591.         }
  1592.         else if (isset($options['search'])) {
  1593.             // Filter Attribute
  1594.             $filterAttribute = [];
  1595.             if (isset($filter['filterAttribute'])) {
  1596.                 $filterAttribute = $filter['filterAttribute'];
  1597.             }
  1598.             // Add Filter Quality
  1599.             $attribute = $this->entityManager->getRepository(Attribute::class)->findOneById(Attribute::ATTRIBUTE_ID_QUALITY);
  1600.             if ($attribute) {
  1601.                 $attributeFilter = $filterAttribute;
  1602.                 if (isset($attributeFilter[$attribute->getId()])) {
  1603.                     unset($attributeFilter[$attribute->getId()]);
  1604.                 }
  1605.                 $attributeFilters[$attribute->getId()] = [
  1606.                     'id'      => $attribute->getId(),
  1607.                     'title'   => $attribute->getTitle($this->translator),
  1608.                     'filter'  => $attributeFilter,
  1609.                     'options' => [],
  1610.                 ];
  1611.             }
  1612.         }
  1613.         $results = $this->getFilterListingResults($filter, $options);
  1614.         $priceMin = $priceMax = false;
  1615.         
  1616.         $productTypes ? $productTypesSet = true : $productTypesSet = false;
  1617.         if ($notebookShow) {
  1618.             //ADD TN display type
  1619.             $panelIds = $this->getTnDisplayAttributeOptionIds();
  1620.             if ($panelIds != '') {
  1621.                 $attributeFilters[Attribute::ATTRIBUTE_ID_DISPLAY_TECHNOLOGY]['options'][] = [
  1622.                     'id' => $panelIds,
  1623.                     'title' => 'TN',
  1624.                     'sortOrder' => 1,
  1625.                 ];
  1626.             }
  1627.         }
  1628.         /** @var Product $product */
  1629.         foreach ($results as $product) {
  1630.             $vendor = $product->getVendor();
  1631.             if ($vendor) {
  1632.                 if (!isset($vendors[$vendor->getId()])) {
  1633.                     $vendors[$vendor->getId()] = [
  1634.                         'id' => $vendor->getId(),
  1635.                         'name' => $vendor->getName(),
  1636.                         'count' => 0,
  1637.                         'sortOrder' => $vendor->getSortIndex(),
  1638.                     ];
  1639.                 }
  1640.             }
  1641.             $productType = $product->getType();
  1642.             if ($productType && isset($options['search'])) {
  1643.                 if (!isset($productTypes[$productType->getId()])) {
  1644.                     $productTypes[$productType->getId()] = [
  1645.                         'id' => $productType->getId(),
  1646.                         'name' => $productType->getTitle($this->translator),
  1647.                         'count' => 0,
  1648.                     ];
  1649.                 }
  1650.             }
  1651.             $productMeta = $product->getDomainMeta($this->priceDomain);
  1652.             if ($productMeta) {
  1653.                 $price = (float)$productMeta->getCurrentVatPrice();
  1654.                 if ($priceMin === false || $priceMax === false) {
  1655.                     $priceMin = $priceMax = $price;
  1656.                 }
  1657.                 if ($priceMin > $price) {
  1658.                     $priceMin = $price;
  1659.                 }
  1660.                 if ($priceMax < $price) {
  1661.                     $priceMax = $price;
  1662.                 }
  1663.             }
  1664.             if (!empty($attributeFilters)) {
  1665.                 $productAttributes = $product->getProductAttributes();
  1666.                 if ($productAttributes) {
  1667.                     /** @var ProductAttribute $productAttribute */
  1668.                     foreach ($productAttributes as $productAttribute) {
  1669.                         $option = $productAttribute->getOption();
  1670.                         $attribute = $option->getAttribute();
  1671.                         if (isset($attributeFilters[$attribute->getId()]) && isset($attributeFilters[$attribute->getId()]['filter'])) {
  1672.                             if (!isset($attributeFilters[$attribute->getId()]['options'][$option->getId()])) {
  1673.                                 $attributeFilter = $attributeFilters[$attribute->getId()]['filter'];
  1674.                                 $attributeFilter[$attribute->getId()] = $option->getId();
  1675.                                 $attributeFilters[$attribute->getId()]['options'][$option->getId()] = [
  1676.                                     'id' => $option->getId(),
  1677.                                     'title' => $option->getTitle($this->translator),
  1678.                                     'count' => 0,
  1679.                                     'sortOrder' => $option->getSortIndex(),
  1680.                                 ];
  1681.                             }
  1682.                         }
  1683.                     }
  1684.                 }
  1685.             }
  1686.         }
  1687.         if (!$productTypesSet && count($productTypes) < 2) {
  1688.             $productTypes = [];
  1689.         }
  1690.         $sortedVendors = [];
  1691.         if (!empty($vendors)) {
  1692.             //HP, DELL, LENOVO AND APPLE
  1693.             $mainVendors = [];
  1694.             $otherVendors = [];
  1695.             $mainNameHelper = [];
  1696.             $otherNameHelper = [];
  1697.             foreach ($vendors as $key => $row) {
  1698.                 if (in_array($key, Vendor::MAIN_NOTEBOOK_VENDOR_LIST) || in_array($key, explode(',', $filter['filterVendor']))) {
  1699.                     $mainVendors[$key] = $row;
  1700.                     $mainNameHelper[$key] = $row['name'];
  1701.                 } else {
  1702.                     $otherVendors[$key] = $row;
  1703.                     $otherNameHelper[$key] = $row['name'];
  1704.                 }
  1705.             }
  1706.             array_multisort($mainNameHelper, SORT_ASC, $mainVendors);
  1707.             array_multisort($otherNameHelper, SORT_ASC, $otherVendors);
  1708.             if ($notebookShow) {
  1709.                 $sortedVendors = [
  1710.                     'main' => $mainVendors,
  1711.                     'other' => $otherVendors,
  1712.                 ];
  1713.             } else {
  1714.                 $sortedVendors = array_merge($mainVendors, $otherVendors);
  1715.             }
  1716.         }
  1717.         if (!empty($productTypes)) {
  1718.             $name = [];
  1719.             foreach ($productTypes as $key => $row) {
  1720.                 $name[$key] = $row['name'];
  1721.             }
  1722.             array_multisort($name, SORT_ASC, $productTypes);
  1723.         }
  1724.         if (!empty($attributeFilters)) {
  1725.             foreach ($attributeFilters as $attributeId => $attribute) {
  1726.                 $sortOrder = [];
  1727.                 $options = $attribute['options'];
  1728.                 if (in_array($attributeId, [1, 12, 25, 45, 46, 63, 68, 141])) {
  1729.                     foreach ($options as $key => $value) {
  1730.                         $sortOrder[$key] = $value['title'];
  1731.                     }
  1732.                 } else {
  1733.                     foreach ($options as $key => $value) {
  1734.                         $sortOrder[$key] = $value['sortOrder'];
  1735.                     }
  1736.                 }
  1737.                 if ($attributeId == Attribute::ATTRIBUTE_ID_CPU_PC_FAMILY) {
  1738.                     array_multisort($sortOrder, SORT_DESC, $options);
  1739.                 } else {
  1740.                     array_multisort($sortOrder, SORT_ASC, $options);
  1741.                 }
  1742.                 if ($attributeId == Attribute::ATTRIBUTE_ID_CONF_NOTEBOOK_SKIN) {
  1743.                     $this->updateFilterOptionsWithThumbnails($options);
  1744.                 }
  1745.                 $attributeFilters[$attributeId]['options'] = $options;
  1746.             }
  1747.         }
  1748.         return [
  1749.             'list'         => in_array($filter['filterCategoryId'], CategoryPage::SALE_CATEGORY_PAGES) && !$useSalesFilters
  1750.                 ? ['main' => []]
  1751.                 : $this->buildFilterSections($productTypes, $categoryFilters, $sortedVendors, $attributeFilters, $filter, $notebookShow),
  1752.             'priceMin'     => floor($priceMin),
  1753.             'priceMax'     => ceil($priceMax),
  1754.             'cpuMarkMin'   => $filter['filterCpuMarkMin'] ?? null,
  1755.             'cpuMarkMax'   => $filter['filterCpuMarkMax'] ?? null,
  1756.             'notebookShow' => $notebookShow,
  1757.             'productTypes' => $productTypes,
  1758.         ];
  1759.     }
  1760.     /**
  1761.      * @param array $productTypes
  1762.      * @param array $categoryFilters
  1763.      * @param array $vendors
  1764.      * @param array $attributeFilters
  1765.      */
  1766.     private function buildFilterSections(array $productTypes, array $categoryFilters, array $vendors, array $attributeFilters, array $checkedFilterList, bool $notebookShow): array
  1767.     {
  1768.         $filterList = [];
  1769.         if (count($categoryFilters) > 0) {
  1770.             $filterList = [
  1771.                 'categoryFilters' => [
  1772.                     'sortOrder' => 0,
  1773.                     'collapseTitle' => 'Categories',
  1774.                     'templateId' => 'category',
  1775.                     'inputName' => 'category',
  1776.                     'checkedList' => $checkedFilterList['filterCategories'],
  1777.                     'isCollapsed' => false,
  1778.                     'filters' => $categoryFilters,
  1779.                 ],
  1780.             ];
  1781.         }
  1782.         $filterList[] = [
  1783.             'sortOrder' => 1,
  1784.             'collapseTitle' => 'Product type',
  1785.             'templateId' => 'productType',
  1786.             'inputName' => 'product-type',
  1787.             'checkedList' => $checkedFilterList['filterProductType'] ?? null,
  1788.             'isCollapsed' => false,
  1789.             'filters' => $productTypes,
  1790.         ];
  1791.         $filterList[] = [
  1792.             'sortOrder' => 2,
  1793.             'collapseTitle' => 'Brand',
  1794.             'templateId' => 'vendorFilter',
  1795.             'inputName' => 'vendor',
  1796.             'checkedList' => $checkedFilterList['filterVendor'],
  1797.             'isCollapsed' => false,
  1798.             'filters' => $vendors,
  1799.         ];
  1800.         $filterSorting = [
  1801.             $this->translator->trans('Categories')             => 0,
  1802.             $this->translator->trans('Product type')           => 1,
  1803.             $this->translator->trans('Brand')                  => 2,
  1804.             $this->translator->trans('Year of production')     => 3,
  1805.             $this->translator->trans('Quality')                => 4,
  1806.             $this->translator->trans('Operating system')       => 5,
  1807.             $this->translator->trans('Screen size')            => 6,
  1808.             $this->translator->trans('CPU mark')               => 7,
  1809.             $this->translator->trans('RAM size')               => 8,
  1810.             $this->translator->trans('SSD')                    => 9,
  1811.             $this->translator->trans('New battery')            => 10,
  1812.             $this->translator->trans('CPU NB FAMILY')          => 11,
  1813.             $this->translator->trans('Maximum resolution')     => 12,
  1814.             $this->translator->trans('Notebook skin')          => 13,
  1815.             $this->translator->trans('Touchscreen')            => 14,
  1816.             $this->translator->trans('Webcam')                 => 15,
  1817.             $this->translator->trans('Connectors external NB') => 16,
  1818.             $this->translator->trans('NumPad')                 => 17,
  1819.             $this->translator->trans('CPU Generation')         => 18,
  1820.             $this->translator->trans('VGA')                    => 19,
  1821.             $this->translator->trans('RAM NB type')            => 20,
  1822.             $this->translator->trans('Display Technology')     => 21,
  1823.             $this->translator->trans('ODD NB')                 => 22,
  1824.             $this->translator->trans('HDD 2,5"')               => 23,
  1825.             $this->translator->trans('Disk capacity')          => 24,
  1826.             $this->translator->trans('Contrast ratio')         => 25,
  1827.             $this->translator->trans('Keyboard language')      => 26,
  1828.         ];
  1829.         // Attribute Option Groups
  1830.         $attributeOptionGroups = AttributeOption::getAttributeOptionGroups();
  1831.         $count = 0;
  1832.         $sortOrderCount = count($filterSorting);
  1833.         foreach ($attributeFilters as $attributeFilter) {
  1834.             $count++;
  1835.             $extraInfo = null;
  1836.             $customCountToShow = null;
  1837.             if (!isset($attributeFilter['id'])){
  1838.                 continue;
  1839.             }
  1840.             $filterOptions = in_array($attributeFilter['id'], [141, 142]) //142 == CPU NB FAMILY
  1841.                 ? array_reverse($attributeFilter['options'])
  1842.                 : $attributeFilter['options'];
  1843.             if ($notebookShow) {
  1844.                 //Screen size
  1845.                 if ($attributeFilter['id'] == Attribute::ATTRIBUTE_ID_SCREEN_SIZE) {
  1846.                     //NE TOROLD KI A SPACEKETE AZ IDNEL " "
  1847.                     $screenSizeFilter = [
  1848.                         0 => [
  1849.                             'title' => 'filterTranslation.screenSize.small',
  1850.                             'id' => ' ',
  1851.                         ],
  1852.                         1 => [
  1853.                             'title' => 'filterTranslation.screenSize.medium',
  1854.                             'id' => ' ',
  1855.                         ],
  1856.                         2 => [
  1857.                             'title' => 'filterTranslation.screenSize.big',
  1858.                             'id' => ' ',
  1859.                         ],
  1860.                     ];
  1861.                     foreach ($filterOptions as $option) {
  1862.                         preg_match('/\d+,\d+"/', $option['title'], $matches);
  1863.                         if (!empty($matches)) {
  1864.                             $screenSize = (float)str_replace(',', '.', $matches[0]);
  1865.                             if ($screenSize < 13) {
  1866.                                 $screenSizeFilter[0]['id'] .= $option['id'] . ',';
  1867.                             } elseif ($screenSize < 15) {
  1868.                                 $screenSizeFilter[1]['id'] .= $option['id'] . ',';
  1869.                             } else {
  1870.                                 $screenSizeFilter[2]['id'] .= $option['id'] . ',';
  1871.                             }
  1872.                         }
  1873.                     }
  1874.                     // Remove trailing commas from screen size filters
  1875.                     $screenSizeFilter[0]['id'] = rtrim($screenSizeFilter[0]['id'], ',');
  1876.                     $screenSizeFilter[1]['id'] = rtrim($screenSizeFilter[1]['id'], ',');
  1877.                     $screenSizeFilter[2]['id'] = rtrim($screenSizeFilter[2]['id'], ',');
  1878.                     $filterOptions = $screenSizeFilter;
  1879.                 }
  1880.                 //Ram size
  1881.                 if ($attributeFilter['id'] == Attribute::ATTRIBUTE_ID_RAM_SIZE) {
  1882.                     //NE TOROLD KI A SPACEKETE AZ IDNEL " "
  1883.                     $ramSizeFilter = [
  1884.                         0 => [
  1885.                             'title' => 'filterTranslation.ramSize.small',
  1886.                             'id' => ' ',
  1887.                         ],
  1888.                         1 => [
  1889.                             'title' => '8GB',
  1890.                             'id' => ' ',
  1891.                         ],
  1892.                         2 => [
  1893.                             'title' => '16GB',
  1894.                             'id' => ' ',
  1895.                         ],
  1896.                         3 => [
  1897.                             'title' => 'filterTranslation.ramSize.big',
  1898.                             'id' => ' ',
  1899.                         ],
  1900.                     ];
  1901.                     foreach ($filterOptions as $option) {
  1902.                         preg_match('/\d+GB/', $option['title'], $matches);
  1903.                         if (!empty($matches)) {
  1904.                             $ramSize = (int)$matches[0];
  1905.                             if ($ramSize < 8) {
  1906.                                 $ramSizeFilter[0]['id'] .= $option['id'] . ',';
  1907.                             } elseif ($ramSize < 16) {
  1908.                                 $ramSizeFilter[1]['id'] .= $option['id'] . ',';
  1909.                             } elseif ($ramSize == 16) {
  1910.                                 $ramSizeFilter[2]['id'] .= $option['id'] . ',';
  1911.                             } else {
  1912.                                 $ramSizeFilter[3]['id'] .= $option['id'] . ',';
  1913.                             }
  1914.                         }
  1915.                     }
  1916.                     $ramSizeFilter[0]['id'] = rtrim($ramSizeFilter[0]['id'], ',');
  1917.                     $ramSizeFilter[1]['id'] = rtrim($ramSizeFilter[1]['id'], ',');
  1918.                     $ramSizeFilter[2]['id'] = rtrim($ramSizeFilter[2]['id'], ',');
  1919.                     $filterOptions = $ramSizeFilter;
  1920.                 }
  1921.                 //SSD size
  1922.                 if ($attributeFilter['id'] == Attribute::ATTRIBUTE_ID_SSD) {
  1923.                     //NE TOROLD KI A SPACEKETE AZ IDNEL " "
  1924.                     $ssdSizeFilter = [
  1925.                         0 => [
  1926.                             'title' => 'filterTranslation.ssdSize.small',
  1927.                             'id' => ' ',
  1928.                         ],
  1929.                         1 => [
  1930.                             'title' => 'filterTranslation.ssdSize.normal',
  1931.                             'id' => ' ',
  1932.                         ],
  1933.                         2 => [
  1934.                             'title' => 'filterTranslation.ssdSize.medium',
  1935.                             'id' => ' ',
  1936.                         ],
  1937.                         3 => [
  1938.                             'title' => 'filterTranslation.ssdSize.big',
  1939.                             'id' => ' ',
  1940.                         ],
  1941.                     ];
  1942.                     foreach ($filterOptions as $option) {
  1943.                         preg_match('/\d+GB/', $option['title'], $matches);
  1944.                         if (!empty($matches)) {
  1945.                             $ssdSize = (int)$matches[0];
  1946.                             if ($ssdSize < 240) {
  1947.                                 $ssdSizeFilter[0]['id'] .= $option['id'] . ',';
  1948.                             } elseif ($ssdSize <= 256) {
  1949.                                 $ssdSizeFilter[1]['id'] .= $option['id'] . ',';
  1950.                             } elseif ($ssdSize <= 512) {
  1951.                                 $ssdSizeFilter[2]['id'] .= $option['id'] . ',';
  1952.                             } else {
  1953.                                 $ssdSizeFilter[3]['id'] .= $option['id'] . ',';
  1954.                             }
  1955.                         } else {
  1956.                             preg_match('/\d+TB/', $option['title'], $matches);
  1957.                             if (!empty($matches)) {
  1958.                                 $ssdSizeFilter[3]['id'] .= $option['id'] . ',';
  1959.                             }
  1960.                         }
  1961.                     }
  1962.                     $ssdSizeFilter[0]['id'] = rtrim($ssdSizeFilter[0]['id'], ',');
  1963.                     $ssdSizeFilter[1]['id'] = rtrim($ssdSizeFilter[1]['id'], ',');
  1964.                     $ssdSizeFilter[2]['id'] = rtrim($ssdSizeFilter[2]['id'], ',');
  1965.                     $ssdSizeFilter[3]['id'] = rtrim($ssdSizeFilter[2]['id'], ',');
  1966.                     $filterOptions = $ssdSizeFilter;
  1967.                 }
  1968.                 //QUALITY sorting + info text
  1969.                 if ($attributeFilter['id'] == Attribute::ATTRIBUTE_ID_QUALITY) {
  1970.                     usort($filterOptions, function ($a, $b) {
  1971.                         return $a['id'] <=> $b['id'];
  1972.                     });
  1973.                     switch ($this->localeDomainId) {
  1974.                         case Domain::DOMAIN_ID_RT_HU:
  1975.                         case Domain::DOMAIN_ID_WS_HU:
  1976.                             $pageId = 27;
  1977.                             break;
  1978.                         case Domain::DOMAIN_ID_RT_SK:
  1979.                         case Domain::DOMAIN_ID_WS_SK:
  1980.                             $pageId = 11;
  1981.                             break;
  1982.                         case Domain::DOMAIN_ID_RT_CZ:
  1983.                         case Domain::DOMAIN_ID_WS_CZ:
  1984.                             $pageId = 60;
  1985.                             break;
  1986.                         case Domain::DOMAIN_ID_RT_EU:
  1987.                         case Domain::DOMAIN_ID_WS_EU:
  1988.                         case Domain::DOMAIN_ID_B2C_EU:
  1989.                             $pageId = 100;
  1990.                             break;
  1991.                     }
  1992.                     $extraInfo = [
  1993.                         'transKey' => 'filterExtraInfos.quality',
  1994.                         'url' => $this->router->generate('page_show', [
  1995.                             'pageId' => $pageId,
  1996.                         ], UrlGeneratorInterface::ABSOLUTE_URL),
  1997.                     ];
  1998.                 }
  1999.                 //Notebook skin remove and replace with custom hint text
  2000.                 if ($attributeFilter['id'] == Attribute::ATTRIBUTE_ID_CONF_NOTEBOOK_SKIN) {
  2001.                     $filterOptions = [];
  2002.                     $extraInfo = [
  2003.                         'transKey' => 'filterExtraInfos.colors',
  2004.                     ];
  2005.                 }
  2006.                 if ($attributeFilter['id'] == Attribute::ATTRIBUTE_ID_TOUCH_SCREEN) {
  2007.                     $touchScreenFilter = [
  2008.                         [
  2009.                             'id' => AttributeOption::ATTRIBUTE_OPTION_TOUCHSCREEN,
  2010.                             'title' => 'yes',
  2011.                         ],
  2012.                     ];
  2013.                     $filterOptions = $touchScreenFilter;
  2014.                 }
  2015.                 if ($attributeFilter['id'] == Attribute::ATTRIBUTE_ID_WEBCAM) {
  2016.                     $webcamIds = '';
  2017.                     foreach ($filterOptions as $option) {
  2018.                         if ($option['id'] != AttributeOption::ATTRIBUTE_OPTION_NO_WEBCAM) {
  2019.                             $webcamIds .= $option['id'] . ',';
  2020.                         }
  2021.                     }
  2022.                     $webcamIds = rtrim($webcamIds, ',');
  2023.                     $webcamFilter = [
  2024.                         [
  2025.                             'id'    => $webcamIds,
  2026.                             'title' => 'yes',
  2027.                         ],
  2028.                         [
  2029.                             'id'    => AttributeOption::ATTRIBUTE_OPTION_NO_WEBCAM,
  2030.                             'title' => 'no',
  2031.                         ],
  2032.                     ];
  2033.                     $filterOptions = $webcamFilter;
  2034.                 }
  2035.                 //Remove the keyboard languages from the filter
  2036.                 if ($attributeFilter['id'] == Attribute::ATTRIBUTE_ID_KEYBOARD_LANGUAGE) {
  2037.                     $filterOptions = [];
  2038.                 }
  2039.                 if ($attributeFilter['id'] == Attribute::ATTRIBUTE_ID_EXTERNAL_CONNECTORS) {
  2040.                     $connectorsFilter = [];
  2041.                     $textToFinc = [
  2042.                         'HDMI',
  2043.                         'VGA',
  2044.                         'Display port',
  2045.                         'Thunderbolt',
  2046.                         'USB 2',
  2047.                         'USB 3',
  2048.                         'USB Type-C',
  2049.                         'RJ45 (LAN)',
  2050.                     ];
  2051.                     foreach ($filterOptions as $option) {
  2052.                         foreach ($textToFinc as $key => $title) {
  2053.                             //option title contains the title
  2054.                             if (str_contains(strtoupper($option['title']), strtoupper($title))) {
  2055.                                 if (!isset($connectorsFilter[$key])) {
  2056.                                     $connectorsFilter[$key] = [
  2057.                                         'id' => $option['id'],
  2058.                                         'title' => $title,
  2059.                                     ];
  2060.                                 } else {
  2061.                                     $connectorsFilter[$key]['id'] .= ',' . $option['id'];
  2062.                                 }
  2063.                             }
  2064.                         }
  2065.                     }
  2066.                     ksort($connectorsFilter);
  2067.                     $filterOptions = $connectorsFilter;
  2068.                 }
  2069.                 if ($attributeFilter['id'] == Attribute::ATTRIBUTE_ID_NUMPAD) {
  2070.                     $numpadFilter = [
  2071.                         [
  2072.                             'id' => AttributeOption::ATTRIBUTE_OPTION_NUMPAD,
  2073.                             'title' => 'yes',
  2074.                         ], [
  2075.                             'id' => AttributeOption::ATTRIBUTE_OPTION_NO_NUMPAD,
  2076.                             'title' => 'no',
  2077.                         ],
  2078.                     ];
  2079.                     $filterOptions = $numpadFilter;
  2080.                 }
  2081.                 if ($attributeFilter['id'] == Attribute::ATTRIBUTE_ID_DISPLAY_TECHNOLOGY) {
  2082.                     $order = ['TN', 'IPS', 'OLED'];
  2083.                     usort($filterOptions, function ($a, $b) use ($order) {
  2084.                         return array_search($a['title'], $order) <=> array_search($b['title'], $order);
  2085.                     });
  2086.                 }
  2087.                 if ($attributeFilter['id'] == Attribute::ATTRIBUTE_ID_OPTICAL_DRIVER) {
  2088.                     $opticalDriverIdString = ' ';
  2089.                     foreach ($filterOptions as $option) {
  2090.                         if ($option['id'] != AttributeOption::ATTRIBUTE_OPTION_NO_OOD) {
  2091.                             $opticalDriverIdString .= $option['id'] . ',';
  2092.                         }
  2093.                     }
  2094.                     $opticalDriverIdString = rtrim($opticalDriverIdString, ',');
  2095.                     $opticalDriverFilter = [
  2096.                         [
  2097.                             'id' => $opticalDriverIdString,
  2098.                             'title' => 'yes',
  2099.                         ], [
  2100.                             'id' => AttributeOption::ATTRIBUTE_OPTION_NO_OOD,
  2101.                             'title' => 'no',
  2102.                         ],
  2103.                     ];
  2104.                     $filterOptions = $opticalDriverFilter;
  2105.                 }
  2106.                 if ($attributeFilter['id'] == Attribute::ATTRIBUTE_ID_VGA) {
  2107.                     //NE TOROLD KI A SPACEKETE AZ IDNEL " "
  2108.                     $vgaFilter = [
  2109.                         [
  2110.                             'id' => ' ',
  2111.                             'title' => 'filterTranslation.vga.integrated',
  2112.                         ], [
  2113.                             'id' => ' ',
  2114.                             'title' => 'filterTranslation.vga.gaming',
  2115.                         ], [
  2116.                             'id' => ' ',
  2117.                             'title' => 'filterTranslation.vga.graphic',
  2118.                         ],
  2119.                     ];
  2120.                     foreach ($filterOptions as $option) {
  2121.                         //Gaming
  2122.                         if (str_contains(strtoupper($option['title']), 'RTX')
  2123.                             || str_contains(strtoupper($option['title']), 'GTX')
  2124.                             || str_contains(strtoupper($option['title']), 'GT')) {
  2125.                             $vgaFilter[1]['id'] .= ',' . $option['id'];
  2126.                         }
  2127.                         //Graphic
  2128.                         elseif (str_contains(strtoupper($option['title']), 'QUADRO')
  2129.                             || str_contains(strtoupper($option['title']), 'FIREPRO')) {
  2130.                             $vgaFilter[2]['id'] .= ',' . $option['id'];
  2131.                             //Other
  2132.                         } else {
  2133.                             $vgaFilter[0]['id'] .= ',' . $option['id'];
  2134.                         }
  2135.                     }
  2136.                     $filterOptions = $vgaFilter;
  2137.                 }
  2138.                 if ($attributeFilter['id'] == Attribute::ATTRIBUTE_ID_YEAR_OF_PRODUCTION) {
  2139.                     $yearFilter = [
  2140.                         [
  2141.                             'id' => $this->getAttributeOptionIdsBasedOnTheAge(self::AGE_0_to_2_YEARS),
  2142.                             'title' => '0-2',
  2143.                         ], [
  2144.                             'id' => $this->getAttributeOptionIdsBasedOnTheAge(self::AGE_3_to_5_YEARS),
  2145.                             'title' => '3-5',
  2146.                         ], [
  2147.                             'id' => $this->getAttributeOptionIdsBasedOnTheAge(self::AGE_6_PLUS_YEARS),
  2148.                             'title' => '6+',
  2149.                         ],
  2150.                     ];
  2151.                     $filterOptions = $yearFilter;
  2152.                 }
  2153.                 if ($attributeFilter['id'] == Attribute::ATTRIBUTE_ID_ACCESSORY_OS) {
  2154.                     $osFilter = [
  2155.                         1 => [
  2156.                             'title' => 'Windows 11',
  2157.                             'id' => ' ',
  2158.                         ],
  2159.                         0 => [
  2160.                             'title' => 'Windows 10',
  2161.                             'id' => ' ',
  2162.                         ],
  2163.                         2 => [
  2164.                             'title' => 'Mac OS',
  2165.                             'id' => ' ',
  2166.                         ],
  2167.                     ];
  2168.                     foreach ($filterOptions as $option) {
  2169.                         if (str_contains(strtoupper($option['title']), 'WINDOWS 10')) {
  2170.                             $osFilter[0]['id'] .= $option['id'] . ',';
  2171.                         } elseif (str_contains(strtoupper($option['title']), 'WINDOWS 11')) {
  2172.                             $osFilter[1]['id'] .= $option['id'] . ',';
  2173.                         } elseif (str_contains(strtoupper($option['title']), 'MAC OS') || str_contains(strtoupper($option['title']), 'MACOS')) {
  2174.                             $osFilter[2]['id'] .= $option['id'] . ',';
  2175.                         }
  2176.                     }
  2177.                     $osFilter[0]['id'] = rtrim($osFilter[0]['id'], ',');
  2178.                     $osFilter[1]['id'] = rtrim($osFilter[1]['id'], ',');
  2179.                     $osFilter[2]['id'] = rtrim($osFilter[2]['id'], ',');
  2180.                     $filterOptions = $osFilter;
  2181.                 }
  2182.                 if ($attributeFilter['id'] == Attribute::ATTRIBUTE_ID_MAXIMUM_RESOLUTION) {
  2183.                     $resolutionFilter = [
  2184.                         0 => [
  2185.                             'title' => 'filterTranslation.resolution.small',
  2186.                             'id' => ' ',
  2187.                         ],
  2188.                         1 => [
  2189.                             'title' => 'filterTranslation.resolution.fullHd',
  2190.                             'id' => ' ',
  2191.                         ],
  2192.                         2 => [
  2193.                             'title' => 'filterTranslation.resolution.big',
  2194.                             'id' => ' ',
  2195.                         ],
  2196.                     ];
  2197.                     foreach ($filterOptions as $option) {
  2198.                         if (preg_match('/(\d+)\s*x/', $option['title'], $matches)) {
  2199.                             $numberBeforeX = $matches[1];
  2200.                             if ($numberBeforeX < 1920) {
  2201.                                 $resolutionFilter[0]['id'] .= $option['id'] . ',';
  2202.                             } elseif ($numberBeforeX == 1920) {
  2203.                                 if (preg_match('/x\s*(\d+)/', $option['title'], $matches)) {
  2204.                                     $numberAfterX = $matches[1];
  2205.                                     if (in_array($numberAfterX, [1080, 1200])) {
  2206.                                         $resolutionFilter[1]['id'] .= $option['id'] . ',';
  2207.                                     } else {
  2208.                                         $resolutionFilter[2]['id'] .= $option['id'] . ',';
  2209.                                     }
  2210.                                 }
  2211.                             } else {
  2212.                                 $resolutionFilter[2]['id'] .= $option['id'] . ',';
  2213.                             }
  2214.                         }
  2215.                     }
  2216.                     $resolutionFilter[0]['id'] = rtrim($resolutionFilter[0]['id'], ',');
  2217.                     $resolutionFilter[1]['id'] = rtrim($resolutionFilter[1]['id'], ',');
  2218.                     $resolutionFilter[2]['id'] = rtrim($resolutionFilter[2]['id'], ',');
  2219.                     $customCountToShow = 3;
  2220.                     $filterOptions = array_merge($resolutionFilter, $filterOptions);
  2221.                 }
  2222.                 if ($attributeFilter['id'] == Attribute::ATTRIBUTE_ID_CPU_MARK) {
  2223.                     $pageId = match ($this->localeDomain->getId()) {
  2224.                         Domain::DOMAIN_ID_RT_HU, Domain::DOMAIN_ID_WS_HU => 175,
  2225.                         Domain::DOMAIN_ID_RT_SK, Domain::DOMAIN_ID_WS_SK => 176,
  2226.                         Domain::DOMAIN_ID_RT_CZ, Domain::DOMAIN_ID_WS_CZ => 177,
  2227.                         Domain::DOMAIN_ID_RT_EU, Domain::DOMAIN_ID_WS_EU => 178,
  2228.                     };
  2229.                     $extraInfo = [
  2230.                         'transKey' => 'filterExtraInfos.cpu_mark',
  2231.                         'url' => $this->router->generate('page_show', [
  2232.                             'pageId' => $pageId,
  2233.                         ], UrlGeneratorInterface::ABSOLUTE_URL),
  2234.                     ];
  2235.                 }
  2236.             }
  2237.             else {
  2238.                 // Merge option rows
  2239.                 if (isset($attributeOptionGroups[$attributeFilter['id']])) {
  2240.                     foreach ($attributeOptionGroups[$attributeFilter['id']] as $group) {
  2241.                         $countItems = 0;
  2242.                         $affectedAttributeOptions = [];
  2243.                         foreach ($attributeFilter['options'] as $key => $option) {
  2244.                             if (in_array($option['id'], $group)) {
  2245.                                 $affectedAttributeOptions[(int)$option['id']] = $key;
  2246.                                 $countItems += (int)$option['count'];
  2247.                             }
  2248.                         }
  2249.                         foreach ($group as $optionId) {
  2250.                             if (isset($affectedAttributeOptions[$optionId])) {
  2251.                                 unset($affectedAttributeOptions[$optionId]);
  2252.                                 $filterOptions[$key]['count'] = $countItems;
  2253.                                 break;
  2254.                             }
  2255.                         }
  2256.                         foreach ($affectedAttributeOptions as $key) {
  2257.                             unset($filterOptions[$key]);
  2258.                         }
  2259.                     }
  2260.                 }
  2261.             }
  2262.             // 5 = SSD, 12 = Screen, 384 = Ram, 101 = Quality, 139 = Touchscreen, 19 = Webcam, 45 = Connectors, 118 = DisplayTechnology,
  2263.             // 11 = OpticalDriver, 25 = VGA, 17 = Numpad, 21 = OperationSystem, 63 = MaxResolution
  2264.             $skipShortingIds = $notebookShow
  2265.                 ? [5, 3, 9, 4, 12, 384, 101, 139, 19, 45, 118, 11, 25, 17, 21, 63]
  2266.                 : [5, 3, 9, 4, 101];
  2267.             if (!in_array($attributeFilter['id'], $skipShortingIds)) {
  2268.                 $filterOptions = $this->orderArrayBy($filterOptions, 'title');
  2269.             }
  2270.             switch ($attributeFilter['id']) {
  2271.                 case Attribute::ATTRIBUTE_ID_CPU_GENERATION:
  2272.                     $filterOptions = $this->orderArrayBy($filterOptions, 'sortOrder');
  2273.                     break;
  2274.                 case Attribute::ATTRIBUTE_ID_RAM_SIZE:
  2275.                     if (!$notebookShow) {
  2276.                         $filterOptions = $this->orderArrayBy($filterOptions, 'title', 'GB');
  2277.                     }
  2278.                     break;
  2279.             }
  2280.             $checkedList = $checkedFilterList['filterAttribute'] && array_key_exists($attributeFilter['id'], $checkedFilterList['filterAttribute'])
  2281.                 ? $checkedFilterList['filterAttribute'][$attributeFilter['id']]
  2282.                 : '';
  2283.             if (in_array($attributeFilter['id'], [
  2284.                 Attribute::ATTRIBUTE_ID_RAM_NB_TYPE,
  2285.                 ])) {
  2286.                 $collapsed = true;
  2287.             } elseif (in_array($attributeFilter['id'], [
  2288.                 Attribute::ATTRIBUTE_ID_YEAR_OF_PRODUCTION,
  2289.                 Attribute::ATTRIBUTE_ID_SCREEN_SIZE,
  2290.                 Attribute::ATTRIBUTE_ID_CPU_MARK,
  2291.                 Attribute::ATTRIBUTE_ID_ACCESSORY_OS,
  2292.                 'newBattery',
  2293.             ])) {
  2294.                 $collapsed = false;
  2295.             } else {
  2296.                 $collapsed = ($count > 5 && $checkedList == '' && $attributeFilter['id'] != Attribute::ATTRIBUTE_ID_SSD);
  2297.             }
  2298.             $filterList[] = [
  2299.                 'sortOrder' => isset($filterSorting[$attributeFilter['title']]) ? $filterSorting[$attributeFilter['title']] : $sortOrderCount++,
  2300.                 'filterId' => $attributeFilter['id'],
  2301.                 'collapseTitle' => $attributeFilter['title'],
  2302.                 'templateId' => 'attributeFilter-' . $attributeFilter['id'],
  2303.                 'inputName' => $attributeFilter['id'] == Attribute::ATTRIBUTE_ID_CPU_MARK ? 'CPU mark' : 'attribute',
  2304.                 'checkedList' => $checkedList,
  2305.                 'isCollapsed' => $collapsed,
  2306.                 'filters' => $filterOptions,
  2307.                 'extraInfo' => $extraInfo,
  2308.                 'customCountToShow' => $customCountToShow,
  2309.             ];
  2310.             if ($attributeFilter['id'] == Attribute::ATTRIBUTE_ID_HDD_25) {
  2311.                 $filterList[key(array_slice($filterList, -1, 1, true))]['collapseTitle'] = "HDD";
  2312.             }
  2313.         }
  2314.         if ($notebookShow) {
  2315.             usort($filterList, function ($a, $b) {
  2316.                 return $a['sortOrder'] <=> $b['sortOrder'];
  2317.             });
  2318.         }
  2319.         return [
  2320.             'main' => $filterList,
  2321.         ];
  2322.     }
  2323.     /**
  2324.      * @param array $items
  2325.      * @param string $attr
  2326.      * @param string $textToRemove
  2327.      * @param string $order
  2328.      * @return array
  2329.      */
  2330.     private function orderArrayBy(array $items, string $attr, string $textToRemove = '', string $order = 'asc'): array
  2331.     {
  2332.         $sortedItems = [];
  2333.         foreach ($items as $item) {
  2334.             $key = is_object($item) ? str_replace($textToRemove, '', $item->{$attr}) : str_replace($textToRemove, '', $item[$attr]);
  2335.             $sortedItems[$key] = $item;
  2336.         }
  2337.         if ($order === 'desc') {
  2338.             krsort($sortedItems);
  2339.         } else {
  2340.             ksort($sortedItems);
  2341.         }
  2342.         return array_values($sortedItems);
  2343.     }
  2344.     /**
  2345.      * @param array &$filterOptions
  2346.      */
  2347.     private function updateFilterOptionsWithThumbnails(array &$filterOptions): void
  2348.     {
  2349.         $filterOptionIds = array_column($filterOptions, 'id');
  2350.         $attributeOptions = $this->entityManager->getRepository(AttributeOption::class)->findBy([
  2351.             'id' => $filterOptionIds,
  2352.         ]);
  2353.         /**
  2354.          * @var AttributeOption $attributeOption
  2355.          */
  2356.         foreach ($attributeOptions as $key => $attributeOption) {
  2357.             if ($attributeOption->getProduct() != null) {
  2358.                 $filterOptions[$key]['thumb'] = $this->getImage($attributeOption->getProduct()->getAlbum()->getNthPhoto(), 'product/' . $attributeOption->getProduct()->getCode())->resize(Image::PRODUCT_LIST_THUMB_WIDTH, Image::PRODUCT_LIST_THUMB_HEIGHT);
  2359.             }
  2360.         }
  2361.     }
  2362.     /**
  2363.      * @param string $language
  2364.      * @return array
  2365.      */
  2366.     private function getCategoryFilters(string $language): array
  2367.     {
  2368.         // Translator
  2369.         $translator = $this->translator;
  2370.         // Filter Categories
  2371.         $categoryFilters = [];
  2372.         switch ($this->multiStore->getDefaultLocale()) {
  2373.             case 'sk':
  2374.                 $categoryFilters = [
  2375.                     CategoryPage::SK_CATEGORYPAGE_ID_PC => [
  2376.                         'id' => CategoryPage::SK_CATEGORYPAGE_ID_PC,
  2377.                         'name' => $translator->trans('Computers'),
  2378.                     ],
  2379.                     CategoryPage::SK_CATEGORYPAGE_ID_NOTEBOOK => [
  2380.                         'id' => CategoryPage::SK_CATEGORYPAGE_ID_NOTEBOOK,
  2381.                         'name' => $translator->trans('Notebooks'),
  2382.                     ],
  2383.                     CategoryPage::SK_CATEGORYPAGE_ID_MONITOR => [
  2384.                         'id' => CategoryPage::SK_CATEGORYPAGE_ID_MONITOR,
  2385.                         'name' => $translator->trans('Monitors'),
  2386.                     ],
  2387.                     CategoryPage::SK_CATEGORYPAGE_ID_MOBIL => [
  2388.                         'id' => CategoryPage::SK_CATEGORYPAGE_ID_MOBIL,
  2389.                         'name' => $translator->trans('Smartphones'),
  2390.                     ],
  2391.                     CategoryPage::SK_CATEGORYPAGE_ID_TABLET => [
  2392.                         'id' => CategoryPage::SK_CATEGORYPAGE_ID_TABLET,
  2393.                         'name' => 'Tablet',
  2394.                     ],
  2395.                     CategoryPage::SK_CATEGORYPAGE_ID_SMARTWATCH => [
  2396.                         'id' => CategoryPage::SK_CATEGORYPAGE_ID_SMARTWATCH,
  2397.                         'name' => 'Smartwatch',
  2398.                     ],
  2399.                     CategoryPage::SK_CATEGORYPAGE_ID_ELECTRIC_SCOOTER => [
  2400.                         'id' => CategoryPage::SK_CATEGORYPAGE_ID_ELECTRIC_SCOOTER,
  2401.                         'name' => $translator->trans('Electric scooters'),
  2402.                     ],
  2403.                 ];
  2404.                 break;
  2405.             case 'hu':
  2406.                 $categoryFilters = [
  2407.                     CategoryPage::HU_CATEGORYPAGE_ID_PC => [
  2408.                         'id' => CategoryPage::HU_CATEGORYPAGE_ID_PC,
  2409.                         'name' => $translator->trans('Computers'),
  2410.                     ],
  2411.                     CategoryPage::HU_CATEGORYPAGE_ID_NOTEBOOK => [
  2412.                         'id' => CategoryPage::HU_CATEGORYPAGE_ID_NOTEBOOK,
  2413.                         'name' => $translator->trans('Notebooks'),
  2414.                     ],
  2415.                     CategoryPage::HU_CATEGORYPAGE_ID_MONITOR => [
  2416.                         'id' => CategoryPage::HU_CATEGORYPAGE_ID_MONITOR,
  2417.                         'name' => $translator->trans('Monitors'),
  2418.                     ],
  2419.                     CategoryPage::HU_CATEGORYPAGE_ID_MOBIL => [
  2420.                         'id' => CategoryPage::HU_CATEGORYPAGE_ID_MOBIL,
  2421.                         'name' => $translator->trans('Smartphones'),
  2422.                     ],
  2423.                     CategoryPage::HU_CATEGORYPAGE_ID_TABLET => [
  2424.                         'id' => CategoryPage::HU_CATEGORYPAGE_ID_TABLET,
  2425.                         'name' => 'Tablet',
  2426.                     ],
  2427.                     CategoryPage::HU_CATEGORYPAGE_ID_SMARTWATCH => [
  2428.                         'id' => CategoryPage::HU_CATEGORYPAGE_ID_SMARTWATCH,
  2429.                         'name' => 'Smartwatch',
  2430.                     ],
  2431.                     CategoryPage::HU_CATEGORYPAGE_ID_ELECTRIC_SCOOTER => [
  2432.                         'id' => CategoryPage::HU_CATEGORYPAGE_ID_ELECTRIC_SCOOTER,
  2433.                         'name' => $translator->trans('Electric scooters'),
  2434.                     ],
  2435.                 ];
  2436.                 break;
  2437.             case 'cz':
  2438.                 $categoryFilters = [
  2439.                     CategoryPage::CZ_CATEGORYPAGE_ID_PC => [
  2440.                         'id' => CategoryPage::CZ_CATEGORYPAGE_ID_PC,
  2441.                         'name' => $translator->trans('Computers'),
  2442.                     ],
  2443.                     CategoryPage::CZ_CATEGORYPAGE_ID_NOTEBOOK => [
  2444.                         'id' => CategoryPage::CZ_CATEGORYPAGE_ID_NOTEBOOK,
  2445.                         'name' => $translator->trans('Notebooks'),
  2446.                     ],
  2447.                     CategoryPage::CZ_CATEGORYPAGE_ID_MONITOR => [
  2448.                         'id' => CategoryPage::CZ_CATEGORYPAGE_ID_MONITOR,
  2449.                         'name' => $translator->trans('Monitors'),
  2450.                     ],
  2451.                     CategoryPage::CZ_CATEGORYPAGE_ID_MOBIL => [
  2452.                         'id' => CategoryPage::CZ_CATEGORYPAGE_ID_MOBIL,
  2453.                         'name' => $translator->trans('Smartphones'),
  2454.                     ],
  2455.                     CategoryPage::CZ_CATEGORYPAGE_ID_TABLET => [
  2456.                         'id' => CategoryPage::CZ_CATEGORYPAGE_ID_TABLET,
  2457.                         'name' => 'Tablet',
  2458.                     ],
  2459.                     CategoryPage::CZ_CATEGORYPAGE_ID_SMARTWATCH => [
  2460.                         'id' => CategoryPage::CZ_CATEGORYPAGE_ID_SMARTWATCH,
  2461.                         'name' => 'Smartwatch',
  2462.                     ],
  2463.                     CategoryPage::CZ_CATEGORYPAGE_ID_ELECTRIC_SCOOTER => [
  2464.                         'id' => CategoryPage::CZ_CATEGORYPAGE_ID_ELECTRIC_SCOOTER,
  2465.                         'name' => $translator->trans('Electric scooters'),
  2466.                     ],
  2467.                 ];
  2468.                 break;
  2469.             case 'en':
  2470.                 $categoryFilters = [
  2471.                     CategoryPage::EU_CATEGORYPAGE_ID_PC => [
  2472.                         'id' => CategoryPage::EU_CATEGORYPAGE_ID_PC,
  2473.                         'name' => $translator->trans('Computers'),
  2474.                     ],
  2475.                     CategoryPage::EU_CATEGORYPAGE_ID_NOTEBOOK => [
  2476.                         'id' => CategoryPage::EU_CATEGORYPAGE_ID_NOTEBOOK,
  2477.                         'name' => $translator->trans('Notebooks'),
  2478.                     ],
  2479.                     CategoryPage::EU_CATEGORYPAGE_ID_MONITOR => [
  2480.                         'id' => CategoryPage::EU_CATEGORYPAGE_ID_MONITOR,
  2481.                         'name' => $translator->trans('Monitors'),
  2482.                     ],
  2483.                     CategoryPage::EU_CATEGORYPAGE_ID_MOBIL => [
  2484.                         'id' => CategoryPage::EU_CATEGORYPAGE_ID_MOBIL,
  2485.                         'name' => $translator->trans('Smartphones'),
  2486.                     ],
  2487.                     CategoryPage::EU_CATEGORYPAGE_ID_TABLET => [
  2488.                         'id' => CategoryPage::EU_CATEGORYPAGE_ID_TABLET,
  2489.                         'name' => 'Tablet',
  2490.                     ],
  2491.                     CategoryPage::EU_CATEGORYPAGE_ID_SMARTWATCH => [
  2492.                         'id' => CategoryPage::EU_CATEGORYPAGE_ID_SMARTWATCH,
  2493.                         'name' => 'Smartwatch',
  2494.                     ],
  2495.                     CategoryPage::EU_CATEGORYPAGE_ID_ELECTRIC_SCOOTER => [
  2496.                         'id' => CategoryPage::EU_CATEGORYPAGE_ID_ELECTRIC_SCOOTER,
  2497.                         'name' => $translator->trans('Electric scooters'),
  2498.                     ],
  2499.                 ];
  2500.                 break;
  2501.         }
  2502.         return $categoryFilters;
  2503.     }
  2504.     /**
  2505.      * @param array $filter
  2506.      * @return array
  2507.      */
  2508.     public function buildPriceFilterForWizard(array $filter): array
  2509.     {
  2510.         $results = $this->getListForWizard($filter, [
  2511.             'filterOptions' => true,
  2512.         ])->getResult();
  2513.         $priceMin = $priceMax = false;
  2514.         foreach ($results as $result) {
  2515.             $productMeta = $result->getDomainMeta($this->priceDomain);
  2516.             if ($productMeta) {
  2517.                 $price = (float)$productMeta->getCurrentVatPrice();
  2518.                 if ($priceMin === false || $priceMax === false) {
  2519.                     $priceMin = $priceMax = $price;
  2520.                 }
  2521.                 if ($priceMin > $price) {
  2522.                     $priceMin = $price;
  2523.                 }
  2524.                 if ($priceMax < $price) {
  2525.                     $priceMax = $price;
  2526.                 }
  2527.             }
  2528.         }
  2529.         return [
  2530.             'priceMin' => floor($priceMin),
  2531.             'priceMax' => ceil($priceMax),
  2532.         ];
  2533.     }
  2534.     /**
  2535.      * @param array $filter
  2536.      * @param array $options
  2537.      * @return array
  2538.      */
  2539.     private function getFilterListingResults(array $filter, array $options = [])
  2540.     {
  2541.         if (isset($filter['filterCategoryId']) && in_array($filter['filterCategoryId'], CategoryPage::SALE_CATEGORY_PAGES)) {
  2542.             $filterListing = [
  2543.                 'filterCategoryId'  => $filter['filterCategoryId'],
  2544.                 'filterProductType' => $filter['filterProductType'],
  2545.             ];
  2546.         }
  2547.         else if (isset($filter['filterCategoryId'])) {
  2548.             $filterListing = [
  2549.                 'filterCategoryId' => $filter['filterCategoryId'],
  2550.             ];
  2551.         }
  2552.         else if (isset($options['search'])) {
  2553.             $filterListing = $filter;
  2554.             // Remove Quality Filter
  2555.             if (isset($filterListing['filterAttribute'][101])) {
  2556.                 unset($filterListing['filterAttribute'][101]);
  2557.             }
  2558.             // Remove Price Filter
  2559.             if (isset($filterListing['filterPriceMin'])) {
  2560.                 unset($filterListing['filterPriceMin']);
  2561.             }
  2562.             if (isset($filterListing['filterPriceMax'])) {
  2563.                 unset($filterListing['filterPriceMax']);
  2564.             }
  2565.         }
  2566.         else {
  2567.             $filterListing = [];
  2568.         }
  2569.         $query = $this->getProductListingQuery($filterListing, [
  2570.             'filterOptions' => true,
  2571.         ]);
  2572.         return $query->getResult();
  2573.     }
  2574.     /**
  2575.      * @param int $categoryId
  2576.      * @param int $limit
  2577.      */
  2578.     public function getPopularItemsByCategory($categoryId, $limit = 3)
  2579.     {
  2580.         $categoryPage = $this->entityManager->getRepository(CategoryPage::class)->findOneById($categoryId);
  2581.         $manuallyAddedProducts = $categoryPage->getPopularProducts();
  2582.         $results = [];
  2583.         if ($manuallyAddedProducts->count() < 3) {
  2584.             $query = $this->getProductListingQuery([
  2585.                 'filterCategoryId' => $categoryId,
  2586.                 'filterDiscounted' => 1,
  2587.                 'filterOrderable' => 1,
  2588.                 'start' => 0,
  2589.                 'order' => 'discountedDate',
  2590.                 'sort' => 'DESC',
  2591.                 'limit' => $limit - $manuallyAddedProducts->count(),
  2592.             ]);
  2593.             foreach ($manuallyAddedProducts as $key => $product) {
  2594.                 array_push($results, $product);
  2595.             }
  2596.             $popularProductsByDiscountedDate = new Paginator($query, $fetchJoinCollection = true);
  2597.             foreach ($popularProductsByDiscountedDate as $key => $product) {
  2598.                 array_push($results, $product);
  2599.             }
  2600.         } else {
  2601.             $results = $manuallyAddedProducts;
  2602.         }
  2603.         if (count($results) < 3) {
  2604.             $query = $this->getProductListingQuery([
  2605.                 'filterCategoryId' => $categoryId,
  2606.                 'filterDiscounted' => 0,
  2607.                 'filterOrderable' => 1,
  2608.                 'start' => 0,
  2609.                 'order' => 'lastSale',
  2610.                 'sort' => 'ASC',
  2611.                 'limit' => $limit - count($results),
  2612.             ]);
  2613.             $popularProductsByLastSale = new Paginator($query, $fetchJoinCollection = true);
  2614.             foreach ($popularProductsByLastSale as $key => $product) {
  2615.                 array_push($results, $product);
  2616.             }
  2617.         }
  2618.         $products = [];
  2619.         $count = 0;
  2620.         foreach ($results as $result) {
  2621.             $count++;
  2622.             if ($count > 3) {
  2623.                 break;
  2624.             }
  2625.             $products[$result->getId()] = $this->getStructuredDataForProduct($result, 'popular');
  2626.         }
  2627.         return $products;
  2628.     }
  2629.     /**
  2630.      * @param int $categoryId
  2631.      * @param int $vendorId
  2632.      * @param int $limit
  2633.      */
  2634.     public function getSimilarItemsByCategoryVendor($categoryId, $vendorId, $limit = 4)
  2635.     {
  2636.         $query = $this->getProductListingQuery([
  2637.             'filterCategoryId' => $categoryId,
  2638.             'filterVendor' => $vendorId,
  2639.             'filterOrderable' => 1,
  2640.             'start' => 0,
  2641.             'limit' => $limit,
  2642.         ]);
  2643.         $results = new Paginator($query, $fetchJoinCollection = true);
  2644.         $products = [];
  2645.         foreach ($results as $result) {
  2646.             $products[$result->getId()] = $this->getStructuredDataForProduct($result, 'list');
  2647.         }
  2648.         return $products;
  2649.     }
  2650.     /**
  2651.      * @param int $productId
  2652.      * @param int $limit
  2653.      * @return array
  2654.      * @throws \Doctrine\ORM\NoResultException
  2655.      * @throws \Doctrine\ORM\NonUniqueResultException
  2656.      */
  2657.     public function getSimilarProducts($productId, $limit = 3)
  2658.     {
  2659.         $product = $this->entityManager->getRepository(Product::class)->findOneById($productId);
  2660.         $domain = $this->priceDomain;
  2661.         $productsWithSameTypeVendor = $this->entityManager
  2662.             ->createQuery("SELECT COUNT(p.id)
  2663.               FROM AtechNetCoreBundle:Product p
  2664.               LEFT JOIN AtechNetCoreBundle:ProductMeta pm WITH pm.product = p AND pm.domain = :domain
  2665.               WHERE p.type = :type AND p.vendor = :vendor AND p.id != :currentProductId AND p.available > 0 AND pm.active = 1 AND pm.orderable = 1 AND p.qualityStatus != :qualityStatus")
  2666.             ->setParameters([
  2667.                 'domain' => $domain,
  2668.                 'type' => $product->getType(),
  2669.                 'vendor' => $product->getVendor(),
  2670.                 'currentProductId' => $product->getId(),
  2671.                 'qualityStatus' => Product::QualityStatus_Bazaar,
  2672.             ])
  2673.             ->setMaxResults($limit)
  2674.             ->getSingleScalarResult();
  2675.         $where = "p.type = :type";
  2676.         $parameters = [
  2677.             'domain' => $domain,
  2678.             'type' => $product->getType(),
  2679.             'currentProductId' => $product->getId(),
  2680.             'qualityStatus' => Product::QualityStatus_Bazaar,
  2681.         ];
  2682.         if ($productsWithSameTypeVendor >= $limit) {
  2683.             $where .= " AND p.vendor = :vendor";
  2684.             $parameters['vendor'] = $product->getVendor();
  2685.         }
  2686.         $results = $this->entityManager
  2687.             ->createQuery("SELECT p, (CASE WHEN v.name = '" . $product->getVendor()->getName() . "' THEN 1 ELSE 0 END) AS HIDDEN vndr
  2688.               FROM AtechNetCoreBundle:Product p
  2689.               LEFT JOIN AtechNetCoreBundle:ProductMeta pm WITH pm.product = p AND pm.domain = :domain
  2690.               LEFT JOIN p.vendor v
  2691.               WHERE " . $where . " AND p.id != :currentProductId AND p.available > 0 AND pm.active = 1 AND pm.orderable = 1 AND p.qualityStatus != :qualityStatus
  2692.               ORDER BY vndr DESC, pm.currentVatPrice ASC")
  2693.             ->setParameters($parameters)
  2694.             ->setMaxResults($limit)
  2695.             ->getResult();
  2696.         $products = [];
  2697.         foreach ($results as $result) {
  2698.             $products[$result->getId()] = $this->getStructuredDataForProduct($result, 'list');
  2699.         }
  2700.         return $products;
  2701.     }
  2702.     private function productIdsFromCategoryPagesProducts(array $categoryIds)
  2703.     {
  2704.         $ret = $this->entityManager->createQueryBuilder()
  2705.             ->from('category_pages_products', 'cpps')
  2706.             ->select('cpps.product_id AS pId')
  2707.             ->where('cpps.category_page_id IN :categoryId')
  2708.             ->setParameter(':categoryId', $categoryIds);
  2709.         return $ret->getDQL();
  2710.     }
  2711.     /**
  2712.      * @param $filter
  2713.      * @param array $options
  2714.      * @return Query
  2715.      */
  2716.     protected function getListForWizard($filter, $options = []): Query
  2717.     {
  2718.         $queryBuilder = $this->entityManager->createQueryBuilder();
  2719.         if (isset($options['count'])) {
  2720.             $queryBuilder
  2721.                 ->select(
  2722.                     $queryBuilder->expr()->countDistinct('p.id')
  2723.                 );
  2724.         } else {
  2725.             $queryBuilder->select('p');
  2726.         }
  2727.         $queryBuilder
  2728.             ->from(Product::class, 'p')
  2729.             ->leftJoin('p.metas', 'm')
  2730.             ->leftJoin('p.vendor', 'v')
  2731.             ->leftJoin('p.categoryPages', 'cp')
  2732.             ->leftJoin('cp.products', 'cpp', 'WITH', 'cpp.id IN(:categoryIds)')
  2733.             ->setParameter(':categoryIds', $this->productIdsFromCategoryPagesProducts($filter['categoryIds']));
  2734.         if (isset($filter['filterType']) && !is_null($filter['filterType'])) {
  2735.             $productTypes = [];
  2736.             $productTypesRaw = explode(',', $filter['filterType']);
  2737.             foreach ($productTypesRaw as $productType) {
  2738.                 $productTypes[] = (int)$productType;
  2739.             }
  2740.             $queryBuilder
  2741.                 ->leftJoin('p.type', 'ppt')
  2742.                 ->andWhere(
  2743.                     $queryBuilder->expr()->in('ppt.id', $productTypes)
  2744.                 );
  2745.         }
  2746.         if (isset($filter['filterBadge']) && !is_null($filter['filterBadge'])) {
  2747.             $badges = explode(',', $filter['filterBadge']);
  2748.             $orX = $queryBuilder->expr()->orX();
  2749.             foreach ($badges as $index => $badge) {
  2750.                 $orX->add(
  2751.                     $queryBuilder->expr()->like(
  2752.                         'p.badges',
  2753.                         $queryBuilder->expr()->literal('%"' . $badge . '"%')
  2754.                     )
  2755.                 );
  2756.             }
  2757.             $queryBuilder->andWhere($orX);
  2758.         }
  2759.         if (isset($filter['filterAttribute']) && !is_null($filter['filterAttribute'])) {
  2760.             // Create OR
  2761.             if (isset($filter['filterGroupOr']) && !is_null($filter['filterGroupOr'])) {
  2762.                 $orGroup = explode(',', $filter['filterGroupOr']);
  2763.                 $hasOrGroup = false;
  2764.                 $orX = $queryBuilder->expr()->orX();
  2765.                 foreach ($filter['filterAttribute'] as $attributeId => $attribute) {
  2766.                     if (in_array($attributeId, $orGroup)) {
  2767.                         $attributeOptions = [];
  2768.                         $optionsRaw = explode(',', $attribute);
  2769.                         foreach ($optionsRaw as $option) {
  2770.                             $attributeOptions[] = (int)$option;
  2771.                         }
  2772.                         $queryBuilder->leftJoin('p.productAttributes', 'pa' . $attributeId);
  2773.                         $orX->add(
  2774.                             $queryBuilder->expr()->in('pa' . $attributeId . '.option', $attributeOptions)
  2775.                         );
  2776.                         $hasOrGroup = true;
  2777.                         unset($filter['filterAttribute'][$attributeId]);
  2778.                     }
  2779.                 }
  2780.                 if ($hasOrGroup) {
  2781.                     $queryBuilder->andWhere($orX);
  2782.                 }
  2783.             }
  2784.             foreach ($filter['filterAttribute'] as $attributeId => $attribute) {
  2785.                 $attributeOptions = [];
  2786.                 $optionsRaw = explode(',', $attribute);
  2787.                 foreach ($optionsRaw as $option) {
  2788.                     $attributeOptions[] = (int)$option;
  2789.                 }
  2790.                 $queryBuilder
  2791.                     ->leftJoin('p.productAttributes', 'pa' . $attributeId)
  2792.                     ->andWhere(
  2793.                         $queryBuilder->expr()->in('pa' . $attributeId . '.option', $attributeOptions)
  2794.                     );
  2795.             }
  2796.         }
  2797.         if (!empty($filter['filterPriceMin'])) {
  2798.             $queryBuilder
  2799.                 ->andWhere('m.currentVatPrice >= :filterPriceMin')
  2800.                 ->setParameter(':filterPriceMin', $filter['filterPriceMin']);
  2801.         }
  2802.         if (!empty($filter['filterPriceMax'])) {
  2803.             $queryBuilder
  2804.                 ->andWhere('m.currentVatPrice <= :filterPriceMax')
  2805.                 ->setParameter(':filterPriceMax', $filter['filterPriceMax']);
  2806.         }
  2807.         $queryBuilder
  2808.             ->andWhere('m.domain = :domainId')
  2809.             ->setParameter(':domainId', $this->priceDomainId)
  2810.             ->andWhere('m.price > 0')
  2811.             ->andWhere('m.active = 1')
  2812.             ->andWhere('m.listed = 1')
  2813.             ->andWhere('p.archived = 0')
  2814.             ->andWhere('p.available > 0');
  2815.         if (!isset($options['count']) && !isset($options['filterOptions'])) {
  2816.             if (!empty($filter['sort']) && !empty($filter['order'])) {
  2817.                 if ($filter['sort'] == 'price' && $filter['order'] == 'asc') {
  2818.                     $queryBuilder->orderBy('m.currentVatPrice', 'ASC');
  2819.                 } elseif ($filter['sort'] == 'price' && $filter['order'] == 'desc') {
  2820.                     $queryBuilder->orderBy('m.currentVatPrice', 'DESC');
  2821.                 } elseif ($filter['sort'] == 'created' && $filter['order'] == 'asc') {
  2822.                     $queryBuilder->orderBy('p.created', 'ASC');
  2823.                 } elseif ($filter['sort'] == 'created' && $filter['order'] == 'desc') {
  2824.                     $queryBuilder->orderBy('p.created', 'DESC');
  2825.                 } elseif ($filter['sort'] == 'name' && $filter['order'] == 'asc') {
  2826.                     $queryBuilder->orderBy(
  2827.                         $queryBuilder->expr()->concat(
  2828.                             'v.name',
  2829.                             $queryBuilder->expr()->concat(
  2830.                                 $queryBuilder->expr()->literal(' '),
  2831.                                 'p.name'
  2832.                             )
  2833.                         ),
  2834.                         'ASC'
  2835.                     );
  2836.                 } elseif ($filter['sort'] == 'name' && $filter['order'] == 'desc') {
  2837.                     $queryBuilder->orderBy(
  2838.                         $queryBuilder->expr()->concat(
  2839.                             'v.name',
  2840.                             $queryBuilder->expr()->concat(
  2841.                                 $queryBuilder->expr()->literal(' '),
  2842.                                 'p.name'
  2843.                             )
  2844.                         ),
  2845.                         'DESC'
  2846.                     );
  2847.                 } else {
  2848.                     $queryBuilder->orderBy('p.available', 'DESC');
  2849.                 }
  2850.             }
  2851.             if (isset($filter['start']) && isset($filter['limit'])) {
  2852.                 if ($filter['start'] < 0) {
  2853.                     $filter['start'] = 0;
  2854.                 }
  2855.                 if ($filter['limit'] < 1) {
  2856.                     $filter['limit'] = 12;
  2857.                 }
  2858.                 $queryBuilder
  2859.                     ->setFirstResult($filter['start'])
  2860.                     ->setMaxResults($filter['limit']);
  2861.             }
  2862.         }
  2863.         return $queryBuilder->getQuery();
  2864.     }
  2865.     /**
  2866.      * @param array $filter
  2867.      * @return \Doctrine\ORM\Query
  2868.      */
  2869.     protected function getProductVariantsQuery(array $filter): Query
  2870.     {
  2871.         $queryBuilder = $this->entityManager->createQueryBuilder();
  2872.         $queryBuilder
  2873.             ->select('p')
  2874.             ->from(Product::class, 'p')
  2875.             ->leftJoin('p.metas', 'm')
  2876.             ->leftJoin('p.vendor', 'v');
  2877.         if (isset($filter['vendorId']) && !is_null($filter['vendorId'])) {
  2878.             $queryBuilder
  2879.                 ->andWhere('v.id = ' . $filter['vendorId']);
  2880.         }
  2881.         if (isset($filter['name']) && !is_null($filter['name'])) {
  2882.             $queryBuilder
  2883.                 ->andWhere(
  2884.                     $queryBuilder->expr()->like(
  2885.                         'p.name',
  2886.                         $queryBuilder->expr()->literal('%' . $filter['name'] . '%')
  2887.                     )
  2888.                 );
  2889.         }
  2890.         if (isset($filter['typeId']) && !is_null($filter['typeId'])) {
  2891.             $queryBuilder
  2892.                 ->leftJoin('p.type', 'ppt')
  2893.                 ->andWhere('ppt.id = ' . $filter['typeId']);
  2894.         }
  2895.         $queryBuilder
  2896.             ->andWhere('m.domain = :domainId')
  2897.             ->setParameter(':domainId', $this->priceDomainId)
  2898.             ->andWhere('m.currentVatPrice > 0')
  2899.             ->andWhere('p.archived = 0')
  2900.             ->andWhere('p.available > 0')
  2901.             ->andWhere('p.id != :productId')
  2902.             ->setParameter(':productId', $filter['productId'])
  2903.             ->andWhere('p.qualityStatus != :qualityStatus')
  2904.             ->setParameter(':qualityStatus', Product::QualityStatus_Bazaar)
  2905.             ->setMaxResults(3)
  2906.             ->andWhere('m.orderable = 1')
  2907.             ->andWhere('m.active = 1')
  2908.             ->orderBy('m.currentVatPrice');
  2909.         return $queryBuilder->getQuery();
  2910.     }
  2911.     /**
  2912.      * @param array $filter
  2913.      * @param array $options
  2914.      * @return Query
  2915.      * @throws NotSupported
  2916.      */
  2917.     protected function getProductListingQuery(array $filter, array $options = [])
  2918.     {
  2919.         $queryBuilder = $this->entityManager->createQueryBuilder();
  2920.         if (isset($options['count'])) {
  2921.             $queryBuilder
  2922.                 ->select(
  2923.                     $queryBuilder->expr()->countDistinct('p.id')
  2924.                 );
  2925.         } else {
  2926.             $queryBuilder->select('p');
  2927.         }
  2928.         $queryBuilder
  2929.             ->from(Product::class, 'p')
  2930.             ->leftJoin('p.metas', 'm')
  2931.             ->leftJoin('p.vendor', 'v')
  2932.             ->leftJoin('p.type', 'ppt');
  2933.         /**
  2934.          * @var CategoryPage $categoryInfo
  2935.          */
  2936.         if (isset($filter['filterCategoryId'])) {
  2937.             $categoryInfo = $this->entityManager->getRepository(CategoryPage::class)->findOneById($filter['filterCategoryId']);
  2938.             if ($categoryInfo) {
  2939.                 $countVendors = $categoryInfo->getVendors()->count();
  2940.                 $countProductTypes = $categoryInfo->getProductTypes()->count();
  2941.                 if ($countVendors > 0) {
  2942.                     $queryBuilder
  2943.                         ->leftJoin('v.categoryPages', 'vc', 'WITH', 'vc.id = :categoryId');
  2944.                 }
  2945.                 if ($countProductTypes > 0) {
  2946.                     $queryBuilder
  2947.                         ->leftJoin('p.type', 't')
  2948.                         ->leftJoin('t.categoryPages', 'tc', 'WITH', 'tc.id = :categoryId');
  2949.                 }
  2950.                 $categoryInfoBadges = json_decode($categoryInfo->getBadges(), true);
  2951.                 $queryBuilder->leftJoin('p.categoryPages', 'c', 'WITH', 'c.id = :categoryId');
  2952.                 if ($countVendors > 0 || $countProductTypes > 0 || (is_array($categoryInfoBadges) && count($categoryInfoBadges) > 0)) {
  2953.                     if ($countProductTypes > 0) {
  2954.                         $queryBuilder
  2955.                             ->andWhere(
  2956.                                 $queryBuilder->expr()->orX(
  2957.                                     $queryBuilder->expr()->eq('c.id', ':categoryId'),
  2958.                                     $queryBuilder->expr()->andX(
  2959.                                         $queryBuilder->expr()->isNotNull('tc.id')
  2960.                                     )
  2961.                                 )
  2962.                             );
  2963.                     }
  2964.                     if ($countVendors > 0) {
  2965.                         $queryBuilder
  2966.                             ->andWhere(
  2967.                                 $queryBuilder->expr()->orX(
  2968.                                     $queryBuilder->expr()->eq('c.id', ':categoryId'),
  2969.                                     $queryBuilder->expr()->andX(
  2970.                                         $queryBuilder->expr()->isNotNull('vc.id')
  2971.                                     )
  2972.                                 )
  2973.                             );
  2974.                     }
  2975.                     if (is_array($categoryInfoBadges) && count($categoryInfoBadges) > 0) {
  2976.                         $orX = $queryBuilder->expr()->orX();
  2977.                         foreach ($categoryInfoBadges as $badge) {
  2978.                             $orX->add(
  2979.                                 $queryBuilder->expr()->like(
  2980.                                     'p.badges',
  2981.                                     $queryBuilder->expr()->literal('%"' . $badge . '"%')
  2982.                                 )
  2983.                             );
  2984.                         }
  2985.                         $queryBuilder->andWhere($orX);
  2986.                     }
  2987.                 } else {
  2988.                     $queryBuilder->andWhere(
  2989.                         $queryBuilder->expr()->isNotNull('c.id')
  2990.                     );
  2991.                 }
  2992.                 $queryBuilder->setParameter('categoryId', $filter['filterCategoryId']);
  2993.             }
  2994.         } elseif (isset($filter['filterCategories']) && $filter['filterCategories'] != null) {
  2995.             $categoryInfos = $this->entityManager->getRepository(CategoryPage::class)->findBy([
  2996.                 'id' => explode(',', $filter['filterCategories']),
  2997.             ]);
  2998.             $countVendors = $countProductTypes = 0;
  2999.             $categoryInfoBadges = [];
  3000.             foreach ($categoryInfos as $categoryPageItem) {
  3001.                 $countVendors += $categoryPageItem->getVendors()->count();
  3002.                 $countProductTypes += $categoryPageItem->getProductTypes()->count();
  3003.                 $categoryInfoBadges = array_merge($categoryInfoBadges, $categoryPageItem->getBadges() != "" ? json_decode($categoryPageItem->getBadges(), true) : []);
  3004.             }
  3005.             if ($countVendors > 0) {
  3006.                 $queryBuilder
  3007.                     ->leftJoin('v.categoryPages', 'vc', 'WITH', 'vc.id IN(:categoryIds)');
  3008.             }
  3009.             if ($countProductTypes > 0) {
  3010.                 $queryBuilder
  3011.                     ->leftJoin('p.type', 't')
  3012.                     ->leftJoin('t.categoryPages', 'tc', 'WITH', 'tc.id IN(:categoryIds)');
  3013.             }
  3014.             $queryBuilder->leftJoin('p.categoryPages', 'c', 'WITH', 'c.id IN(:categoryIds)');
  3015.             if ($countVendors > 0 || $countProductTypes > 0 || (is_array($categoryInfoBadges) && count($categoryInfoBadges) > 0)) {
  3016.                 if ($countProductTypes > 0) {
  3017.                     $queryBuilder
  3018.                         ->andWhere(
  3019.                             $queryBuilder->expr()->orX(
  3020.                                 $queryBuilder->expr()->in('c.id', ':categoryIds'),
  3021.                                 $queryBuilder->expr()->andX(
  3022.                                     $queryBuilder->expr()->isNotNull('tc.id')
  3023.                                 )
  3024.                             )
  3025.                         );
  3026.                 }
  3027.                 if ($countVendors > 0) {
  3028.                     $queryBuilder
  3029.                         ->andWhere(
  3030.                             $queryBuilder->expr()->orX(
  3031.                                 $queryBuilder->expr()->in('c.id', ':categoryIds'),
  3032.                                 $queryBuilder->expr()->andX(
  3033.                                     $queryBuilder->expr()->isNotNull('vc.id')
  3034.                                 )
  3035.                             )
  3036.                         );
  3037.                 }
  3038.                 if (is_array($categoryInfoBadges) && count($categoryInfoBadges) > 0) {
  3039.                     $orX = $queryBuilder->expr()->orX();
  3040.                     foreach ($categoryInfoBadges as $badge) {
  3041.                         $orX->add(
  3042.                             $queryBuilder->expr()->like(
  3043.                                 'p.badges',
  3044.                                 $queryBuilder->expr()->literal('%"' . $badge . '"%')
  3045.                             )
  3046.                         );
  3047.                     }
  3048.                     $queryBuilder->andWhere($orX);
  3049.                 }
  3050.             } else {
  3051.                 $queryBuilder->andWhere(
  3052.                     $queryBuilder->expr()->isNotNull('c.id')
  3053.                 );
  3054.             }
  3055.             $queryBuilder->setParameter('categoryIds', explode(',', $filter['filterCategories']));
  3056.         }
  3057.         if (isset($filter['filterProductType']) && !is_null($filter['filterProductType']) && $filter['filterProductType'] != '') {
  3058.             $productTypes = [];
  3059.             $productTypesRaw = explode(',', $filter['filterProductType']);
  3060.             foreach ($productTypesRaw as $productType) {
  3061.                 $productTypes[] = (int)$productType;
  3062.             }
  3063.             $queryBuilder
  3064.                 ->andWhere(
  3065.                     $queryBuilder->expr()->in('p.type', $productTypes)
  3066.                 );
  3067.         }
  3068.         if (isset($filter['filterVendor']) && !is_null($filter['filterVendor']) && $filter['filterVendor'] != '') {
  3069.             $vendors = [];
  3070.             $vendorsRaw = explode(',', $filter['filterVendor']);
  3071.             foreach ($vendorsRaw as $vendor) {
  3072.                 $vendors[] = (int)$vendor;
  3073.             }
  3074.             $queryBuilder
  3075.                 ->andWhere(
  3076.                     $queryBuilder->expr()->in('v.id', $vendors)
  3077.                 );
  3078.         }
  3079.         if (isset($filter['filterType']) && !is_null($filter['filterType'])) {
  3080.             $productTypes = [];
  3081.             $productTypesRaw = explode(',', $filter['filterType']);
  3082.             foreach ($productTypesRaw as $productType) {
  3083.                 $productTypes[] = (int)$productType;
  3084.             }
  3085.             $queryBuilder
  3086.                 ->andWhere(
  3087.                     $queryBuilder->expr()->in('ppt.id', $productTypes)
  3088.                 );
  3089.         }
  3090.         if (isset($filter['filterBadge']) && !is_null($filter['filterBadge'])) {
  3091.             $badges = explode(',', $filter['filterBadge']);
  3092.             $orX = $queryBuilder->expr()->orX();
  3093.             foreach ($badges as $index => $badge) {
  3094.                 $orX->add(
  3095.                     $queryBuilder->expr()->like(
  3096.                         'p.badges',
  3097.                         $queryBuilder->expr()->literal('%"' . $badge . '"%')
  3098.                     )
  3099.                 );
  3100.             }
  3101.             $queryBuilder->andWhere($orX);
  3102.         }
  3103.         if (isset($filter['filterAttribute']) && $filter['filterAttribute'] != '') {
  3104.             // Attribute Option Groups
  3105.             $attributeOptionGroups = AttributeOption::getAttributeOptionGroups();
  3106.             // Create OR
  3107.             if (isset($filter['filterGroupOr']) && !is_null($filter['filterGroupOr'])) {
  3108.                 $orGroup = explode(',', $filter['filterGroupOr']);
  3109.                 $hasOrGroup = false;
  3110.                 $orX = $queryBuilder->expr()->orX();
  3111.                 foreach ($filter['filterAttribute'] as $attributeId => $attribute) {
  3112.                     if (in_array($attributeId, $orGroup)) {
  3113.                         $attributeOptions = [];
  3114.                         $optionsRaw = explode(',', $attribute);
  3115.                         foreach ($optionsRaw as $option) {
  3116.                             $attributeOptions[] = (int)$option;
  3117.                         }
  3118.                         if (isset($attributeOptionGroups[$attributeId])) {
  3119.                             foreach ($attributeOptionGroups[$attributeId] as $group) {
  3120.                                 $merge = false;
  3121.                                 foreach ($group as $optionId) {
  3122.                                     if (in_array((int)$optionId, $attributeOptions)) {
  3123.                                         $merge = true;
  3124.                                     }
  3125.                                 }
  3126.                                 if ($merge) {
  3127.                                     $attributeOptions = array_merge($attributeOptions, $group);
  3128.                                 }
  3129.                             }
  3130.                             array_unique($attributeOptions, SORT_NUMERIC);
  3131.                         }
  3132.                         $queryBuilder->leftJoin('p.productAttributes', 'pa' . $attributeId);
  3133.                         $orX->add(
  3134.                             $queryBuilder->expr()->in('pa' . $attributeId . '.option', $attributeOptions)
  3135.                         );
  3136.                         $hasOrGroup = true;
  3137.                         unset($filter['filterAttribute'][$attributeId]);
  3138.                     }
  3139.                 }
  3140.                 if ($hasOrGroup) {
  3141.                     $queryBuilder->andWhere($orX);
  3142.                 }
  3143.             }
  3144.             foreach ($filter['filterAttribute'] as $attributeId => $attribute) {
  3145.                 //TN panel is not set so we need to use id is not query... (â–º__â—„)
  3146.                 if ($attributeId == Attribute::ATTRIBUTE_ID_DISPLAY_TECHNOLOGY
  3147.                     && $attribute == $this->getTnDisplayAttributeOptionIds()) {
  3148.                     $queryBuilder
  3149.                         ->andWhere(
  3150.                             $queryBuilder->expr()->not(
  3151.                                 $queryBuilder->expr()->exists(
  3152.                                     $this->entityManager->createQueryBuilder()
  3153.                                         ->select('1')
  3154.                                         ->from('AtechNetCoreBundle:ProductAttribute', 'paDT')
  3155.                                         ->innerJoin('AtechNetCoreBundle:AttributeOption', 'ao', 'WITH', 'paDT.option = ao.id')
  3156.                                         ->where('paDT.product = p.id')
  3157.                                         ->andWhere('ao.attribute = :attributeId')
  3158.                                         ->getDQL()
  3159.                                 )
  3160.                             )
  3161.                         )
  3162.                         ->setParameter(':attributeId', Attribute::ATTRIBUTE_ID_DISPLAY_TECHNOLOGY);
  3163.                     
  3164.                     continue;
  3165.                 }
  3166.                 //SAME WITH THE NUMPAD THE NO NUMPAD ATTRIBUTE OPTION IS NOT USED (ï¿£ i ï¿£)
  3167.                 else if ($attributeId == Attribute::ATTRIBUTE_ID_NUMPAD
  3168.                     && $attribute == AttributeOption::ATTRIBUTE_OPTION_NO_NUMPAD) {
  3169.                     $queryBuilder
  3170.                         ->andWhere(
  3171.                             $queryBuilder->expr()->not(
  3172.                                 $queryBuilder->expr()->exists(
  3173.                                     $this->entityManager->createQueryBuilder()
  3174.                                         ->select('1')
  3175.                                         ->from('AtechNetCoreBundle:ProductAttribute', 'paNum')
  3176.                                         ->innerJoin('AtechNetCoreBundle:AttributeOption', 'ao', 'WITH', 'paNum.option = ao.id')
  3177.                                         ->where('paNum.product = p.id')
  3178.                                         ->andWhere('ao.attribute = :attributeId')
  3179.                                         ->getDQL()
  3180.                                 )
  3181.                             )
  3182.                         )
  3183.                         ->setParameter(':attributeId', Attribute::ATTRIBUTE_ID_NUMPAD);
  3184.                     continue;
  3185.                 }
  3186.                 else if ($attributeId == 'newBattery') {
  3187.                     $queryBuilder->innerJoin('p.tags', 'tag')
  3188.                         ->andWhere('tag.title = :newBattery')
  3189.                         ->setParameter(':newBattery', 'New battery - Notebook');
  3190.                     continue;
  3191.                 }
  3192.                 
  3193.                 $attributeOptions = [];
  3194.                 $optionsRaw = explode(',', $attribute);
  3195.                 foreach ($optionsRaw as $option) {
  3196.                     $attributeOptions[] = (int)$option;
  3197.                 }
  3198.                 if (isset($attributeOptionGroups[$attributeId])) {
  3199.                     foreach ($attributeOptionGroups[$attributeId] as $group) {
  3200.                         $merge = false;
  3201.                         foreach ($group as $optionId) {
  3202.                             if (in_array((int)$optionId, $attributeOptions)) {
  3203.                                 $merge = true;
  3204.                             }
  3205.                         }
  3206.                         if ($merge) {
  3207.                             $attributeOptions = array_merge($attributeOptions, $group);
  3208.                         }
  3209.                     }
  3210.                     array_unique($attributeOptions, SORT_NUMERIC);
  3211.                 }
  3212.                 $queryBuilder
  3213.                     ->leftJoin('p.productAttributes', 'pa' . $attributeId)
  3214.                     ->andWhere(
  3215.                         $queryBuilder->expr()->in('pa' . $attributeId . '.option', $attributeOptions)
  3216.                     );
  3217.             }
  3218.         }
  3219.         if (!empty($filter['filterPriceMin'])) {
  3220.             $queryBuilder
  3221.                 ->andWhere('m.currentVatPrice >= :filterPriceMin')
  3222.                 ->setParameter(':filterPriceMin', $filter['filterPriceMin']);
  3223.         }
  3224.         if (!empty($filter['filterPriceMax'])) {
  3225.             $queryBuilder
  3226.                 ->andWhere('m.currentVatPrice <= :filterPriceMax')
  3227.                 ->setParameter(':filterPriceMax', $filter['filterPriceMax']);
  3228.         }
  3229.         if (!empty($filter['filterCpuMarkMin'])) {
  3230.             $queryBuilder
  3231.                 ->andWhere('p.cpuMark != 0')
  3232.                 ->andWhere('p.cpuMark >= :filterCpuMarkMin')
  3233.                 ->setParameter(':filterCpuMarkMin', self::CPU_MARK_HELPER_MIN[$filter['filterCpuMarkMin']]);
  3234.         }
  3235.         if (!empty($filter['filterCpuMarkMax'])) {
  3236.             $queryBuilder
  3237.                 ->andWhere('p.cpuMark != 0')
  3238.                 ->andWhere('p.cpuMark <= :filterCpuMarkMax')
  3239.                 ->setParameter(':filterCpuMarkMax', self::CPU_MARK_HELPER_MAX[$filter['filterCpuMarkMax']]);
  3240.         }
  3241.         //page without discount
  3242.         if (isset($categoryInfo) && in_array($categoryInfo->getId(), [659, CategoryPage::UJVARI_FURDO_BANNER_SK, CategoryPage::UJVARI_FURDO_BANNER_HU, CategoryPage::UJVARI_FURDO_BANNER_CZ, 689, 690])) {
  3243.             $queryBuilder->andWhere('m.discounted = 0');
  3244.         }
  3245.         if (!empty($filter['filterDiscounted']) || (isset($categoryInfo) && $categoryInfo->isShowOnlyDiscounted())) {
  3246.             $filterValue = isset($categoryInfo) && $categoryInfo->isShowOnlyDiscounted() ? 1 : (int)$filter['filterDiscounted'];
  3247.             $queryBuilder
  3248.                 ->andWhere('m.discounted = :filterDiscounted')
  3249.                 ->setParameter(':filterDiscounted', $filterValue);
  3250.         }
  3251.         if (isset($categoryInfo) && $categoryInfo->isShowOnlyFlashSales()) {
  3252.             $queryBuilder
  3253.                 ->andWhere('m.discounted = 1 AND (m.saleStart IS NOT NULL OR m.saleEnd IS NOT NULL) AND (
  3254.                     (m.saleStart IS NOT NULL AND m.saleEnd IS NULL AND m.saleStart < :currentDate) OR (m.saleStart IS NULL AND m.saleEnd IS NOT NULL AND m.saleEnd > :currentDate) OR (m.saleStart IS NOT NULL AND m.saleEnd IS NOT NULL AND m.saleStart < :currentDate AND m.saleEnd > :currentDate)
  3255.                 )')
  3256.                 ->setParameter(':currentDate', (new \DateTime()));
  3257.         }
  3258.         if (isset($categoryInfo) && $categoryInfo->isShowOnlyBazaar()) {
  3259.             $queryBuilder
  3260.                 ->andWhere('p.qualityStatus = :bazaar')
  3261.                 ->setParameter(':bazaar', Product::QualityStatus_Bazaar);
  3262.         }
  3263.         else if (in_array($this->multiStore->getDomainId(), MultiStore::getWholesaleDomainIDs())) {
  3264.             //note WSen minden kategoriaban ott lehet a bazar, Katival megbeszelve
  3265.         }
  3266.         else if ((!isset($categoryInfo) || (isset($categoryInfo) && $categoryInfo->isIncludeBazaar() === false)) && empty($filter['filterSearch'])) {
  3267.             $queryBuilder
  3268.                 ->andWhere('p.qualityStatus != :bazaar')
  3269.                 ->setParameter(':bazaar', Product::QualityStatus_Bazaar);
  3270.         }
  3271.         if (!empty($filter['filterLastPieces'])) {
  3272.             $queryBuilder
  3273.                 ->andWhere('p.available = :filterLastPieces')
  3274.                 ->setParameter(':filterLastPieces', (int)$filter['filterLastPieces']);
  3275.         }
  3276.         $queryBuilder
  3277.             ->andWhere('m.domain = :domainId')
  3278.             ->setParameter(':domainId', $this->priceDomainId)
  3279.             ->andWhere('m.price > 0')
  3280.             ->andWhere('p.archived = 0');
  3281.         if (!isset($options['show_not_available'])) {
  3282.             $queryBuilder
  3283.                 ->andWhere('m.active = :active')
  3284.                 ->setParameter(':active', 1)
  3285.                 ->andWhere('m.listed = :listed')
  3286.                 ->setParameter(':listed', 1);
  3287.             $orX = $queryBuilder->expr()->orX(
  3288.                 $queryBuilder->expr()->andX(
  3289.                     $queryBuilder->expr()->gt(
  3290.                         'p.available',
  3291.                         0
  3292.                     ),
  3293.                     $queryBuilder->expr()->not(
  3294.                         $queryBuilder->expr()->eq(
  3295.                             'p.outOfStockStatus',
  3296.                             Product::OutOfStockStatus_VisibleShadow
  3297.                         )
  3298.                     )
  3299.                 )
  3300.             );
  3301.             $queryBuilder->andWhere($orX);
  3302.         }
  3303.         if (isset($filter['filterBestseller'])) {
  3304.             $queryBuilder
  3305.                 ->andWhere('m.bestSeller = :bestSeller')
  3306.                 ->setParameter(':bestSeller', (int)$filter['filterBestseller']);
  3307.         }
  3308.         if (isset($filter['filterOrderable'])) {
  3309.             $queryBuilder
  3310.                 ->andWhere('m.orderable = :orderable')
  3311.                 ->setParameter(':orderable', (int)$filter['filterOrderable']);
  3312.         }
  3313.         if (!empty($filter['filterSearch'])) {
  3314.             $andX = $queryBuilder->expr()->andX();
  3315.             $words = explode(' ', trim(preg_replace('/\s+/', ' ', $filter['filterSearch'])));
  3316.             foreach ($words as $index => $word) {
  3317.                 $andX
  3318.                     ->add(
  3319.                         $queryBuilder->expr()->like(
  3320.                             $queryBuilder->expr()->concat(
  3321.                                 'v.name',
  3322.                                 $queryBuilder->expr()->literal(' '),
  3323.                                 'ppt.title',
  3324.                                 $queryBuilder->expr()->literal(' '),
  3325.                                 'p.name'
  3326.                             ),
  3327.                             $queryBuilder->expr()->literal('%' . $word . '%')
  3328.                         )
  3329.                     );
  3330.             }
  3331.             $orX = $queryBuilder->expr()->orX(
  3332.                 $queryBuilder->expr()->andX(
  3333.                     $queryBuilder->expr()->gt(
  3334.                         'p.available',
  3335.                         0
  3336.                     ),
  3337.                     $queryBuilder->expr()->not(
  3338.                         $queryBuilder->expr()->eq(
  3339.                             'p.outOfStockStatus',
  3340.                             Product::OutOfStockStatus_VisibleShadow
  3341.                         )
  3342.                     )
  3343.                 )
  3344.             );
  3345.             $queryBuilder
  3346.                 ->andWhere(
  3347.                     $queryBuilder->expr()->orX(
  3348.                         $andX,
  3349.                         $queryBuilder->expr()->like(
  3350.                             'p.code',
  3351.                             $queryBuilder->expr()->literal('%' . $filter['filterSearch'] . '%')
  3352.                         )
  3353.                     )
  3354.                 )
  3355.                 ->orWhere('p.ean = ' . $queryBuilder->expr()->literal($filter['filterSearch']))
  3356.                 ->orWhere('p.partNumber = ' . $queryBuilder->expr()->literal($filter['filterSearch']))
  3357.                 ->andWhere('m.currentVatPrice != 0')
  3358.                 ->andWhere($orX);
  3359.         }
  3360.         $isNewProductPage = key_exists('filterCategories', $filter);
  3361.         if ($isNewProductPage) {
  3362.             $queryBuilder
  3363.                 ->andWhere('m.activationDate > :currentDateMinus1Month')
  3364.                 ->setParameter(':currentDateMinus1Month', (new \DateTime('-1 month')));
  3365.         }
  3366.         if (!isset($options['count']) && !isset($options['filterOptions'])) {
  3367.             if (!empty($filter['sort']) && !empty($filter['order'])) {
  3368.                 $orderByOption = false;
  3369.                 switch ($filter['sort']) {
  3370.                     case 'price':
  3371.                         $orderByOption = 'm.currentVatPrice';
  3372.                         break;
  3373.                     case 'created':
  3374.                         $orderByOption = 'm.activationDate';
  3375.                         break;
  3376.                     case 'name':
  3377.                         $orderByOption = $queryBuilder->expr()->concat(
  3378.                             'v.name',
  3379.                             $queryBuilder->expr()->concat(
  3380.                                 $queryBuilder->expr()->literal(' '),
  3381.                                 'p.name'
  3382.                             )
  3383.                         );
  3384.                         break;
  3385.                     case 'popular':
  3386.                         $orderByOption = 'p.soldDuringTimeFrame';
  3387.                         break;
  3388.                     case 'discountedDate':
  3389.                         $orderByOption = 'm.discountedDate';
  3390.                         break;
  3391.                     case 'lastSale':
  3392.                         $orderByOption = 'p.lastSale';
  3393.                         break;
  3394.                 }
  3395.                 $order = $filter['order'] == 'asc' ? 'asc' : 'desc';
  3396.                 $queryBuilder->addOrderBy(
  3397.                     $orderByOption ?: ($isNewProductPage ? 'm.activationDate' : 'p.available'),
  3398.                     $order
  3399.                 );
  3400.             }
  3401.             if (isset($filter['start']) && isset($filter['limit'])) {
  3402.                 if ($filter['start'] < 0) {
  3403.                     $filter['start'] = 0;
  3404.                 }
  3405.                 if ($filter['limit'] < 1) {
  3406.                     $filter['limit'] = 12;
  3407.                 }
  3408.                 $queryBuilder
  3409.                     ->setFirstResult($filter['start'])
  3410.                     ->setMaxResults($filter['limit']);
  3411.             }
  3412.         }
  3413.         if (isset($filter['searchInDockTonerDesc']) && $filter['searchInDockTonerDesc']) {
  3414.             $orWhereString = "p.type IN (16,92,106,128) AND m.detail LIKE :filterSearchLike";
  3415.             $queryBuilder
  3416.                 ->setParameter('filterSearchLike', '%' . $filter['filterSearch'] . '%');
  3417.             if (isset($filter['filterPriceMax']) && $filter['filterPriceMax'] > 0) {
  3418.                 $orWhereString .= ' AND m.currentVatPrice >= :filterPriceMin AND m.currentVatPrice <= :filterPriceMax';
  3419.                 $queryBuilder
  3420.                     ->setParameter('filterPriceMin', $filter['filterPriceMin'])
  3421.                     ->setParameter('filterPriceMax', $filter['filterPriceMax']);
  3422.             }
  3423.             $queryBuilder
  3424.                 ->orWhere($orWhereString);
  3425.         }
  3426.         return $queryBuilder->getQuery();
  3427.     }
  3428.     /**
  3429.      * @param Product $product
  3430.      * @return array
  3431.      */
  3432.     protected function getProductIcons(Product $product)
  3433.     {
  3434.         $productAttributeOptions = $product->getSpecificationProductAttributeOptions();
  3435.         $badges = $product->getBadges();
  3436.         $productTypeId = $product->getType()->getId();
  3437.         $icons = [
  3438.             [
  3439.                 'attributeId' => 141,            // PC Processor family
  3440.                 'attributeOptionId' => 1339,        // i3
  3441.                 'file' => 'processor-i3.svg',
  3442.                 'sortOrder' => 1,
  3443.                 'title' => 'Intel Core i3',
  3444.             ], [
  3445.                 'attributeId' => 141,            // PC Processor family
  3446.                 'attributeOptionId' => 1340,        // i5
  3447.                 'file' => 'processor-i5.svg',
  3448.                 'sortOrder' => 2,
  3449.                 'title' => 'Intel Core i5',
  3450.             ], [
  3451.                 'attributeId' => 141,            // PC Processor family
  3452.                 'attributeOptionId' => 1341,        // i7
  3453.                 'file' => 'processor-i7.svg',
  3454.                 'sortOrder' => 3,
  3455.                 'title' => 'Intel Core i7',
  3456.             ], [
  3457.                 'attributeId' => 142,            // PC Processor family
  3458.                 'attributeOptionId' => 1357,        // i3
  3459.                 'file' => 'processor-i3.svg',
  3460.                 'sortOrder' => 4,
  3461.                 'title' => 'Intel Core i3',
  3462.             ], [
  3463.                 'attributeId' => 142,            // PC Processor family
  3464.                 'attributeOptionId' => 1358,        // i5
  3465.                 'file' => 'processor-i5.svg',
  3466.                 'sortOrder' => 5,
  3467.                 'title' => 'Intel Core i5',
  3468.             ], [
  3469.                 'attributeId' => 142,            // PC Processor family
  3470.                 'attributeOptionId' => 1359,        // i7
  3471.                 'file' => 'processor-i7.svg',
  3472.                 'sortOrder' => 6,
  3473.                 'title' => 'Intel Core i7',
  3474.             ], [
  3475.                 'attributeId' => 15,            // Bluetooth
  3476.                 'attributeOptionId' => 567,            // Yes
  3477.                 'file' => 'bluetooth.svg',
  3478.                 'sortOrder' => 7,
  3479.                 'title' => 'Bluetooth',
  3480.             ], [
  3481.                 'attributeId' => 118,            // Technology
  3482.                 'attributeOptionId' => 926,            // IPS
  3483.                 'file' => 'ips.svg',
  3484.                 'sortOrder' => 8,
  3485.                 'title' => 'IPS display',
  3486.             ], [
  3487.                 'attributeId' => 5,            // SSD
  3488.                 'attributeOptionId' => false,        // More Options
  3489.                 'file' => 'ssd.svg',
  3490.                 'sortOrder' => 9,
  3491.                 'title' => 'SSD disc',
  3492.             ], [
  3493.                 'attributeId' => 16,            // Wi-Fi
  3494.                 'attributeOptionId' => 217,            // Yes
  3495.                 'file' => 'wifi.svg',
  3496.                 'sortOrder' => 10,
  3497.                 'title' => 'Wifi',
  3498.             ], [
  3499.                 'attributeId' => 139,            // Touchscreen
  3500.                 'attributeOptionId' => 1295,        // Yes
  3501.                 'file' => 'touchdisplay.svg',
  3502.                 'sortOrder' => 11,
  3503.                 'title' => 'Touch display',
  3504.             ], [
  3505.                 'attributeId' => 17,            // NumPad
  3506.                 'attributeOptionId' => 555,            // Yes
  3507.                 'file' => 'numeric.svg',
  3508.                 'sortOrder' => 12,
  3509.                 'title' => 'Numeric keyboard',
  3510.             ], [
  3511.                 'attributeId' => 50,            // Night light
  3512.                 'attributeOptionId' => 556,            // Yes
  3513.                 'file' => 'keyboardlight.svg',
  3514.                 'sortOrder' => 13,
  3515.                 'title' => 'Night light',
  3516.             ], [
  3517.                 'attributeId' => 46,            // Connectors external PC
  3518.                 'attributeOptionId' => 504,            // HDMI
  3519.                 'file' => 'hdmi.svg',
  3520.                 'sortOrder' => 14,
  3521.                 'title' => 'HDMI connector',
  3522.             ], [
  3523.                 'attributeId' => 45,            // Connectors external NB
  3524.                 'attributeOptionId' => 544,            // HDMI
  3525.                 'file' => 'hdmi.svg',
  3526.                 'sortOrder' => 15,
  3527.                 'title' => 'HDMI connector',
  3528.             ], [
  3529.                 'attributeId' => 68,            // Graphical inputs
  3530.                 'attributeOptionId' => 733,            // HDMI
  3531.                 'file' => 'hdmi.svg',
  3532.                 'sortOrder' => 16,
  3533.                 'title' => 'HDMI connector',
  3534.             ], [
  3535.                 'attributeId' => 55,            // Fingerprint
  3536.                 'attributeOptionId' => 564,            // Yes
  3537.                 'file' => 'fingerprint.svg',
  3538.                 'sortOrder' => 17,
  3539.                 'title' => 'Fingerprint sensor',
  3540.             ], [
  3541.                 'attributeId' => 63,            // Maximum resolution
  3542.                 'attributeOptionId' => 632,            // Full HD (1920 x 1080)
  3543.                 'file' => 'fullhd.svg',
  3544.                 'sortOrder' => 18,
  3545.                 'title' => 'Full HD display',
  3546.             ], [
  3547.                 'attributeId' => 12,            // Screen size
  3548.                 'attributeOptionId' => 201,            // 19
  3549.                 'file' => 'screen-size-19.svg',
  3550.                 'sortOrder' => 19,
  3551.                 'title' => '19"',
  3552.             ], [
  3553.                 'attributeId' => 12,            // Screen size
  3554.                 'attributeOptionId' => 1283,        // 20
  3555.                 'file' => 'screen-size-20.svg',
  3556.                 'sortOrder' => 20,
  3557.                 'title' => '20"',
  3558.             ], [
  3559.                 'attributeId' => 12,            // Screen size
  3560.                 'attributeOptionId' => 1019,        // 21.5
  3561.                 'file' => 'screen-size-21-3.svg',
  3562.                 'sortOrder' => 21,
  3563.                 'title' => '21.3"',
  3564.             ], [
  3565.                 'attributeId' => 12,            // Screen size
  3566.                 'attributeOptionId' => 402,        // 21.5
  3567.                 'file' => 'screen-size-21-5.svg',
  3568.                 'sortOrder' => 21,
  3569.                 'title' => '21.5"',
  3570.             ], [
  3571.                 'attributeId' => 12,            // Screen size
  3572.                 'attributeOptionId' => 202,            // 22
  3573.                 'file' => 'screen-size-22.svg',
  3574.                 'sortOrder' => 22,
  3575.                 'title' => '22"',
  3576.             ], [
  3577.                 'attributeId' => 12,            // Screen size
  3578.                 'attributeOptionId' => 203,            // 23
  3579.                 'file' => 'screen-size-23.svg',
  3580.                 'sortOrder' => 23,
  3581.                 'title' => '23"',
  3582.             ], [
  3583.                 'attributeId' => 12,            // Screen size
  3584.                 'attributeOptionId' => 204,            // 24
  3585.                 'file' => 'screen-size-24.svg',
  3586.                 'sortOrder' => 24,
  3587.                 'title' => '24"',
  3588.             ], [
  3589.                 'attributeId' => 12,            // Screen size
  3590.                 'attributeOptionId' => 205,            // 27
  3591.                 'file' => 'screen-size-27.svg',
  3592.                 'sortOrder' => 25,
  3593.                 'title' => '27"',
  3594.             ], [
  3595.                 'attributeId' => 10,            // ODD PC
  3596.                 'attributeOptionId' => 179,            // DVD-RW
  3597.                 'file' => 'optical-drive-dvd-rw.svg',
  3598.                 'sortOrder' => 25,
  3599.                 'title' => 'DVD-RW',
  3600.             ], [
  3601.                 'attributeId' => 11,            // ODD NB
  3602.                 'attributeOptionId' => 189,            // DVD-RW
  3603.                 'file' => 'optical-drive-dvd-rw.svg',
  3604.                 'sortOrder' => 25,
  3605.                 'title' => 'DVD-RW',
  3606.             ], [
  3607.                 'attributeId' => 46,            // Connector PC
  3608.                 'attributeOptionId' => 503,            // Displayport
  3609.                 'file' => 'displayport.svg',
  3610.                 'sortOrder' => 25,
  3611.                 'title' => 'display port',
  3612.             ], [
  3613.                 'attributeId' => 45,            // Connector NB
  3614.                 'attributeOptionId' => 547,            // Displayport
  3615.                 'file' => 'displayport.svg',
  3616.                 'sortOrder' => 25,
  3617.                 'title' => 'display port',
  3618.             ], [
  3619.                 'attributeId' => 68,            // Graphical input
  3620.                 'attributeOptionId' => 628,            // Displayport
  3621.                 'file' => 'displayport.svg',
  3622.                 'sortOrder' => 25,
  3623.                 'title' => 'display port',
  3624.             ], [
  3625.                 'attributeId' => 71,            // Speakers
  3626.                 'attributeOptionId' => 596,            // Built in
  3627.                 'file' => 'speakers.svg',
  3628.                 'sortOrder' => 25,
  3629.                 'title' => 'speakers',
  3630.             ], [
  3631.                 'attributeId' => 19,            // Webcam
  3632.                 'attributeOptionId' => 571,            // Webcam
  3633.                 'file' => 'webcam.svg',
  3634.                 'sortOrder' => 25,
  3635.                 'title' => 'web camera',
  3636.             ], [
  3637.                 'attributeId' => 19,            // Webcam
  3638.                 'attributeOptionId' => 574,            // Webcam HD
  3639.                 'file' => 'webcam.svg',
  3640.                 'sortOrder' => 25,
  3641.                 'title' => 'web camera',
  3642.             ], [
  3643.                 'attributeId' => 19,            // Webcam
  3644.                 'attributeOptionId' => 763,            // standard
  3645.                 'file' => 'webcam.svg',
  3646.                 'sortOrder' => 25,
  3647.                 'title' => 'web camera',
  3648.             ], [
  3649.                 'attributeId' => 19,            // Webcam
  3650.                 'attributeOptionId' => 1481,        // Full hd
  3651.                 'file' => 'webcam.svg',
  3652.                 'sortOrder' => 25,
  3653.                 'title' => 'web camera',
  3654.             ], [
  3655.                 'attributeId' => 34,       // Monitor - LED screen
  3656.                 'attributeOptionId' => 452,
  3657.                 'file' => 'led-screen.svg',
  3658.                 'sortOrder' => 30,
  3659.                 'title' => 'LED screen',
  3660.             ], [
  3661.                 'badge' => 'senior',    // Senior
  3662.                 'attributeId' => false,
  3663.                 'attributeOptionId' => false,
  3664.                 'file' => 'senior.svg',
  3665.                 'sortOrder' => 26,
  3666.             ], [
  3667.                 'badge' => 'gamer',    // Gamer
  3668.                 'attributeId' => false,
  3669.                 'attributeOptionId' => false,
  3670.                 'file' => 'gaming.svg',
  3671.                 'sortOrder' => 27,
  3672.             ], [
  3673.                 'badge' => 'internet',    // Internet
  3674.                 'attributeId' => false,
  3675.                 'attributeOptionId' => false,
  3676.                 'file' => 'internet.svg',
  3677.                 'sortOrder' => 28,
  3678.             ], [
  3679.                 'badge' => 'business',    // Work
  3680.                 'attributeId' => false,
  3681.                 'attributeOptionId' => false,
  3682.                 'file' => 'company.svg',
  3683.                 'sortOrder' => 29,
  3684.             ],
  3685.         ];
  3686.         $productIcons = [];
  3687.         foreach ($productAttributeOptions as $productAttributeOption) {
  3688.             $attributeOption = $productAttributeOption->getOption();
  3689.             $attribute = $attributeOption->getAttribute();
  3690.             foreach ($icons as $icon) {
  3691.                 if ((int)$icon['attributeId'] == (int)$attribute->getId()) {
  3692.                     if ((bool)$icon['attributeOptionId'] === false) {
  3693.                         $productIcons[$icon['file']] = $icon;
  3694.                         break;
  3695.                     } elseif ((int)$icon['attributeOptionId'] == (int)$attributeOption->getId()) {
  3696.                         $productIcons[$icon['file']] = $icon;
  3697.                         break;
  3698.                     }
  3699.                 }
  3700.             }
  3701.         }
  3702.         foreach ($badges as $badge) {
  3703.             foreach ($icons as $icon) {
  3704.                 if (isset($icon['badge']) && $icon['badge'] == $badge) {
  3705.                     $productIcons[$icon['file']] = $icon;
  3706.                     break;
  3707.                 }
  3708.             }
  3709.         }
  3710.         foreach ($icons as $icon) {
  3711.             if (isset($icon['productTypeId']) && $icon['productTypeId'] == $productTypeId) {
  3712.                 $productIcons[$icon['file']] = $icon;
  3713.             }
  3714.         }
  3715.         $sortOrder = [];
  3716.         foreach ($productIcons as $key => $value) {
  3717.             $sortOrder[$key] = $value['sortOrder'];
  3718.         }
  3719.         array_multisort($sortOrder, SORT_ASC, $productIcons);
  3720.         return array_slice($productIcons, 0, 5);
  3721.     }
  3722.     /**
  3723.      * @param string|null $detail
  3724.      * @return string
  3725.      */
  3726.     public static function createFormatedDetail(?string $detail): string
  3727.     {
  3728.         $result = '';
  3729.         $lines = preg_split('/\\n\\r*/', $detail);
  3730.         $is_list = false;
  3731.         foreach ($lines as $line) {
  3732.             $line = trim($line);
  3733.             $line = str_replace('[b]', '<strong>', $line);
  3734.             $line = str_replace('[/b]', '</strong>', $line);
  3735.             $line = str_replace('[bl]', '', $line);
  3736.             $line = str_replace('[/bl]', '', $line);
  3737.             $line = str_replace('[r]', '', $line);
  3738.             $line = str_replace('[/r]', '', $line);
  3739.             if ($line == '[l]' && !$is_list) {
  3740.                 $result .= '<ul>';
  3741.                 $is_list = true;
  3742.             } elseif ($line == '[/l]' && $is_list) {
  3743.                 $result .= '</ul>';
  3744.                 $is_list = false;
  3745.             } elseif ($is_list) {
  3746.                 $result .= '<li>' . $line . '</li>';
  3747.             } elseif ($line != '') {
  3748.                 $result .= '<span>' . $line . '</span>';
  3749.             }
  3750.         }
  3751.         return $result;
  3752.     }
  3753.     /**
  3754.      * @param Product $product
  3755.      * @param Domain $domain
  3756.      * @param int $limit
  3757.      * @return array
  3758.      * @throws \Doctrine\DBAL\DBALException
  3759.      * @throws \Doctrine\ORM\NoResultException
  3760.      * @throws \Doctrine\ORM\NonUniqueResultException
  3761.      */
  3762.     public function getRecommendedProducts(Product $product, Domain $domain, $limit = 4)
  3763.     {
  3764.         $upsellProducts = [];
  3765.         // Antivirus Software
  3766.         if (in_array($product->getType()->getId(), [52, 60, 107, 116])) {
  3767.             // Except Chromebook & Apple Products
  3768.             if (!in_array('Chromebook', $product->getBadges()) && $product->getVendor()->getId() !== 70) {
  3769.                 $products = $this->entityManager
  3770.                     ->createQuery("SELECT p 
  3771.                         FROM AtechNetCoreBundle:Product p 
  3772.                         LEFT JOIN p.productAttributes pa 
  3773.                         LEFT JOIN AtechNetCoreBundle:ProductMeta pm WITH pm.product = p.id AND pm.domain = :domain 
  3774.                         WHERE pm.active = 1 AND pm.orderable = 1 AND p.qualityStatus != :qualityStatus AND p.type = 82 AND p.vendor IN ( 141, 21 ) ORDER BY p.vendor DESC")
  3775.                     ->setParameters([
  3776.                         'domain' => $domain,
  3777.                         'qualityStatus' => Product::QualityStatus_Bazaar,
  3778.                     ])
  3779.                     ->getResult();
  3780.                 foreach ($products as $p) {
  3781.                     if (!isset($upsellProducts[$p->getId()]) && count($upsellProducts) < 3) {
  3782.                         $upsellProducts[$p->getId()] = $this->getStructuredDataForProduct($p, 'list');
  3783.                     }
  3784.                 }
  3785.             }
  3786.         }
  3787.         // Other Accessory
  3788.         $recommendedProductTypes = self::getRecommendedProductsStructureByProductType($product->getType()->getId());
  3789.         if (count($recommendedProductTypes) > 0) {
  3790.             $recommendedProducts = [];
  3791.             foreach ($recommendedProductTypes as $rpt) {
  3792.                 $where = "";
  3793.                 $where .= count($rpt['productTypeIDs']) > 0 ? "p.type IN (" . implode(',', $rpt['productTypeIDs']) . ")" : '';
  3794.                 if (count($rpt['productAttributeOptionIDs']) > 0) {
  3795.                     if ($where != "") {
  3796.                         $where .= " AND ";
  3797.                     }
  3798.                     $where .= count($rpt['productTypeIDs']) > 0 ? "pa.option IN (" . implode(',', $rpt['productAttributeOptionIDs']) . ")" : '';
  3799.                 }
  3800.                 if (count($rpt['productIDs']) > 0) {
  3801.                     if ($where != "") {
  3802.                         $where .= " AND ";
  3803.                     }
  3804.                     $where .= "p.id IN (" . implode(',', $rpt['productIDs']) . ")";
  3805.                 }
  3806.                 if ($rpt['priceRange']['min'] || $rpt['priceRange']['max']) {
  3807.                     if ($where != "") {
  3808.                         $where .= " AND ";
  3809.                     }
  3810.                     if ($rpt['priceRange']['min']) {
  3811.                         $where .= "pm.currentVatPrice >= " . $rpt['priceRange']['min'];
  3812.                     }
  3813.                     if ($rpt['priceRange']['max']) {
  3814.                         if ($rpt['priceRange']['min']) {
  3815.                             $where .= " AND ";
  3816.                         }
  3817.                         $where .= "pm.currentVatPrice <= " . $rpt['priceRange']['max'];
  3818.                     }
  3819.                 }
  3820.                 if ($rpt['matchingVendor']) {
  3821.                     if ($where != "") {
  3822.                         $where .= " AND ";
  3823.                     }
  3824.                     $where .= "p.vendor = " . $product->getVendor()->getId();
  3825.                 }
  3826.                 $products = $this
  3827.                     ->entityManager
  3828.                     ->createQuery("SELECT p
  3829.                         FROM AtechNetCoreBundle:Product p
  3830.                         LEFT JOIN p.productAttributes pa
  3831.                         LEFT JOIN AtechNetCoreBundle:ProductMeta pm WITH pm.product = p.id AND pm.domain = :domain
  3832.                         WHERE pm.active = 1 AND pm.orderable = 1 AND p.qualityStatus != :qualityStatus AND " . $where)
  3833.                     ->setParameters([
  3834.                         'domain' => $domain,
  3835.                         'qualityStatus' => Product::QualityStatus_Bazaar,
  3836.                     ])
  3837.                     ->setMaxResults($rpt['limit'])
  3838.                     ->getResult();
  3839.                 foreach ($products as $p) {
  3840.                     if (!isset($recommendedProducts[$p->getId()]) && count($recommendedProducts) < $limit) {
  3841.                         $recommendedProducts[$p->getId()] = $p;
  3842.                     }
  3843.                 }
  3844.             }
  3845.             foreach ($recommendedProducts as $p) {
  3846.                 $upsellProducts[$p->getId()] = $this->getStructuredDataForProduct($p, 'list');
  3847.             }
  3848.             $upsellProductCount = count($upsellProducts);
  3849.             if ($upsellProductCount < $limit && $product->getType()->getId() != 41) {
  3850.                 $missingProductsCount = $limit - $upsellProductCount;
  3851.                 $additionalUpsellProducts = $this->getSimilarProducts($product->getId(), $missingProductsCount);
  3852.                 foreach ($additionalUpsellProducts as $ap) {
  3853.                     $upsellProducts[] = $ap;
  3854.                 }
  3855.             }
  3856.         }
  3857.         return $upsellProducts;
  3858.     }
  3859.     /**
  3860.      * @param int $productTypeId
  3861.      * @param CacheItemPoolInterface $cache
  3862.      * @param Domain $domain
  3863.      * @param Request $request
  3864.      * @param SeoUrlHelper $seoUrlHelper
  3865.      * @param EntityManagerInterface $entityManager
  3866.      * @return array
  3867.      */
  3868.     public function getRecommendedProductCategories(int $productTypeId, CacheItemPoolInterface $cache, Domain $domain, Request $request, SeoUrlHelper $seoUrlHelper, EntityManagerInterface $entityManager)
  3869.     {
  3870.         $recommendedProductCategoriesCacheKey = 'product.v1.recommendedProductCategories.' . $productTypeId . '.' . $domain->getId() . '.' . $request->getLocale();
  3871.         $recommendedProductCategoriesCacheItem = $cache->getItem($recommendedProductCategoriesCacheKey);
  3872.         if (!$recommendedProductCategoriesCacheItem->isHit()) {
  3873.             $recommendedCategories = [];
  3874.             $language = $domain->getLanguage(); // TODO: Localise Title of Categories
  3875.             foreach (self::getRecommendedProductTypesStructure($domain->getId(), $productTypeId) as $categoryId) {
  3876.                 $category = $entityManager->getRepository(CategoryPage::class)->findOneById($categoryId);
  3877.                 /**
  3878.                  * @var CategoryPage $category
  3879.                  */
  3880.                 if ($category) {
  3881.                     // Parent
  3882.                     if ($category->getParentCategory()) {
  3883.                         $parentId = $category->getParentCategory()->getId();
  3884.                     } else {
  3885.                         $parentId = 0;
  3886.                     }
  3887.                     $recommendedCategories[] = [
  3888.                         'id' => $category->getId(),
  3889.                         'title' => $category->getMetaByLanguage($language)->getTitle(),
  3890.                         'href' => $seoUrlHelper->link('category', [
  3891.                             'path'      => ($parentId > 0 ? $parentId . '_' : '') . $category->getId(), 
  3892.                             'isBazaar'  => $category->isShowOnlyBazaar()
  3893.                         ]),
  3894.                     ];
  3895.                 }
  3896.             }
  3897.             $recommendedProductCategoriesCacheItem->set($recommendedCategories);
  3898.             $recommendedProductCategoriesCacheItem->expiresAfter(604800);
  3899.             $cache->save($recommendedProductCategoriesCacheItem);
  3900.             return $recommendedCategories;
  3901.         } else {
  3902.             return $recommendedProductCategoriesCacheItem->get();
  3903.         }
  3904.     }
  3905.     /**
  3906.      * @param $requestUrl
  3907.      * @param $structuredDataResults
  3908.      * @param $imagePlaceholder
  3909.      * @return array
  3910.      */
  3911.     public function convertStructuredDataToListViewData($requestUrl, $structuredDataResults, $imagePlaceholder): array
  3912.     {
  3913.         $convertedData = [];
  3914.         foreach ($structuredDataResults as $result) {
  3915.             $image = $imagePlaceholder;
  3916.             if (is_object($result['images'])) {
  3917.                 $image = $this->getImage($result['images'], 'product/' . $result['code'])->resize(Image::PRODUCT_LIST_THUMB_WIDTH, Image::PRODUCT_LIST_THUMB_HEIGHT);
  3918.             }
  3919.             $result['title'] = in_array($requestUrl->getDomainId(), [Domain::DOMAIN_ID_RT_EU, Domain::DOMAIN_ID_WS_EU]) ?
  3920.                 str_replace([' (Quality: Bazar)', ' (Quality: Bazár)', ' (Quality: Bazár, '], ['', '', ' ('], $result['title']) :
  3921.                 $result['title'];
  3922.             $convertedData[] = [
  3923.                 'id' => $result['id'],
  3924.                 'title' => ProductHelper::shortenProductName($result['vendor'], $result['title']),
  3925.                 'vendor' => $result['vendor'],
  3926.                 'vendorId' => $result['vendorId'],
  3927.                 'typeId' => $result['productTypeId'],
  3928.                 'labels' => $result['badges'],
  3929.                 'price' => $result['vatPrice'],
  3930.                 'special' => ($result['isDiscounted'] ? $result['discountVatPrice'] : false),
  3931.                 'rawPrice' => $result['rawPrice'],
  3932.                 'rawDiscountPrice' => $result['rawDiscountPrice'],
  3933.                 'image' => $image,
  3934.                 'attributes' => $result['attributes'],
  3935.                 'attributesListTxt' => $result['attributesListTxt'],
  3936.                 'available' => $result['availableQty'],
  3937.                 'href' => $requestUrl->link('product', [
  3938.                     'product_id' => $result['id'], 
  3939.                     'isBazaar'   => $result['isBazaar'] ?? 0
  3940.                 ]),
  3941.                 'active' => $result['active'],
  3942.                 'timedSalesActive' => $result['timedSalesActive'],
  3943.                 'reviewCount' => $result['reviewCount'],
  3944.                 'reviewStars' => $result['reviewStars'],
  3945.                 'reviews' => $result['reviews'],
  3946.                 'freeShipping' => $result['freeShipping'],
  3947.                 'labelOS' => $this->getOSLabel($result),
  3948.                 'qualityStatusId' => $result['qualityStatusId'],
  3949.                 'qualityStatusTitle' => Product::getQualityStatusChoices()[$result['qualityStatusId']],
  3950.                 'isBestseller' => $result['isBestseller'] ?? false
  3951.             ];
  3952.         }
  3953.         return $convertedData;
  3954.     }
  3955.     /**
  3956.      * @param string $productGroupTitle
  3957.      * @param Domain $domain
  3958.      * @return array{
  3959.      *      productGroupProducts: array<int,Product>, 
  3960.      *      productGroupCodesAdded: array<int,string>, 
  3961.      *      productsToDisplay: int, 
  3962.      *      isRandom: bool|null, 
  3963.      *      customData: array<int, array{title: string, description?: string}>}
  3964.      * }
  3965.      */
  3966.     public function getProductGroupProducts(string $productGroupTitle, Domain $domain): array
  3967.     {
  3968.         /** @var ProductGroup|null $productGroup */
  3969.         $productGroup = $this->entityManager->getRepository(ProductGroup::class)->findOneBy([
  3970.             'title'  => $productGroupTitle, 
  3971.             'domain' => $domain
  3972.         ]);
  3973.         $productGroupProducts = [];
  3974.         $productGroupCodesAdded = [];
  3975.         $customData = [];
  3976.         if ( !is_null($productGroup) ) {
  3977.             /** @var ProductGroupMember $member */
  3978.             foreach ($productGroup->getMembers() as $member) {
  3979.                 if (count($productGroupCodesAdded) >= $productGroup->getProductsToDisplay()) {
  3980.                     continue;
  3981.                 }
  3982.                 $product = $member->getProduct();
  3983.                 $productMeta = $product->getDomainMeta($domain);
  3984.                 if (!is_null($member->getTitle())) {
  3985.                     $customData[$product->getId()]['title'] = $member->getTitle();
  3986.                 }
  3987.                 if (!is_null($member->getDescription())) {
  3988.                     $customData[$product->getId()]['description'] = $member->getDescription();
  3989.                 }
  3990.                 if ( str_contains($productGroupTitle, '.bazaar') ) {
  3991.                     if ( $product->getAvailable() > 0 
  3992.                         && $productMeta->isActive() 
  3993.                         && $productMeta->isOrderable() 
  3994.                         && $productMeta->isListed() 
  3995.                         && $product->getQualityStatus() == Product::QualityStatus_Bazaar 
  3996.                     ) {
  3997.                         $productGroupProducts[] = $product;
  3998.                         $productGroupCodesAdded[] = $product->getCode();
  3999.                     }                    
  4000.                 } elseif ($productGroupTitle == 'homepage.main' && $domain->getType() == 'retail') {
  4001.                     if ($product->getAvailable() > 0
  4002.                         && $productMeta->isActive()
  4003.                         && $productMeta->isOrderable()
  4004.                         && $productMeta->isListed()
  4005.                         && $product->getQualityStatus() != Product::QualityStatus_Bazaar)
  4006.                     {
  4007.                         $productGroupProducts[] = $product;
  4008.                         $productGroupCodesAdded[] = $product->getCode();
  4009.                     }
  4010.                 } else {
  4011.                     if ($product->getAvailable() > 0
  4012.                         && $productMeta->isActive()
  4013.                         && $productMeta->isOrderable()
  4014.                         && $productMeta->isListed())
  4015.                     {
  4016.                         $productGroupProducts[] = $product;
  4017.                         $productGroupCodesAdded[] = $product->getCode();
  4018.                     }
  4019.                 }
  4020.             }
  4021.         }
  4022.         return [
  4023.             'productGroupProducts' => $productGroupProducts,
  4024.             'productGroupCodesAdded' => $productGroupCodesAdded,
  4025.             'productsToDisplay' => !is_null($productGroup) ? $productGroup->getProductsToDisplay() : 0,
  4026.             'isRandom' => $productGroup?->getShowRandom(),
  4027.             'customData' => $customData,
  4028.         ];
  4029.     }
  4030.     /**
  4031.      * @param Domain $domain
  4032.      * @param integer $categoryGroupType
  4033.      * @param SeoUrlHelper $seoUrlHelper
  4034.      * @return array<string,string|bool|int>
  4035.      */
  4036.     public function getCategoryGroup(Domain $domain, int $categoryGroupType, SeoUrlHelper $seoUrlHelper): array
  4037.     {
  4038.         $categoryGroup = $this->entityManager->getRepository(CategoryGroup::class)->findOneBy([
  4039.             'type' => $categoryGroupType,
  4040.             'domain' => $domain,
  4041.             'active' => true,
  4042.         ]);
  4043.         $result = [];
  4044.         if (!is_null($categoryGroup)) {
  4045.             if (in_array($categoryGroup->getType(), [CategoryGroup::CG_TYPE_FEATURED_CATEGORIES_UPPER, CategoryGroup::CG_TYPE_FEATURED_CATEGORIES_LOWER])) {
  4046.                 $categoryMember = $categoryGroup->getCategories()->count() > 0 ? $categoryGroup->getCategories()->first() : [];
  4047.                 $result['title'] = $categoryMember->getTitle();
  4048.                 $result['description'] = $categoryMember->getDescription();
  4049.                 $result['isWizard'] = $categoryMember->isWizard();
  4050.                 $result['href'] = $categoryMember->isWizard() ? '/wizard' : $seoUrlHelper->link('category', [
  4051.                     'path' => $categoryMember->getCategory()->getId(),
  4052.                     'isBazaar'  => $categoryMember->getCategory()->isShowOnlyBazaar()
  4053.                 ]);
  4054.                 $image = $categoryMember->getImage();
  4055.                 $imageFilenames = [
  4056.                     'filename' => $image ? $image->getFilename() : '',
  4057.                     'originalName' => $image ? $image->getOriginalName() : '',
  4058.                 ];
  4059.                 $result['image'] = $image ? $this->getImage($imageFilenames, CategoryGroupMemberImage::CLASS_IMAGE_DIRECTORY)->resize(Image::HEADER_FEATURED_CATEGORY_WIDTH, Image::HEADER_FEATURED_CATEGORY_HEIGHT) : '';
  4060.             }
  4061.         }
  4062.         return $result;
  4063.     }
  4064.     /**
  4065.      * @param $vendor
  4066.      * @param $name
  4067.      * @return string
  4068.      */
  4069.     public static function shortenProductName($vendor, $name): string
  4070.     {
  4071.         $title = $name;
  4072.         $maxFullTitleLength = 75;
  4073.         $longTitle = $vendor . ' ' . $name;
  4074.         if (mb_strlen($longTitle) > $maxFullTitleLength) {
  4075.             $vendorLength = mb_strlen($vendor . ' ');
  4076.             $length = $maxFullTitleLength - $vendorLength;
  4077.             $title = mb_substr($title, 0, $length) . '...';
  4078.         }
  4079.         return $title;
  4080.     }
  4081.     /**
  4082.      * @param Product $product
  4083.      * @param Domain $domain
  4084.      * @return bool
  4085.      */
  4086.     public static function isFlashSaleValidForProduct(Product $product, Domain $domain): bool
  4087.     {
  4088.         $pm = $product->getDomainMeta($domain);
  4089.         $currentDateTime = new \DateTime();
  4090.         if ($product->getAvailable() > 0 && $pm->isActive() && $pm->isOrderable() && $pm->isListed() && $pm->isDiscounted()
  4091.             && (
  4092.                 (!is_null($pm->getSaleStart()) && !is_null($pm->getSaleEnd()) && $pm->getSaleStart() < $currentDateTime && $currentDateTime < $pm->getSaleEnd())
  4093.                 || (!is_null($pm->getSaleStart()) && is_null($pm->getSaleEnd()) && $pm->getSaleStart() < $currentDateTime)
  4094.                 || (is_null($pm->getSaleStart()) && !is_null($pm->getSaleEnd()) && $currentDateTime < $pm->getSaleEnd())
  4095.             )
  4096.         ) {
  4097.             return true;
  4098.         }
  4099.         return false;
  4100.     }
  4101.     /**
  4102.      * @param $product
  4103.      * @param bool $skipType
  4104.      * @return string
  4105.      */
  4106.     public static function getHeadingTitle($product, bool $skipType = false)
  4107.     {
  4108.         $productType = $skipType ? '' : ' ' . $product['productType'];
  4109.         return $product['title'] . $productType . ' - ' . $product['code'];
  4110.     }
  4111.     /**
  4112.      * @param $product
  4113.      * @param $translator
  4114.      */
  4115.     public static function getLongTitleWithConfig($product, $translator)
  4116.     {
  4117.         if ($product['qualityStatusId'] != Product::QualityStatus_New) {
  4118.             $isTinyPc = self::hasBadgeByStructuredData('Tiny PC', $product['badgesRaw']);
  4119.             switch ($product['productTypeId']) {
  4120.                 case ProductType::PRODUCT_TYPE_ID_SMARTPHONE:
  4121.                     $h1Title = $product['title'] . ' - ' . $product['code'];
  4122.                     $h1Title .= (!in_array($product['qualityStatusId'], [Product::QualityStatus_New, Product::QualityStatus_Platinum]) ? ' (' . $translator->trans('refurbished') . ')' : '');
  4123.                     break;
  4124.                 case ProductType::PRODUCT_TYPE_ID_MONITOR:
  4125.                     $h1Title = $product['title'] . ' - ' . $product['code'];
  4126.                     break;
  4127.                 case ProductType::PRODUCT_TYPE_ID_NOTEBOOK:
  4128.                     $h1Title = $product['title'] . ' - ' . $product['code'];
  4129.                     break;
  4130.                 case ProductType::PRODUCT_TYPE_ID_PC:
  4131.                 case ProductType::PRODUCT_TYPE_ID_PC_SET:
  4132.                     $h1Title = $product['title'] . ' - ' . $product['code'];
  4133.                     break;
  4134.                 case ProductType::PRODUCT_TYPE_ID_ALL_IN_ONE:
  4135.                     $h1Title = $product['title'] . ' - ' . $product['code'];
  4136.                     break;
  4137.                 default:
  4138.                     $h1Title = $product['title'] . ' - ' . $product['code'];
  4139.             }
  4140.         } else {
  4141.             $h1Title = $product['title'] . ' - ' . $product['code'];
  4142.         }
  4143.         return $h1Title;
  4144.     }
  4145.     /**
  4146.      * @param array $attributes
  4147.      * @return string
  4148.      */
  4149.     public static function getConfigurationInTitle($attributes)
  4150.     {
  4151.         $configurationInTitle = '';
  4152.         $configsToAdd = [
  4153.             32, 33, 35, 36, // Processors
  4154.             25, // VGA
  4155.             3, // RAM PC
  4156.             4, // RAM NB
  4157.             5, // SSD
  4158.             6, 7, 8, 9, // HDD
  4159.             12, // Screen size
  4160.             63, // Display resolution
  4161.             118, // IPS
  4162.         ];
  4163.         foreach ($configsToAdd as $configToAdd) {
  4164.             if (isset($attributes[$configToAdd])) {
  4165.                 if ($configurationInTitle != '') {
  4166.                     $configurationInTitle .= ', ';
  4167.                 }
  4168.                 $shortTitle = reset($attributes[$configToAdd]['options'])['shortTitle'];
  4169.                 $type = '';
  4170.                 if (in_array(substr($shortTitle, 0, 2), ['i3', 'i5', 'i7'])) {
  4171.                     $type = 'Intel Core ';
  4172.                 }
  4173.                 $postFix = '';
  4174.                 if ($configToAdd == 5) { // SSD
  4175.                     $postFix = ' SSD';
  4176.                 }
  4177.                 if (in_array($configToAdd, [6, 7, 8, 9])) { // HDD
  4178.                     $postFix = ' HDD';
  4179.                 }
  4180.                 if (in_array($configToAdd, [3, 4])) { // RAM
  4181.                     $postFix = ' RAM';
  4182.                 }
  4183.                 $configurationInTitle .= $type . $shortTitle . $postFix;
  4184.             }
  4185.         }
  4186.         return $configurationInTitle;
  4187.     }
  4188.     /**
  4189.      * @param $badgeTocheck
  4190.      * @param $badges
  4191.      * @return bool
  4192.      */
  4193.     private static function hasBadgeByStructuredData($badgeTocheck, $badges)
  4194.     {
  4195.         $hasBadge = false;
  4196.         foreach ($badges as $badge) {
  4197.             if (!$hasBadge && $badge == $badgeTocheck) {
  4198.                 $hasBadge = true;
  4199.             }
  4200.         }
  4201.         return $hasBadge;
  4202.     }
  4203.     /**
  4204.      * @param $productTypeId
  4205.      * @return array|array[]
  4206.      */
  4207.     private static function getRecommendedProductsStructureByProductType($productTypeId)
  4208.     {
  4209.         switch ($productTypeId) {
  4210.             case 60: // PC
  4211.             case 107: // PC SET
  4212.             case 116: // All in one
  4213.                 $recommendedProductTypes = [
  4214.                     [
  4215.                         // basic wired mouse
  4216.                         'productTypeIDs' => [
  4217.                             46,
  4218.                         ],
  4219.                         'productAttributeOptionIDs' => [
  4220.                             2020,
  4221.                         ],
  4222.                         'productIDs' => [],
  4223.                         'priceRange' => [
  4224.                             'min' => false,
  4225.                             'max' => false,
  4226.                         ],
  4227.                         'matchingVendor' => false,
  4228.                         'limit' => 4,
  4229.                     ],
  4230.                     [
  4231.                         // basic wireless mouse
  4232.                         'productTypeIDs' => [
  4233.                             46,
  4234.                         ],
  4235.                         'productAttributeOptionIDs' => [
  4236.                             2021,
  4237.                         ],
  4238.                         'productIDs' => [],
  4239.                         'priceRange' => [
  4240.                             'min' => false,
  4241.                             'max' => false,
  4242.                         ],
  4243.                         'matchingVendor' => false,
  4244.                         'limit' => 4,
  4245.                     ],
  4246.                     [
  4247.                         // middle wired mouse
  4248.                         'productTypeIDs' => [
  4249.                             46,
  4250.                         ],
  4251.                         'productAttributeOptionIDs' => [
  4252.                             2020,
  4253.                         ],
  4254.                         'productIDs' => [],
  4255.                         'priceRange' => [
  4256.                             'min' => false,
  4257.                             'max' => false,
  4258.                         ],
  4259.                         'matchingVendor' => false,
  4260.                         'limit' => 4,
  4261.                     ],
  4262.                     [
  4263.                         // middle wireless mouse
  4264.                         'productTypeIDs' => [
  4265.                             46,
  4266.                         ],
  4267.                         'productAttributeOptionIDs' => [
  4268.                             2021,
  4269.                         ],
  4270.                         'productIDs' => [],
  4271.                         'priceRange' => [
  4272.                             'min' => false,
  4273.                             'max' => false,
  4274.                         ],
  4275.                         'matchingVendor' => false,
  4276.                         'limit' => 4,
  4277.                     ],
  4278.                     [
  4279.                         // gamer wired mouse
  4280.                         'productTypeIDs' => [
  4281.                             46,
  4282.                         ],
  4283.                         'productAttributeOptionIDs' => [
  4284.                             2020,
  4285.                         ],
  4286.                         'productIDs' => [],
  4287.                         'priceRange' => [
  4288.                             'min' => false,
  4289.                             'max' => false,
  4290.                         ],
  4291.                         'matchingVendor' => false,
  4292.                         'limit' => 4,
  4293.                     ],
  4294.                     [
  4295.                         // gamer wireless mouse
  4296.                         'productTypeIDs' => [
  4297.                             46,
  4298.                         ],
  4299.                         'productAttributeOptionIDs' => [
  4300.                             2021,
  4301.                         ],
  4302.                         'productIDs' => [],
  4303.                         'priceRange' => [
  4304.                             'min' => false,
  4305.                             'max' => false,
  4306.                         ],
  4307.                         'matchingVendor' => false,
  4308.                         'limit' => 2,
  4309.                     ],
  4310.                     [
  4311.                         // wireless keyboard
  4312.                         'productTypeIDs' => [
  4313.                             38,
  4314.                         ],
  4315.                         'productAttributeOptionIDs' => [
  4316.                             2021,
  4317.                         ],
  4318.                         'productIDs' => [],
  4319.                         'priceRange' => [
  4320.                             'min' => false,
  4321.                             'max' => false,
  4322.                         ],
  4323.                         'matchingVendor' => false,
  4324.                         'limit' => 4,
  4325.                     ],
  4326.                     [
  4327.                         // wired keyboard
  4328.                         'productTypeIDs' => [
  4329.                             38,
  4330.                         ],
  4331.                         'productAttributeOptionIDs' => [
  4332.                             2020,
  4333.                         ],
  4334.                         'productIDs' => [],
  4335.                         'priceRange' => [
  4336.                             'min' => false,
  4337.                             'max' => false,
  4338.                         ],
  4339.                         'matchingVendor' => false,
  4340.                         'limit' => 4,
  4341.                     ],
  4342.                     [
  4343.                         // gamer keyboard
  4344.                         'productTypeIDs' => [
  4345.                             38,
  4346.                         ],
  4347.                         'productAttributeOptionIDs' => [],
  4348.                         'productIDs' => [],
  4349.                         'priceRange' => [
  4350.                             'min' => false,
  4351.                             'max' => false,
  4352.                         ],
  4353.                         'matchingVendor' => false,
  4354.                         'limit' => 2,
  4355.                     ],
  4356.                     [
  4357.                         // wireless headphones
  4358.                         'productTypeIDs' => [
  4359.                             35,
  4360.                         ],
  4361.                         'productAttributeOptionIDs' => [
  4362.                             2021,
  4363.                         ],
  4364.                         'productIDs' => [],
  4365.                         'priceRange' => [
  4366.                             'min' => false,
  4367.                             'max' => false,
  4368.                         ],
  4369.                         'matchingVendor' => false,
  4370.                         'limit' => 4,
  4371.                     ],
  4372.                     [
  4373.                         // wired headphones
  4374.                         'productTypeIDs' => [
  4375.                             35,
  4376.                         ],
  4377.                         'productAttributeOptionIDs' => [
  4378.                             2020,
  4379.                         ],
  4380.                         'productIDs' => [],
  4381.                         'priceRange' => [
  4382.                             'min' => false,
  4383.                             'max' => false,
  4384.                         ],
  4385.                         'matchingVendor' => false,
  4386.                         'limit' => 4,
  4387.                     ],
  4388.                     [
  4389.                         // wireless headphones
  4390.                         'productTypeIDs' => [
  4391.                             35,
  4392.                         ],
  4393.                         'productAttributeOptionIDs' => [
  4394.                             2021,
  4395.                         ],
  4396.                         'productIDs' => [],
  4397.                         'priceRange' => [
  4398.                             'min' => false,
  4399.                             'max' => false,
  4400.                         ],
  4401.                         'matchingVendor' => false,
  4402.                         'limit' => 4,
  4403.                     ],
  4404.                     [
  4405.                         // wired headphones
  4406.                         'productTypeIDs' => [
  4407.                             35,
  4408.                         ],
  4409.                         'productAttributeOptionIDs' => [
  4410.                             2020,
  4411.                         ],
  4412.                         'productIDs' => [],
  4413.                         'priceRange' => [
  4414.                             'min' => false,
  4415.                             'max' => false,
  4416.                         ],
  4417.                         'matchingVendor' => false,
  4418.                         'limit' => 4,
  4419.                     ],
  4420.                     [
  4421.                         // headset combo
  4422.                         'productTypeIDs' => [
  4423.                             35,
  4424.                         ],
  4425.                         'productAttributeOptionIDs' => [
  4426.                             2021,
  4427.                         ],
  4428.                         'productIDs' => [],
  4429.                         'priceRange' => [
  4430.                             'min' => false,
  4431.                             'max' => false,
  4432.                         ],
  4433.                         'matchingVendor' => false,
  4434.                         'limit' => 2,
  4435.                     ],
  4436.                     [
  4437.                         // webcam
  4438.                         'productTypeIDs' => [
  4439.                             104,
  4440.                         ],
  4441.                         'productAttributeOptionIDs' => [],
  4442.                         'productIDs' => [],
  4443.                         'priceRange' => [
  4444.                             'min' => false,
  4445.                             'max' => false,
  4446.                         ],
  4447.                         'matchingVendor' => false,
  4448.                         'limit' => 2,
  4449.                     ],
  4450.                     [
  4451.                         // Speaker
  4452.                         'productTypeIDs' => [
  4453.                             84,
  4454.                         ],
  4455.                         'productAttributeOptionIDs' => [],
  4456.                         'productIDs' => [],
  4457.                         'priceRange' => [
  4458.                             'min' => false,
  4459.                             'max' => false,
  4460.                         ],
  4461.                         'matchingVendor' => false,
  4462.                         'limit' => 2,
  4463.                     ],
  4464.                     [
  4465.                         // External HDD adapter
  4466.                         'productTypeIDs' => [
  4467.                             124,
  4468.                         ],
  4469.                         'productAttributeOptionIDs' => [],
  4470.                         'productIDs' => [],
  4471.                         'priceRange' => [
  4472.                             'min' => false,
  4473.                             'max' => false,
  4474.                         ],
  4475.                         'matchingVendor' => false,
  4476.                         'limit' => 2,
  4477.                     ],
  4478.                     [
  4479.                         // Furbify mouse pad
  4480.                         'productTypeIDs' => [],
  4481.                         'productAttributeOptionIDs' => [],
  4482.                         'productIDs' => [
  4483.                             14731,
  4484.                         ],
  4485.                         'priceRange' => [
  4486.                             'min' => false,
  4487.                             'max' => false,
  4488.                         ],
  4489.                         'matchingVendor' => false,
  4490.                         'limit' => 0,
  4491.                     ],
  4492.                     [
  4493.                         // USB wifi
  4494.                         'productTypeIDs' => [
  4495.                             102,
  4496.                         ],
  4497.                         'productAttributeOptionIDs' => [],
  4498.                         'productIDs' => [],
  4499.                         'priceRange' => [
  4500.                             'min' => false,
  4501.                             'max' => false,
  4502.                         ],
  4503.                         'matchingVendor' => false,
  4504.                         'limit' => 2,
  4505.                     ],
  4506.                     [
  4507.                         // Compressed AIR
  4508.                         'productTypeIDs' => [],
  4509.                         'productAttributeOptionIDs' => [],
  4510.                         'productIDs' => [
  4511.                             15198,
  4512.                         ],
  4513.                         'priceRange' => [
  4514.                             'min' => false,
  4515.                             'max' => false,
  4516.                         ],
  4517.                         'matchingVendor' => false,
  4518.                         'limit' => 2,
  4519.                     ],
  4520.                 ];
  4521.                 break;
  4522.             case 52: // Notebook
  4523.                 $recommendedProductTypes = [
  4524.                     [
  4525.                         // basic wired mouse
  4526.                         'productTypeIDs' => [
  4527.                             46,
  4528.                         ],
  4529.                         'productAttributeOptionIDs' => [
  4530.                             2020,
  4531.                         ],
  4532.                         'productIDs' => [],
  4533.                         'priceRange' => [
  4534.                             'min' => false,
  4535.                             'max' => false,
  4536.                         ],
  4537.                         'matchingVendor' => false,
  4538.                         'limit' => 4,
  4539.                     ],
  4540.                     [
  4541.                         // basic wireless mouse
  4542.                         'productTypeIDs' => [
  4543.                             46,
  4544.                         ],
  4545.                         'productAttributeOptionIDs' => [
  4546.                             2021,
  4547.                         ],
  4548.                         'productIDs' => [],
  4549.                         'priceRange' => [
  4550.                             'min' => false,
  4551.                             'max' => false,
  4552.                         ],
  4553.                         'matchingVendor' => false,
  4554.                         'limit' => 4,
  4555.                     ],
  4556.                     [
  4557.                         // middle wired mouse
  4558.                         'productTypeIDs' => [
  4559.                             46,
  4560.                         ],
  4561.                         'productAttributeOptionIDs' => [
  4562.                             2020,
  4563.                         ],
  4564.                         'productIDs' => [],
  4565.                         'priceRange' => [
  4566.                             'min' => false,
  4567.                             'max' => false,
  4568.                         ],
  4569.                         'matchingVendor' => false,
  4570.                         'limit' => 4,
  4571.                     ],
  4572.                     [
  4573.                         // middle wireless mouse
  4574.                         'productTypeIDs' => [
  4575.                             46,
  4576.                         ],
  4577.                         'productAttributeOptionIDs' => [
  4578.                             2021,
  4579.                         ],
  4580.                         'productIDs' => [],
  4581.                         'priceRange' => [
  4582.                             'min' => false,
  4583.                             'max' => false,
  4584.                         ],
  4585.                         'matchingVendor' => false,
  4586.                         'limit' => 4,
  4587.                     ],
  4588.                     [
  4589.                         // wireless keyboard
  4590.                         'productTypeIDs' => [
  4591.                             38,
  4592.                         ],
  4593.                         'productAttributeOptionIDs' => [
  4594.                             2021,
  4595.                         ],
  4596.                         'productIDs' => [],
  4597.                         'priceRange' => [
  4598.                             'min' => false,
  4599.                             'max' => false,
  4600.                         ],
  4601.                         'matchingVendor' => false,
  4602.                         'limit' => 4,
  4603.                     ],
  4604.                     [
  4605.                         // wireless headphones
  4606.                         'productTypeIDs' => [
  4607.                             35,
  4608.                         ],
  4609.                         'productAttributeOptionIDs' => [
  4610.                             2021,
  4611.                         ],
  4612.                         'productIDs' => [],
  4613.                         'priceRange' => [
  4614.                             'min' => false,
  4615.                             'max' => false,
  4616.                         ],
  4617.                         'matchingVendor' => false,
  4618.                         'limit' => 4,
  4619.                     ],
  4620.                     [
  4621.                         // wired headphones
  4622.                         'productTypeIDs' => [
  4623.                             35,
  4624.                         ],
  4625.                         'productAttributeOptionIDs' => [
  4626.                             2020,
  4627.                         ],
  4628.                         'productIDs' => [],
  4629.                         'priceRange' => [
  4630.                             'min' => false,
  4631.                             'max' => false,
  4632.                         ],
  4633.                         'matchingVendor' => false,
  4634.                         'limit' => 4,
  4635.                     ],
  4636.                     [
  4637.                         // wireless headphones
  4638.                         'productTypeIDs' => [
  4639.                             35,
  4640.                         ],
  4641.                         'productAttributeOptionIDs' => [
  4642.                             2021,
  4643.                         ],
  4644.                         'productIDs' => [],
  4645.                         'priceRange' => [
  4646.                             'min' => false,
  4647.                             'max' => false,
  4648.                         ],
  4649.                         'matchingVendor' => false,
  4650.                         'limit' => 4,
  4651.                     ],
  4652.                     [
  4653.                         // wired headphones
  4654.                         'productTypeIDs' => [
  4655.                             35,
  4656.                         ],
  4657.                         'productAttributeOptionIDs' => [
  4658.                             2020,
  4659.                         ],
  4660.                         'productIDs' => [],
  4661.                         'priceRange' => [
  4662.                             'min' => false,
  4663.                             'max' => false,
  4664.                         ],
  4665.                         'matchingVendor' => false,
  4666.                         'limit' => 4,
  4667.                     ],
  4668.                     [
  4669.                         // headset combo
  4670.                         'productTypeIDs' => [
  4671.                             35,
  4672.                         ],
  4673.                         'productAttributeOptionIDs' => [
  4674.                             2021,
  4675.                         ],
  4676.                         'productIDs' => [],
  4677.                         'priceRange' => [
  4678.                             'min' => false,
  4679.                             'max' => false,
  4680.                         ],
  4681.                         'matchingVendor' => false,
  4682.                         'limit' => 2,
  4683.                     ],
  4684.                     [
  4685.                         // webcam
  4686.                         'productTypeIDs' => [
  4687.                             104,
  4688.                         ],
  4689.                         'productAttributeOptionIDs' => [],
  4690.                         'productIDs' => [],
  4691.                         'priceRange' => [
  4692.                             'min' => false,
  4693.                             'max' => false,
  4694.                         ],
  4695.                         'matchingVendor' => false,
  4696.                         'limit' => 2,
  4697.                     ],
  4698.                     [
  4699.                         // HDMI cable
  4700.                         'productTypeIDs' => [
  4701.                             7,
  4702.                         ],
  4703.                         'productAttributeOptionIDs' => [],
  4704.                         'productIDs' => [],
  4705.                         'priceRange' => [
  4706.                             'min' => false,
  4707.                             'max' => false,
  4708.                         ],
  4709.                         'matchingVendor' => false,
  4710.                         'limit' => 2,
  4711.                     ],
  4712.                     [
  4713.                         // Notebook cooler
  4714.                         'productTypeIDs' => [
  4715.                             126,
  4716.                         ],
  4717.                         'productAttributeOptionIDs' => [],
  4718.                         'productIDs' => [],
  4719.                         'priceRange' => [
  4720.                             'min' => false,
  4721.                             'max' => false,
  4722.                         ],
  4723.                         'matchingVendor' => false,
  4724.                         'limit' => 2,
  4725.                     ],
  4726.                     [
  4727.                         // Notebook bag
  4728.                         'productTypeIDs' => [
  4729.                             54,
  4730.                         ],
  4731.                         'productAttributeOptionIDs' => [],
  4732.                         'productIDs' => [],
  4733.                         'priceRange' => [
  4734.                             'min' => false,
  4735.                             'max' => false,
  4736.                         ],
  4737.                         'matchingVendor' => false,
  4738.                         'limit' => 2,
  4739.                     ],
  4740.                     [
  4741.                         // External HDD adapter
  4742.                         'productTypeIDs' => [
  4743.                             124,
  4744.                         ],
  4745.                         'productAttributeOptionIDs' => [],
  4746.                         'productIDs' => [],
  4747.                         'priceRange' => [
  4748.                             'min' => false,
  4749.                             'max' => false,
  4750.                         ],
  4751.                         'matchingVendor' => false,
  4752.                         'limit' => 2,
  4753.                     ],
  4754.                     [
  4755.                         // Furbify mouse pad
  4756.                         'productTypeIDs' => [],
  4757.                         'productAttributeOptionIDs' => [],
  4758.                         'productIDs' => [
  4759.                             14731,
  4760.                         ],
  4761.                         'priceRange' => [
  4762.                             'min' => false,
  4763.                             'max' => false,
  4764.                         ],
  4765.                         'matchingVendor' => false,
  4766.                         'limit' => 2,
  4767.                     ],
  4768.                     [
  4769.                         // Compressed AIR
  4770.                         'productTypeIDs' => [],
  4771.                         'productAttributeOptionIDs' => [],
  4772.                         'productIDs' => [
  4773.                             15198,
  4774.                         ],
  4775.                         'priceRange' => [
  4776.                             'min' => false,
  4777.                             'max' => false,
  4778.                         ],
  4779.                         'matchingVendor' => false,
  4780.                         'limit' => 2,
  4781.                     ],
  4782.                 ];
  4783.                 break;
  4784.             case 44: // Monitor
  4785.                 $recommendedProductTypes = [
  4786.                     [
  4787.                         // monitor cleaning set
  4788.                         'productTypeIDs' => [
  4789.                             20,
  4790.                         ],
  4791.                         'productAttributeOptionIDs' => [],
  4792.                         'productIDs' => [],
  4793.                         'priceRange' => [
  4794.                             'min' => false,
  4795.                             'max' => false,
  4796.                         ],
  4797.                         'matchingVendor' => false,
  4798.                         'limit' => 4,
  4799.                     ],
  4800.                     [
  4801.                         // Furbify mouse pad
  4802.                         'productTypeIDs' => [],
  4803.                         'productAttributeOptionIDs' => [],
  4804.                         'productIDs' => [
  4805.                             14731,
  4806.                         ],
  4807.                         'priceRange' => [
  4808.                             'min' => false,
  4809.                             'max' => false,
  4810.                         ],
  4811.                         'matchingVendor' => false,
  4812.                         'limit' => 4,
  4813.                     ],
  4814.                     [
  4815.                         // hdmi cable
  4816.                         'productTypeIDs' => [
  4817.                             7,
  4818.                         ],
  4819.                         'productAttributeOptionIDs' => [],
  4820.                         'productIDs' => [],
  4821.                         'priceRange' => [
  4822.                             'min' => false,
  4823.                             'max' => false,
  4824.                         ],
  4825.                         'matchingVendor' => false,
  4826.                         'limit' => 4,
  4827.                     ],
  4828.                     [
  4829.                         // Speakers
  4830.                         'productTypeIDs' => [
  4831.                             84,
  4832.                         ],
  4833.                         'productAttributeOptionIDs' => [],
  4834.                         'productIDs' => [],
  4835.                         'priceRange' => [
  4836.                             'min' => false,
  4837.                             'max' => false,
  4838.                         ],
  4839.                         'matchingVendor' => false,
  4840.                         'limit' => 8,
  4841.                     ],
  4842.                     [
  4843.                         // headphones
  4844.                         'productTypeIDs' => [
  4845.                             35,
  4846.                         ],
  4847.                         'productAttributeOptionIDs' => [],
  4848.                         'productIDs' => [],
  4849.                         'priceRange' => [
  4850.                             'min' => false,
  4851.                             'max' => false,
  4852.                         ],
  4853.                         'matchingVendor' => false,
  4854.                         'limit' => 8,
  4855.                     ],
  4856.                 ];
  4857.                 break;
  4858.             case 41: // Mobile
  4859.                 $recommendedProductTypes = [
  4860.                     [
  4861.                         // Accessories
  4862.                         'productTypeIDs' => [42],
  4863.                         'productAttributeOptionIDs' => [],
  4864.                         'productIDs' => [],
  4865.                         'priceRange' => [
  4866.                             'min' => false,
  4867.                             'max' => false,
  4868.                         ],
  4869.                         'matchingVendor' => false,
  4870.                         'limit' => 8,
  4871.                     ],
  4872.                 ];
  4873.                 break;
  4874.             default:
  4875.                 $recommendedProductTypes = [];
  4876.         }
  4877.         return $recommendedProductTypes;
  4878.     }
  4879.     public static function getRecommendedProductTypesStructure($domainId, $productTypeId)
  4880.     {
  4881.         $recommendedProductTypes = [];
  4882.         if (in_array($domainId, [1, 2])) { // RT SK, WS SK
  4883.             switch ($productTypeId) {
  4884.                 case 60: // PC
  4885.                 case 107: // PC SET
  4886.                 case 116: // All in one
  4887.                     $recommendedProductTypes = [
  4888.                         30, // Mouse
  4889.                         31, // Keyboard
  4890.                         198, // Headphones
  4891.                         194, // Webcam
  4892.                         201, // Speaker
  4893.                         202, // External HDD adapter
  4894.                         10, // USB wifi
  4895.                         203, // Cleaner
  4896.                     ];
  4897.                     break;
  4898.                 case 52: // Notebook
  4899.                     $recommendedProductTypes = [
  4900.                         30, // Mouse
  4901.                         31, // Keyboard
  4902.                         198, // Headphones
  4903.                         194, // Webcam
  4904.                         9, // Docking station
  4905.                         199, // HDMI cable
  4906.                         200, // Notebook cooler
  4907.                         98, // Notebook bag
  4908.                     ];
  4909.                     break;
  4910.                 case 44: // Monitor
  4911.                     $recommendedProductTypes = [
  4912.                         203, // Cleaner
  4913.                         199, // HDMI cable
  4914.                         201, // Speaker
  4915.                         198, // Headphones
  4916.                     ];
  4917.                     break;
  4918.                 default:
  4919.                     $recommendedProductTypes = [];
  4920.             }
  4921.         } elseif (in_array($domainId, [3, 4])) { // RT HU, WS HU
  4922.             switch ($productTypeId) {
  4923.                 case 60: // PC
  4924.                 case 107: // PC SET
  4925.                 case 116: // All in one
  4926.                     $recommendedProductTypes = [
  4927.                         68, // Mouse
  4928.                         69, // Keyboard
  4929.                         204, // Headphones
  4930.                         195, // Webcam
  4931.                         205, // Speaker
  4932.                         206, // External HDD adapter
  4933.                         47, // USB wifi
  4934.                         207, // Cleaner
  4935.                     ];
  4936.                     break;
  4937.                 case 52: // Notebook
  4938.                     $recommendedProductTypes = [
  4939.                         68, // Mouse
  4940.                         69, // Keyboard
  4941.                         204, // Headphones
  4942.                         195, // Webcam
  4943.                         66, // Docking station
  4944.                         208, // HDMI cable
  4945.                         209, // Notebook cooler
  4946.                         99, // Notebook bag
  4947.                     ];
  4948.                     break;
  4949.                 case 44: // Monitor
  4950.                     $recommendedProductTypes = [
  4951.                         207, // Cleaner
  4952.                         208, // HDMI cable
  4953.                         205, // Speaker
  4954.                         204, // Headphones
  4955.                     ];
  4956.                     break;
  4957.                 default:
  4958.                     $recommendedProductTypes = [];
  4959.             }
  4960.         } elseif (in_array($domainId, [5, 9])) { // RT CZ, WS CZ
  4961.             switch ($productTypeId) {
  4962.                 case 60: // PC
  4963.                 case 107: // PC SET
  4964.                 case 116: // All in one
  4965.                     $recommendedProductTypes = [
  4966.                         142, // Mouse
  4967.                         143, // Keyboard
  4968.                         210, // Headphones
  4969.                         196, // Webcam
  4970.                         211, // Speaker
  4971.                         212, // External HDD adapter
  4972.                         121, // USB wifi
  4973.                         213, // Cleaner
  4974.                     ];
  4975.                     break;
  4976.                 case 52: // Notebook
  4977.                     $recommendedProductTypes = [
  4978.                         142, // Mouse
  4979.                         143, // Keyboard
  4980.                         210, // Headphones
  4981.                         196, // Webcam
  4982.                         120, // Docking station
  4983.                         214, // HDMI cable
  4984.                         215, // Notebook cooler
  4985.                         165, // Notebook bag
  4986.                     ];
  4987.                     break;
  4988.                 case 44: // Monitor
  4989.                     $recommendedProductTypes = [
  4990.                         213, // Cleaner
  4991.                         214, // HDMI cable
  4992.                         211, // Speaker
  4993.                         210, // Headphones
  4994.                     ];
  4995.                     break;
  4996.                 default:
  4997.                     $recommendedProductTypes = [];
  4998.             }
  4999.         }
  5000.         return $recommendedProductTypes;
  5001.     }
  5002.     /**
  5003.      * @param int $categoryId
  5004.      */
  5005.     public function countInCategory(array $filter)
  5006.     {
  5007.         $query = $this->getProductListingQuery($filter);
  5008.         $result = $query->getScalarResult();
  5009.         if (!empty($result)) {
  5010.             return count($result);
  5011.         } else {
  5012.             return 0;
  5013.         }
  5014.     }
  5015.     /**
  5016.      * @param int $domainId
  5017.      * @return array
  5018.      */
  5019.     public static function getFreeShipLimitsForDomain(int $domainId): array
  5020.     {
  5021.         return self::getFreeShipLimits()[$domainId] ?? [
  5022.             'freeShipLimit' => 0,
  5023.         ];
  5024.     }
  5025.     /**
  5026.      * @return int[][]
  5027.      */
  5028.     public static function getFreeShipLimits(): array
  5029.     {
  5030.         return [
  5031.             Domain::DOMAIN_ID_RT_SK => [
  5032.                 'freeShipLimit' => 50,
  5033.             ],
  5034.             Domain::DOMAIN_ID_WS_SK => [
  5035.                 'freeShipLimit' => 50,
  5036.             ],
  5037.             Domain::DOMAIN_ID_RT_HU => [
  5038.                 'freeShipLimit' => 18000,
  5039.             ],
  5040.             Domain::DOMAIN_ID_WS_HU => [
  5041.                 'freeShipLimit' => 200000,
  5042.             ],
  5043.             Domain::DOMAIN_ID_WS_CZ => [
  5044.                 'freeShipLimit' => 0,
  5045.             ],
  5046.             Domain::DOMAIN_ID_WS_EU => [
  5047.                 'freeShipLimit' => 0,
  5048.             ],
  5049.             Domain::DOMAIN_ID_RT_CZ => [
  5050.                 'freeShipLimit' => 1400,
  5051.             ],
  5052.             Domain::DOMAIN_ID_RT_EU => [
  5053.                 // RT EU
  5054.                 'freeShipLimit' => 0,
  5055.             ],
  5056.         ];
  5057.     }
  5058.     /**
  5059.      * @param int $domainId
  5060.      * @param string $locale
  5061.      * @param int $productId
  5062.      * @return string
  5063.      */
  5064.     public static function getProductCacheKey(int $domainId, string $locale, int $productId): string
  5065.     {
  5066.         return 'product.v29.' . $domainId . '.' . $locale . '.' . $productId;
  5067.     }
  5068.     /**
  5069.      * @param int $domainId
  5070.      * @param string $locale
  5071.      * @param int $productId
  5072.      * @return string
  5073.      */
  5074.     public static function getSimiliarProductsCacheKey(int $domainId, string $locale, int $productId): string
  5075.     {
  5076.         return 'product.v3.similiar.' . $productId . '.' . $domainId . '.' . $locale;
  5077.     }
  5078.     /**
  5079.      * @param int $domainId
  5080.      * @param string $locale
  5081.      * @param int $productId
  5082.      * @return string
  5083.      */
  5084.     public static function getRecommendedProductsCacheKey(int $domainId, string $locale, int $productId): string
  5085.     {
  5086.         return 'product.v4.recommended.' . $productId . '.' . $domainId . '.' . $locale;
  5087.     }
  5088.     /**
  5089.      * @param int $productId
  5090.      * @return array
  5091.      */
  5092.     public function getNotifications(int $productId): array
  5093.     {
  5094.         $customerNotifications = $this->entityManager
  5095.             ->createQuery("SELECT cn
  5096.                 FROM AtechNetCoreBundle:CustomerNotification cn 
  5097.                 WHERE cn.product = :product AND cn.domain = :domain")
  5098.             ->setParameters([
  5099.                 'product' => $productId,
  5100.                 'domain' => $this->priceDomainId,
  5101.             ])
  5102.             ->getResult();
  5103.         if ($customerNotifications) {
  5104.             return $customerNotifications;
  5105.         }
  5106.         return [];
  5107.     }
  5108.     /**
  5109.      * @param int $productId
  5110.      * @param Customer|null $customer
  5111.      * @param string|null $visitor
  5112.      * @return bool
  5113.      */
  5114.     public function isPriceMonitorActivated(int $productId, ?Customer $customer, ?string $visitor): bool
  5115.     {
  5116.         $notifications = $this->getNotifications($productId);
  5117.         if (!is_null($customer)) {
  5118.             foreach ($notifications as $notification) {
  5119.                 if (!is_null($notification->getCustomer()) && $notification->getCustomer()->getId() == $customer->getId()) {
  5120.                     return true;
  5121.                 }
  5122.             }
  5123.         } elseif (!is_null($visitor)) {
  5124.             foreach ($notifications as $notification) {
  5125.                 if ($notification->getVisitor() == $visitor) {
  5126.                     return true;
  5127.                 }
  5128.             }
  5129.         }
  5130.         return false;
  5131.     }
  5132.     /**
  5133.      * @param object|string $photo
  5134.      * @param string $directory
  5135.      * @return Image
  5136.      */
  5137.     protected function getImage($photo, string $directory = 'product'): Image
  5138.     {
  5139.         return new Image($this->cloudFlareApiConfig, $photo, $directory);
  5140.     }
  5141.     /**
  5142.      * @param int $albumId
  5143.      * @return bool
  5144.      */
  5145.     public function getAlbumImages(int $albumId): array
  5146.     {
  5147.         $album = $this->entityManager->getRepository(Album::class)->findOneById($albumId);
  5148.         $images = [];
  5149.         if (count($album->getPhotos()) > 0) {
  5150.             foreach ($album->getPhotos() as $image) {
  5151.                 $imgs = [
  5152.                     'original' => $image->getCdnUrl('ProductViewPop'),
  5153.                 ];
  5154.                 $images[] = $imgs;
  5155.             }
  5156.         }
  5157.         return $images;
  5158.     }
  5159.     /**
  5160.      * @param array $productData
  5161.      * @return bool
  5162.      */
  5163.     public function getOSLabel(array $productData): ?array
  5164.     {
  5165.         $operationSystem = array_key_exists(Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM, $productData['attributes']) ? $productData['attributes'][Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM]['options'][array_key_first($productData['attributes'][Attribute::ATTRIBUTE_ID_OPERATING_SYSTEM]['options'])]['title'] : null;
  5166.         $filename = null;
  5167.         $productTypeId = array_key_exists('typeId', $productData) ? $productData['typeId'] : $productData['productTypeId'];
  5168.         if (str_contains(strtolower($operationSystem), 'chrome')) {
  5169.             $filename = 'chrome';
  5170.         } elseif (str_contains(strtolower($operationSystem), 'ios') || (array_key_exists('vendorId', $productData) && $productData['vendorId'] == Vendor::VENDOR_ID_APPLE)) {
  5171.             if (in_array($productTypeId, [
  5172.                 ProductType::PRODUCT_TYPE_ID_NOTEBOOK,
  5173.                 ProductType::PRODUCT_TYPE_ID_PC,
  5174.                 ProductType::PRODUCT_TYPE_ID_PC_SET,
  5175.                 ProductType::PRODUCT_TYPE_ID_ALL_IN_ONE,
  5176.             ])) {
  5177.                 $filename = 'apple';
  5178.             } elseif ($productTypeId == ProductType::PRODUCT_TYPE_ID_SMARTPHONE) {
  5179.                 $filename = 'ios';
  5180.             } elseif ($productTypeId == ProductType::PRODUCT_TYPE_ID_TABLET) {
  5181.                 $filename = 'ipados';
  5182.             }
  5183.         } elseif (str_contains(strtolower($operationSystem), 'android')) {
  5184.             $filename = 'android';
  5185.         } elseif (
  5186.             str_contains(strtolower($operationSystem), 'win') ||
  5187.             (
  5188.                 (
  5189.                     in_array($productTypeId, [
  5190.                         ProductType::PRODUCT_TYPE_ID_NOTEBOOK,
  5191.                         ProductType::PRODUCT_TYPE_ID_PC,
  5192.                         ProductType::PRODUCT_TYPE_ID_PC_SET,
  5193.                         ProductType::PRODUCT_TYPE_ID_ALL_IN_ONE,
  5194.                     ])
  5195.                 ) &&
  5196.                 $productData['vendorId'] != Vendor::VENDOR_ID_APPLE
  5197.             )
  5198.         ) {
  5199.             $labels = isset($productData['badges']) ? $productData['badges'] : $productData['labels'];
  5200.             $filename = 'windows' . (in_array('Windows 11', $labels) ? '_11' : '');
  5201.         }
  5202.         return [
  5203.             'filename' => $filename,
  5204.             'title' => $operationSystem,
  5205.         ];
  5206.     }
  5207.     /**
  5208.      * @param string $locale
  5209.      * @param string $webshopPhoneNumber
  5210.      * @param array $salesManager
  5211.      * @return array
  5212.      */
  5213.     public function getSalesManagerForProductShow(string $locale, string $webshopPhoneNumber, array $salesManager): array
  5214.     {
  5215.         $position = '';
  5216.         $phoneNumber = $webshopPhoneNumber;
  5217.         switch ($locale) {
  5218.             case 'en':
  5219.                 $photoFilename = 'mária-face.png';
  5220.                 $name = 'Mária';
  5221.                 $position = 'sales specialist';
  5222.                 break;
  5223.             case 'cz':
  5224.             case 'sk':
  5225.                 $photoFilename = 'mária-face.png';
  5226.                 $name = 'Mária';
  5227.                 break;
  5228.             default:
  5229.                 $photoFilename = 'mária-face.png';
  5230.                 $name = 'Mária';
  5231.                 break;
  5232.         }
  5233.         $customerTypeId = $this->multiStore->getCustomerTypeId();
  5234.         if (isset($salesManager['id']) && in_array($salesManager['id'], [Customer::SALES_MANAGER_WS_SK1_ID, Customer::SALES_MANAGER_NIKI_ID])) {
  5235.             $photoFilename = 'kati-face.png';
  5236.             $name = 'Kati';
  5237.             $phoneNumber = '+421 220 512 060';
  5238.         } else {
  5239.             if ($customerTypeId == MultiStore::CUSTOMER_TYPE_WHOLESALE && !is_null($salesManager) && isset($salesManager['username']) && isset($salesManager['name'])) {
  5240.                 if (in_array($salesManager['id'], [Customer::SALES_MANAGER_DENISA_ID, 296])) {
  5241.                     $photoFilename = "";
  5242.                 } else {
  5243.                     $photoFilename = strtolower($salesManager['name']) . '-face.png';
  5244.                 }
  5245.                 $name = $salesManager['name'];
  5246.                 $position = 'sales manager';
  5247.                 if ($salesManager['phone'] != '' && $salesManager['phone'] != null && $locale != 'hu') {
  5248.                     $phoneNumber = $salesManager['phone'];
  5249.                 } elseif ($locale == 'hu') {
  5250.                     $phoneNumber = '+36 17 65 46 57';
  5251.                 } elseif ($locale == 'sk') {
  5252.                     $phoneNumber = '+421 220 512 060';
  5253.                 }
  5254.             }
  5255.         }
  5256.         return [
  5257.             'photoFilename' => $photoFilename,
  5258.             'name' => $name,
  5259.             'position' => $position,
  5260.             'phoneNumber' => $phoneNumber,
  5261.         ];
  5262.     }
  5263.     public function getFullSpecificationList(array $data, $translator, $reviewUrl): array
  5264.     {
  5265.         $specificationList = [
  5266.             [
  5267.                 'title' => 'Brand',
  5268.                 'content' => $data['categoryLinkWithBrandFilter'] ? "<a href=\"{$data['categoryLinkWithBrandFilter']}\" class=\"link-bold\" target=\"_blank\">{$data['brand']}</a>" : $data['brand'],
  5269.             ],
  5270.             [
  5271.                 'title' => 'Model',
  5272.                 'content' => $data['productName'],
  5273.             ],
  5274.             [
  5275.                 'title' => 'Product code',
  5276.                 'content' => $data['model'] . ($data['partNumber'] && $data['partNumber'] != '' ? "<small class=\"text-secondary\">({$data['partNumber']})</small>" : ''),
  5277.             ],
  5278.             [
  5279.                 'title' => 'Availability',
  5280.                 'content' => $this->getStockStatus($data, $translator),
  5281.             ],
  5282.             [
  5283.                 'title' => 'Quality grading',
  5284.                 'content' => $data['domainId'] == Domain::DOMAIN_ID_WS_EU ? "<strong>{$translator->trans($data['quality'])}</strong>" : $this->getQualityGradingContent($data, $translator, $reviewUrl),
  5285.             ],
  5286.         ];
  5287.         if (count($data['containerParts']) > 0) {
  5288.             return $specificationList;
  5289.         }
  5290.         foreach ($data['attributes'] as $attribute) {
  5291.             if ($attribute == null || !array_key_exists('title', $attribute)) {
  5292.                 continue;
  5293.             }
  5294.             if (in_array($attribute['id'], [Attribute::ATTRIBUTE_ID_RAM_PC_TYPE, Attribute::ATTRIBUTE_ID_RAM_NB_TYPE, Attribute::ATTRIBUTE_ID_RAM_SIZE])) {
  5295.                 continue;
  5296.             }
  5297.             $isAttributeTypeEqualsStorage = in_array($attribute['id'], $data['storageAttributeIds']);
  5298.             $title = $isAttributeTypeEqualsStorage ? 'Storage' : $attribute['title'];
  5299.             $content = '';
  5300.             if ($attribute['options']) {
  5301.                 foreach ($attribute['options'] as $option) {
  5302.                     $content .= '<span class="fw-bold">';
  5303.                     $content .= ($option['multiplier'] > 0 ? "{$option['multiplier']} x " : '') . $option['title'];
  5304.                     if ($isAttributeTypeEqualsStorage) {
  5305.                         $content .= " {$attribute['title']}";
  5306.                     }
  5307.                     $content .= '</span><br>';
  5308.                 }
  5309.             }
  5310.             $specificationList[] = [
  5311.                 'title' => $title,
  5312.                 'content' => $content,
  5313.             ];
  5314.         }
  5315.         if ($data['weight']) {
  5316.             $specificationList[] = [
  5317.                 'title' => 'Weight',
  5318.                 'content' => $data['weight'],
  5319.             ];
  5320.         }
  5321.         if ($data['dimensions']) {
  5322.             $specificationList[] = [
  5323.                 'title' => 'Dimensions',
  5324.                 'content' => $data['dimensions'],
  5325.             ];
  5326.         }
  5327.         if (
  5328.             in_array($data['typeId'], [
  5329.                 ProductType::PRODUCT_TYPE_ID_NOTEBOOK,
  5330.                 ProductType::PRODUCT_TYPE_ID_PC,
  5331.                 ProductType::PRODUCT_TYPE_ID_MONITOR,
  5332.                 ProductType::PRODUCT_TYPE_ID_SMARTPHONE,
  5333.                 ProductType::PRODUCT_TYPE_ID_TABLET,
  5334.                 ProductType::PRODUCT_TYPE_ID_ALL_IN_ONE,
  5335.                 ProductType::PRODUCT_TYPE_ID_PROJECTOR,
  5336.             ])) {
  5337.             $specificationList[] = [
  5338.                 'title'   => 'Disclaimer',
  5339.                 'content' => $this->getDisclaimer($translator, $data['typeId']),
  5340.             ];
  5341.         }
  5342.         return $specificationList;
  5343.     }
  5344.     public function getSSDSize(array $data): ?string
  5345.     {
  5346.         $ssdSize = null;
  5347.         foreach ($data as $attribute) {
  5348.             if ($attribute && $attribute['title'] === 'SSD' && !empty($attribute['options'])) {
  5349.                 foreach ($attribute['options'] as $option) {
  5350.                     // Example: "1TB (M.2)" or "512GB"
  5351.                     if (preg_match('/(\d+(?:\.\d+)?)\s*(TB|GB)/i', $option['title'], $matches)) {
  5352.                         $ssdSize = $matches[1] . strtoupper($matches[2]);
  5353.                         break 2;
  5354.                     }
  5355.                 }
  5356.             }
  5357.         }
  5358.         return $ssdSize;
  5359.     }
  5360.     private function getQualityGradingContent($data, $translator, $reviewUrl)
  5361.     {
  5362.         $quality = match ($data['quality']) {
  5363.             'Gold'   => 'A',
  5364.             'Silver' => 'B',
  5365.             'Bronze' => 'C',
  5366.             default  => $data['quality'],
  5367.         };
  5368.         $quality_description = $data['quality_description'];
  5369.         $elements = "<strong>{$translator->trans("grades.$quality")}";
  5370.         if ($quality && $quality_description) {
  5371.             $elements .= ": ";
  5372.         }
  5373.         $elements .= "</strong>";
  5374.         if ($quality && $quality_description) {
  5375.             $elements .= "<span>{$quality_description} ";
  5376.         }
  5377.         $elements .= str_replace(
  5378.             ['{qualityTitle}', '{hrefValue}'],
  5379.             [
  5380.                 $translator->trans($quality),
  5381.                 "$reviewUrl?category={$data['product_type_id']}&quality={$data['qualityStatusId']}",
  5382.             ],
  5383.             $translator->trans('Customer reviews and photos of our {qualityTitle} rated products')
  5384.         );
  5385.         return $elements;
  5386.     }
  5387.     private function getStockStatus(array $data, $translator): string
  5388.     {
  5389.         $active = $data['active'];
  5390.         $available = $data['available'];
  5391.         $orderWaitingDays = $data['orderWaitingDays'];
  5392.         if (!$active) {
  5393.             return "<span class=\"in-stock text-grey\"><span class=\"dot\"></span>{$translator->trans('Out of stock')}</span>";
  5394.         }
  5395.         $classList = 'in-stock text-grey';
  5396.         $textContent = 'Out of stock';
  5397.         if ($orderWaitingDays > 0 && $available < 1) {
  5398.             $textContent = str_replace('%d', $orderWaitingDays, $translator->trans('On order within %d working days'));
  5399.             return "<span class=\"$classList\"><span class=\"dot\"></span>{$textContent}</span>";
  5400.         }
  5401.         if ($available > 1) {
  5402.             $classList = "in-stock text-success";
  5403.         }
  5404.         if ($available == 1) {
  5405.             return "<span class=\"in-stock text-danger\"><span class=\"dot\"></span>{$translator->trans('Only one left')}</span>";
  5406.         }
  5407.         if ($available > 10) {
  5408.             $textContent = '10+ in stock';
  5409.         } elseif ($available >= 5) {
  5410.             $textContent = '5-10 in stock';
  5411.         } elseif ($available > 1) {
  5412.             $textContent = '2-4 in stock';
  5413.         }
  5414.         return "<span class=\"$classList\"><span class=\"dot\"></span>{$translator->trans($textContent)}</span>";
  5415.     }
  5416.     private function getDisclaimer($translator, int $productType)
  5417.     {
  5418.         return $translator->trans("Disclaimer_note.$productType");
  5419.     }
  5420.     private function getTnDisplayAttributeOptionIds(): string
  5421.     {
  5422.         $attribute = $this->entityManager->find(Attribute::class, Attribute::ATTRIBUTE_ID_DISPLAY_TECHNOLOGY);
  5423.         if ($attribute) {
  5424.             $panelIds = '';
  5425.             foreach ($attribute->getOptions() as $option) {
  5426.                 if (!in_array($option->getId(), [
  5427.                     AttributeOption::ATTRIBUTE_OPTION_IPS_PANEL,
  5428.                     AttributeOption::ATTRIBUTE_OPTION_OLED_PANEL,
  5429.                     AttributeOption::ATTRIBUTE_OPTION_QLED_PANEL,
  5430.                 ])) {
  5431.                     $panelIds .= $option->getId() . ',';
  5432.                 }
  5433.             }
  5434.             return trim($panelIds, ',');
  5435.         }
  5436.         return '';
  5437.     }
  5438.     private function getAttributeOptionIdsBasedOnTheAge(int $age): string
  5439.     {
  5440.         $dateTime = new \DateTime();
  5441.         $currentYear = $dateTime->format('Y');
  5442.         $yearFrom = 0;
  5443.         $yearTo = 0;
  5444.         switch ($age) {
  5445.             case self::AGE_0_to_2_YEARS:
  5446.                 $yearFrom = $currentYear - 0; // to convert int
  5447.                 $yearTo = $currentYear - 2;
  5448.                 break;
  5449.             case self::AGE_3_to_5_YEARS:
  5450.                 $yearFrom = $currentYear - 3;
  5451.                 $yearTo = $currentYear - 5;
  5452.                 break;
  5453.             case self::AGE_6_PLUS_YEARS:
  5454.                 $yearFrom = $currentYear - 6;
  5455.                 $yearTo = 2005;
  5456.                 break;
  5457.         }
  5458.         if ($yearFrom == 0 && $yearTo == 0) {
  5459.             return '';
  5460.         }
  5461.         $yearOptionIds = '';
  5462.         for ($i = $yearFrom; $i >= $yearTo; $i--) {
  5463.             $yearOptionIds .= AttributeOption::getYearAttributeOptionIds()[$i] . ',';
  5464.         }
  5465.         return trim($yearOptionIds, ',');
  5466.     }
  5467.     private function getPriorityProductsForCategoryPages(array $filter, CategoryPage $categoryPage): array
  5468.     {
  5469.         /**
  5470.          * CHECK IF ANY FILTER IS APPLIED BY THE USER IF YES THEN DON`T SHOW THE PRIORITY PRODUCTS
  5471.          */
  5472.         if (!$this->isUserApplyingFilters($filter, $categoryPage)) {
  5473.             $priorityProducts = [];
  5474.             if ($categoryPage->getPriorityProducts()->count() > 0) {
  5475.                 $priorityProducts = [];
  5476.                 foreach ($categoryPage->getPriorityProducts() as $priorityProduct) {
  5477.                     if ($priorityProduct->getProduct()->isAvailable()) {
  5478.                         $priorityProducts[$priorityProduct->getSortIndex()] = $priorityProduct->getProduct();
  5479.                     }
  5480.                 }
  5481.                 ksort($priorityProducts);
  5482.                 $priorityProducts = array_slice($priorityProducts, 0, 12);
  5483.             }
  5484.             return $priorityProducts;
  5485.         }
  5486.         return [];
  5487.     }
  5488.     private function isUserApplyingFilters(array $filter, CategoryPage $categoryPage): bool
  5489.     {
  5490.         $sideFilters = true;
  5491.         $sortFilter = true;
  5492.         if ((array_key_exists('filterVendor', $filter) && is_null($filter['filterVendor']))
  5493.             && (is_null($filter['filterAttribute']) || count($filter['filterAttribute']) == 0)
  5494.             && is_null($filter['filterPriceMin'])
  5495.             && is_null($filter['filterPriceMax'])
  5496.             && is_null($filter['filterLastPieces'])
  5497.             && (is_null($filter['filterDiscounted']) || $categoryPage->isShowOnlyDiscounted() == $filter['filterDiscounted'])
  5498.             && is_null($filter['filterCpuMarkMin'])
  5499.             && is_null($filter['filterCpuMarkMax'])
  5500.             && is_null($filter['filterProductType'])
  5501.             && (array_key_exists('start', $filter) && $filter['start'] == 0)
  5502.         ) {
  5503.             $sideFilters = false;
  5504.         }
  5505.         if ($filter['sort'] == 'popular') {
  5506.             $sortFilter = false;
  5507.         }
  5508.         return $sideFilters || $sortFilter;
  5509.     }
  5510.     private function getProductMetaText(?string $rawText, array $attributes): ?string
  5511.     {
  5512.         if (!$rawText) {
  5513.             return null;
  5514.         }
  5515.         preg_match_all('/\[\[(.*?)\]\]/', $rawText, $matches);
  5516.         foreach ($matches[1] as $i => $match) {
  5517.             $translatedTitle = $this->translator->trans($match);
  5518.             $result = array_filter($attributes, function ($item) use ($translatedTitle) {
  5519.                 return isset($item['title']) && $item['title'] === $translatedTitle;
  5520.             });
  5521.             $shortTitle = "";
  5522.             foreach ($result as $item) {
  5523.                 if (isset($item['options']) && is_array($item['options'])) {
  5524.                     foreach ($item['options'] as $option) {
  5525.                         if (isset($option['title'])) {
  5526.                             $shortTitle .= $option['title'] . ", ";
  5527.                         }
  5528.                     }
  5529.                 }
  5530.             }
  5531.             $rawText = str_replace($matches[0][$i], rtrim($shortTitle, ', '), $rawText);
  5532.         }
  5533.         return $rawText;
  5534.     }
  5535. }