custom/plugins/ShopWithMeUI/src/Service/HomePageService.php line 320

Open in your IDE?
  1. <?php
  2. namespace ShopWithMe\UI\Service;
  3. use Econsor\Shopware\SearchBar\Storefront\Controller\EconsorTagSaverController;
  4. use PhpParser\Node\Expr\AssignOp\Mul;
  5. use Shopware\Core\Checkout\Customer\CustomerEntity;
  6. use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStates;
  7. use Shopware\Core\Checkout\Order\OrderEntity;
  8. use Shopware\Core\Content\Media\Aggregate\MediaFolder\MediaFolderEntity;
  9. use Shopware\Core\Content\Media\File\FileSaver;
  10. use Shopware\Core\Content\Media\File\MediaFile;
  11. use Shopware\Core\Content\Media\MediaEntity;
  12. use Shopware\Core\Content\Media\MediaService;
  13. use Shopware\Core\Content\Product\Aggregate\ProductVisibility\ProductVisibilityDefinition;
  14. use Shopware\Core\Content\Product\SalesChannel\ProductAvailableFilter;
  15. use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductCollection;
  16. use Shopware\Core\Framework\Context;
  17. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
  18. use Doctrine\DBAL\Connection;
  19. use Shopware\Core\Content\Product\ProductEntity;
  20. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
  21. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
  22. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\NotFilter;
  23. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\OrFilter;
  24. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\RangeFilter;
  25. use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
  26. use Shopware\Core\Framework\DataAbstractionLayer\Util\AfterSort;
  27. use Shopware\Core\Framework\Uuid\Uuid;
  28. use Shopware\Core\System\SalesChannel\Entity\SalesChannelRepositoryInterface;
  29. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  30. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  31. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  32. use ShopWithMe\Reward\Controller\Storefront\CreditShopController;
  33. use ShopWithMe\Reward\Service\RewardPointService;
  34. use Symfony\Component\Cache\Adapter\RedisAdapter;
  35. use Symfony\Component\HttpFoundation\File\UploadedFile;
  36. use ShopWithMe\MobileApi\Service\ProductService;
  37. use Symfony\Component\HttpFoundation\JsonResponse;
  38. class HomePageService
  39. {
  40.     protected EntityRepository $productRepository;
  41.     protected Connection $connection;
  42.     protected EntityRepository $orderLineItemRepository;
  43.     protected CreditShopController $creditShopController;
  44.     protected RewardPointService $rewardPointService;
  45.     protected EntityRepository $orderRepository;
  46.     protected EntityRepository $orderCustomer;
  47.     protected EntityRepository $categoryRepository;
  48.     protected EntityRepository $rewardPointRepository;
  49.     protected EntityRepository $customerTagRepository;
  50.     protected SalesChannelRepositoryInterface $salesChannelProductRepository;
  51.     protected MediaService $mediaService;
  52.     protected FileSaver $fileSaver;
  53.     protected EntityRepository $mediaRepository;
  54.     protected EntityRepository $mediaFolderRepository;
  55.     protected EntityRepository $mediaDefaultFolderRepository;
  56.     protected EntityRepository $subscriptionRepository;
  57.     protected ProductService $productService;
  58.     protected $redisUrl;
  59.     public function __construct(
  60.         EntityRepository                $repository,
  61.         Connection                      $connection,
  62.         EntityRepository                $orderLineItemRepository,
  63.         CreditShopController            $creditShopController,
  64.         RewardPointService              $rewardPointService,
  65.         EntityRepository                $orderRepository,
  66.         EntityRepository                $orderCustomer,
  67.         EntityRepository                $categoryRepository,
  68.         EntityRepository                $rewardPointRepository,
  69.         EntityRepository                $customerTagRepository,
  70.         SalesChannelRepositoryInterface $salesChannelProductRepository,
  71.         MediaService                    $mediaService,
  72.         FileSaver                       $fileSaver,
  73.         EntityRepository                $mediaRepository,
  74.         EntityRepository                $mediaFolderRepository,
  75.         EntityRepository                $mediaDefaultFolderRepository,
  76.         protected EntityRepository      $customerRepository,
  77.         EntityRepository                $subscriptionRepository,
  78.         ProductService                  $productService,
  79.     )
  80.     {
  81.         $this->productRepository $repository;
  82.         $this->connection $connection;
  83.         $this->orderLineItemRepository $orderLineItemRepository;
  84.         $this->creditShopController $creditShopController;
  85.         $this->rewardPointService $rewardPointService;
  86.         $this->orderRepository $orderRepository;
  87.         $this->orderCustomer $orderCustomer;
  88.         $this->categoryRepository $categoryRepository;
  89.         $this->rewardPointRepository $rewardPointRepository;
  90.         $this->customerTagRepository $customerTagRepository;
  91.         $this->salesChannelProductRepository $salesChannelProductRepository;
  92.         $this->mediaService $mediaService;
  93.         $this->fileSaver $fileSaver;
  94.         $this->mediaRepository $mediaRepository;
  95.         $this->mediaFolderRepository $mediaFolderRepository;
  96.         $this->mediaDefaultFolderRepository $mediaDefaultFolderRepository;
  97.         $this->subscriptionRepository $subscriptionRepository;
  98.         $this->productService $productService;
  99.         $this->redisUrl getenv('REDIS_URL');
  100.     }
  101.     public function searchProduct(?string $keywordSalesChannelContext $context)
  102.     {
  103.         $criteria = new Criteria();
  104.         if (!empty($keyword)) {
  105.             $criteria->setTerm($keyword);
  106.         }
  107.         $criteria->addSorting(new FieldSorting('createdAt'FieldSorting::DESCENDING));
  108.         $criteria->addFilter(new NotFilter(
  109.             MultiFilter::CONNECTION_OR,
  110.             [
  111.                 new EqualsFilter('customFields.productIsBuyWithCredit'true),
  112.                 new EqualsFilter('customFields.isHideOnSearch''true')
  113.             ]
  114.         ));
  115.         $criteria->addAssociations(['deals''cover.media''customFields']);
  116.         return $this->salesChannelProductRepository->search($criteria$context)->getEntities();
  117.     }
  118.     public function productDealDetail($productIdSalesChannelContext $salesChannelContext)
  119.     {
  120.         if (!$productId) {
  121.             return ['error' => 'Product ID is required'];
  122.         }
  123.         $context $salesChannelContext->getContext();
  124.         $languageId $context->getLanguageId();
  125.         $criteria = new Criteria([$productId]);
  126.         $criteria->addAssociation('media');
  127.         /** @var ProductEntity|null $product */
  128.         $product $this->productRepository->search($criteria$context)->first();
  129.         if (!$product) {
  130.             return ['error' => 'Product not found'];
  131.         }
  132.         $images = [];
  133.         $jpgImages array_filter($product->getMedia()->getElements(), function ($media) {
  134.             return str_ends_with($media->getMedia()->getUrl(), '.jpg');
  135.         });
  136.         foreach ($jpgImages as $media) {
  137.             $images[] = [
  138.                 'id' => $media->getMedia()->getId(),
  139.                 'url' => $media->getMedia()->getUrl(),
  140.             ];
  141.         }
  142.         $orderCriteria = new Criteria();
  143.         $orderCriteria->addFilter(new EqualsFilter('productId'$product->getId()));
  144.         $orderCriteria->addAssociation('order');
  145.         $orderLineItems $this->orderLineItemRepository->search($orderCriteria$context);
  146.         $orderDetails = [];
  147.         foreach ($orderLineItems as $lineItem) {
  148.             $order $lineItem->getOrder();
  149.             $orderDetails[] = [
  150.                 'purchaseDate' => $order->getOrderDateTime()->format('Y-m-d H:i:s'),
  151.                 'transactionDate' => $order->getTransactions()?->first()->getCreatedAt()->format('Y-m-d H:i:s'),
  152.                 'trackingCode' => $order->getDeliveries()?->first()->getTrackingCodes()
  153.             ];
  154.         }
  155.         return [
  156.             'id' => $product->getId(),
  157.             'name' => $product->getTranslation('name'$languageId),
  158. //            'price' => $product->getPrice()->first()->getGross(),
  159. //            'description' => $product->getTranslation('description', $languageId),
  160.             'images' => $images,
  161.             'orderDetails' => $orderDetails,
  162.         ];
  163.     }
  164.     public function loadNextCreditProductOfCustomer(int $customerCreditSalesChannelContext $context)
  165.     {
  166.         // Define criteria to filter products
  167.         $criteria = new Criteria();
  168.         $criteria->addFilter(new EqualsFilter('customFields.productIsBuyWithCredit'true));
  169.         $criteria->addFilter(new RangeFilter('customFields.productCreditPrice', [
  170.             RangeFilter::GTE => $customerCredit,
  171.         ]));
  172.         // Optionally, add pagination, sorting, or other filters
  173.         $availableFilter = new ProductAvailableFilter($context->getSalesChannelId(),
  174.             ProductVisibilityDefinition::VISIBILITY_ALL);
  175.         $criteria->addFilter($availableFilter);
  176.         $criteria->addSorting(new FieldSorting('customFields.productCreditPrice'FieldSorting::ASCENDING));
  177.         // Load products using the productPageLoader and the defined criteria
  178.         // Note: The method to load products might differ based on your setup and Shopware version
  179.         // This is a generic example assuming you have a method similar to 'search' in your productPageLoader
  180.         $nextCreditProduct $this->salesChannelProductRepository->search($criteria$context)->first();
  181.         if (empty($nextCreditProduct)) {
  182.             $nextCreditProduct $this->salesChannelProductRepository->search((new Criteria())
  183.                 ->addFilter(new EqualsFilter('customFields.productIsBuyWithCredit'true))
  184.                 ->addFilter($availableFilter)
  185.                 ->addSorting(new FieldSorting('customFields.productCreditPrice'FieldSorting::DESCENDING))
  186.                 ->setLimit(1), $context)->first();
  187.         }
  188.         return $nextCreditProduct;
  189.     }
  190.     public function loadProductsWithCreditOption(SalesChannelContext $context$limit 0)
  191.     {
  192.         // Define criteria to filter products
  193.         $criteria = new Criteria();
  194.         $criteria->addFilter(new EqualsFilter('customFields.productIsBuyWithCredit'true));
  195.         // Optionally, add pagination, sorting, or other filters
  196.         // $criteria->setLimit(15); // Example: Limit to 15 products
  197.         $availableFilter = new ProductAvailableFilter($context->getSalesChannelId(),
  198.             ProductVisibilityDefinition::VISIBILITY_ALL);
  199.         $criteria->addFilter($availableFilter);
  200.         $criteria->addSorting(new FieldSorting('customFields.productCreditPrice'FieldSorting::DESCENDING));
  201.         if ($limit == 5) {
  202.             $criteria->setLimit($limit);
  203.         }
  204.         // Load products using the productPageLoader and the defined criteria
  205.         // Note: The method to load products might differ based on your setup and Shopware version
  206.         // This is a generic example assuming you have a method similar to 'search' in your productPageLoader
  207.         return $this->salesChannelProductRepository->search($criteria$context);
  208.     }
  209.     public function getCustomerCredit(SalesChannelContext $channelContextContext $context)
  210.     {
  211.         $customer $channelContext->getCustomer();
  212.         if ($customer) {
  213.             $customerId $customer->getId();
  214.             $balance $this->rewardPointService->getCustomerTotalRewardPoint($customerId$context);
  215.             return $balance;
  216.         }
  217.         return 0;
  218.     }
  219.     public function getCustomerAddress(SalesChannelContext $context)
  220.     {
  221.         $customerId $context->getCustomer();
  222.         if (!$customerId) {
  223.             return false;
  224.         }
  225.         $data = [
  226.             'defaultBillingAddress' => $customerId->getDefaultBillingAddress(),
  227.             'defaultShippingAddress' => $customerId->getDefaultShippingAddress(),
  228.             'address' => $customerId->getAddresses()
  229.         ];
  230.         return $data;
  231.     }
  232.     public function getCustomerOrderByStatus($statusSalesChannelContext $context)
  233.     {
  234.         $customerId $context->getCustomer()?->getId();
  235.         if (!$customerId) {
  236.             return [];
  237.         }
  238.         $criteria = new Criteria();
  239.         $criteria->addFilter(new EqualsFilter('orderCustomer.customerId'$customerId));
  240.         if ($status) {
  241.             if ($status == 'shipped') {
  242.                 $criteria->addFilter(new EqualsFilter('deliveries.stateMachineState.technicalName'$status));
  243.             } else {
  244.                 $criteria->addFilter(new EqualsFilter('stateMachineState.technicalName'$status));
  245.             }
  246.         }
  247.         $criteria->addAssociation('stateMachineState');
  248.         // $criteria->addAssociation('lineItems.product.media');
  249.         $criteria->addAssociation('lineItems.cover');
  250.         $criteria->addAssociation('lineItems.product');
  251.         $criteria->addAssociation('deliveries');
  252.         $criteria->addAssociation('transactions');
  253.         $criteria->addAssociation('currency');
  254.         $criteria->addAssociation('language.locale');
  255.         $criteria->addSorting(new FieldSorting('createdAt'FieldSorting::DESCENDING));
  256.         $orders $this->orderRepository->search($criteria$context->getContext());
  257.         if ($orders->count() === 0) {
  258.             return [];
  259.         }
  260.         $products = [];
  261.         foreach ($orders as $order) {
  262.             /** @var OrderEntity $order */
  263.             $isSubscription false;
  264.             foreach ($order->getLineItems() as $lineItem) {
  265.                 $product $lineItem->getProduct();
  266.                 if ($product) {
  267.                     $customFields $product->getCustomFields();
  268.                     if (isset($customFields['isBuyProductSubscription']) && $customFields['isBuyProductSubscription']) {
  269.                         $isSubscription true;
  270.                         break;
  271.                     }
  272.                 }
  273.             }
  274.             $products[] = [
  275.                 'orderId' => $order->getId(),
  276.                 'deliveryStatus' => $order->getDeliveries()->last(),
  277.                 'orderNumber' => $order->getOrderNumber(),
  278.                 'orderDate' => $order->getOrderDateTime() ? $order->getOrderDateTime()->format('Y-m-d H:i:s') : null,
  279.                 'orderStatus' => $order->getStateMachineState()?->getTranslated()['name'] ?? 'Unknown',
  280.                 'order' => $order,
  281.                 'isSubscription' => $isSubscription,
  282.             ];
  283.         }
  284.         return $products;
  285.     }
  286.     public function getCategories(SalesChannelContext $context, ?bool $isGetProduct true)
  287.     {
  288.         $client RedisAdapter::createConnection($this->redisUrl);
  289.         $cache = new RedisAdapter($client);
  290.         $entryPointId $context->getSalesChannel()->getNavigationCategoryId();
  291.         $languageId $context->getLanguageId();
  292.         $cacheKey $entryPointId '-categories-' $languageId;
  293.         $item $cache->getItem($cacheKey);
  294.         $categories $item->get();
  295.         if(empty($categories)){
  296.             $criteria = new Criteria();
  297.             $criteria->addFilter(new EqualsFilter('active'true));
  298.             $criteria->addFilter(new EqualsFilter('parentId'$entryPointId));
  299.             $criteria->addAssociation('translations');
  300.             $categoriesQuery $this->categoryRepository->search($criteria$context->getContext());
  301.             $cache->deleteItem($cacheKey);
  302.             $item->set($categoriesQuery);
  303.             $cache->save($item);
  304.             $item $cache->getItem($cacheKey);
  305.             $categories $item->get();
  306.         }
  307.         $categorySorted AfterSort::sort($categories->getElements(), 'afterCategoryId');
  308.         $categoryList = [];
  309.         $products = new SalesChannelProductCollection([]);
  310.         if ($isGetProduct) {
  311.             $productCriteria = new Criteria();
  312.             $productCriteria->addFilter(new EqualsAnyFilter('categories.id'$categories->getIds()));
  313.             $productCriteria->addAssociations(['categories''media''deals']);
  314.             $productCriteria->addSorting(new FieldSorting('updatedAt'FieldSorting::DESCENDING));
  315.             $productCriteria->addFilter(new NotFilter(MultiFilter::CONNECTION_AND, [
  316.                 new EqualsFilter('customFields.product_hide_from_list''true'),
  317.             ]));
  318.             $products $this->salesChannelProductRepository->search($productCriteria$context);
  319.             $productList = [];
  320.             $categoryProducts $products->filter(fn(ProductEntity $product) => empty($product->getCustomFields()['productIsBuyWithCredit']) || !$product->getCustomFields()['productIsBuyWithCredit']);
  321.             foreach ($categoryProducts as $key => $product) {
  322.                 $this->productService->handleProductCustomFields($product$context);
  323.                 $productList[] = $product;
  324.             }
  325.             $categoryList[] = [
  326.                 'id' => 'all',
  327.                 'name' => 'All',
  328.                 'visible' => true,
  329.                 'active' => true,
  330.                 'products' => $productList,
  331.             ];
  332.         }
  333.         foreach ($categorySorted as $category) {
  334.             $productList = [];
  335.             if ($isGetProduct) {
  336.                 $categoryProducts $products->filter(fn(ProductEntity $product) => in_array($category->getId(), $product->getCategoryIds()));
  337.                 if (!empty($categoryProducts)) {
  338.                     foreach ($categoryProducts as $key => $product) {
  339.                         $productList[] = $product;
  340.                     }
  341.                 }
  342.             }
  343.             $categoryList[] = [
  344.                 'id' => $category->getId(),
  345.                 'name' => $category->getTranslation('name'),
  346.                 'visible' => $category->getVisible(),
  347.                 'active' => $category->getActive(),
  348.                 'products' => $productList
  349.             ];
  350.         }
  351.         return $categoryList;
  352.     }
  353.     public function getProductNav(SalesChannelContext $context)
  354.     {
  355.         $productCriteria = new Criteria();
  356.         $productCriteria->addAssociations(['media''deals']);
  357.         $productCriteria->addFilter(new NotFilter(MultiFilter::CONNECTION_AND,
  358.             [new EqualsFilter('customFields.productIsBuyWithCredit'true)]));
  359.         $productCriteria->addFilter(
  360.             new OrFilter([
  361.                 new EqualsFilter('productNumber''EUBT124241029'),
  362.                 new EqualsFilter('productNumber''EUBT101241029'),
  363.                 new EqualsFilter('productNumber''EUBT119241029'),
  364.                 new EqualsFilter('productNumber''EUBT117241029'),
  365.                 new EqualsFilter('productNumber''EUBT108241029'),
  366.             ])
  367.         );
  368.         $productList $this->salesChannelProductRepository->search($productCriteria$context);
  369.         $products $productList->getEntities();
  370.         if ($products->count() < 5) {
  371.             $randomCriteria = new Criteria();
  372.             $randomCriteria->addFilter(new NotFilter(MultiFilter::CONNECTION_AND,
  373.                 [new EqualsFilter('customFields.productIsBuyWithCredit'true)]));
  374.             $randomCriteria->addSorting(new FieldSorting('id'FieldSorting::DESCENDING));
  375.             $randomCriteria->setLimit($products->count());
  376.             $randomProducts $this->salesChannelProductRepository->search($randomCriteria$context);
  377.             foreach ($randomProducts->getEntities() as $randomProduct) {
  378.                 if (!$products->has($randomProduct->getId())) {
  379.                     $products->add($randomProduct);
  380.                 }
  381.             }
  382.         }
  383.         return $products;
  384.     }
  385.     public function getCredits(SalesChannelContext $context)
  386.     {
  387.         $result = new \stdClass();
  388.         $customer $context->getCustomer();
  389.         $date = (new \DateTime())->format('Y-m-d H:i:s');
  390.         $criteria = new Criteria();
  391.         $criteria->addFilter(new EqualsFilter('customerId'$customer->getId()));
  392.         $criteria->addFilter(new RangeFilter('amountTotal', ['gte' => 0]));
  393.         $criteria->addFilter(new OrFilter([
  394.             new EqualsFilter('releaseAt'null),
  395.             new RangeFilter('releaseAt', ['lte' => $date]),
  396.         ]));
  397.         $criteria->addSorting(new FieldSorting('createdAt'FieldSorting::DESCENDING));
  398.         $criteria->addAssociation('order.lineItems');
  399.         $credits $this->rewardPointRepository->search($criteria$context->getContext())->getEntities();
  400.         $earns = array();
  401.         $spend = array();
  402.         foreach ($credits as $credit) {
  403.             if ($credit->getType()->{'value'} === 'awarded') {
  404.                 array_push($earns$credit->{'amountTotal'});
  405.             }
  406.             if ($credit->getType()->{'value'} === 'spent') {
  407.                 array_push($spend$credit->{'amountTotal'});
  408.             }
  409.             $productList = [];
  410.             if ($credit->order && $credit->order->lineItems) {
  411.                 foreach ($credit->order->getLineItems() as $lineItem) {
  412.                     $productList[] = $lineItem->productId;
  413.                 }
  414.                 $products $this->queryProductById($productList$context);
  415.                 $credit->products $products;
  416.             }
  417.         }
  418.         $result->credits $credits;
  419.         $result->totalEarn array_sum($earns);
  420.         $result->totalSpent array_sum($spend);
  421.         return $result;
  422.     }
  423.     public function getTopProducts(SalesChannelContext $context)
  424.     {
  425.         $customer $context->getCustomer();
  426.         $productCriteria = new Criteria();
  427.         $productCriteria->setLimit(5);
  428. //        $productCriteria->addFilter(new EqualsFilter('customFields.is_top_seller', true));
  429.         $productCriteria->addFilter(new NotFilter(
  430.             MultiFilter::CONNECTION_OR,
  431.             [
  432.                 new EqualsFilter('customFields.productIsBuyWithCredit'true),
  433.                 new EqualsFilter('customFields.isHideOnSearch''true')
  434.             ]
  435.         ));
  436.         $productCriteria->addSorting(new FieldSorting('customFields.is_top_seller'FieldSorting::DESCENDING));
  437.         $productCriteria->addSorting(new FieldSorting('updatedAt'FieldSorting::DESCENDING));
  438.         $productCriteria->addAssociations(['deals''cover.media''customFields']);
  439.         $products $this->salesChannelProductRepository->search($productCriteria$context)->getEntities();
  440.         foreach ($products as $product) {
  441.             if (!empty($product->getExtension('currentDeal')->dealPrice) && !empty($product->getExtension('currentDeal')->marketPrice)) {
  442.                 $dealPriceKey array_keys($product->getExtension('currentDeal')->dealPrice->getElements())[0];
  443.                 $marketPriceKey array_keys($product->getExtension('currentDeal')->marketPrice->getElements())[0];
  444.                 $market $product->getExtension('currentDeal')->marketPrice->getElements()[$marketPriceKey];
  445.                 $dealPrice $product->getExtension('currentDeal')->dealPrice->getElements()[$dealPriceKey];
  446.                 $product->addArrayExtension('stat', [
  447.                     'marketPrice' => $market->getGross(),
  448.                     'dealPrice' => $dealPrice->getGross(),
  449.                     'discountRate' => $product->getExtension('currentDeal')->getDiscountRate() == null $product->getExtension('currentDeal')->getDiscountRate(),
  450.                 ]);
  451.             } else {
  452.                 $product->addArrayExtension('stat', [
  453.                     'marketPrice' => 0,
  454.                     'dealPrice' => 0,
  455.                     'discountRate' => 0,
  456.                 ]);
  457.             }
  458.         }
  459.         return $products;
  460.     }
  461.     public function getRedeemedItems(SalesChannelContext $context)
  462.     {
  463.         $orderItemCriteria = new Criteria();
  464.         $customer $context->getCustomer();
  465.         $orderItemCriteria->addFilter(new EqualsFilter('type''credit-product'));
  466.         $orderItemCriteria->addAssociation('order.transactions.paymentMethod');
  467.         $orderItemCriteria->addAssociation('order.transactions.stateMachineState');
  468.         $orderItemCriteria->addAssociation('order.stateMachineState');
  469.         $orderItemCriteria->addAssociation('order.deliveries');
  470.         $orderItemCriteria->addAssociation('product.media');
  471.         $orderItemCriteria->addAssociation('product.cover');
  472.         $orderItemCriteria->addFilter(new EqualsFilter('order.orderCustomer.customerId'$customer->getId()));
  473.         $orderItems $this->orderLineItemRepository->search($orderItemCriteria$context->getContext())->getEntities();
  474.         $productList = [];
  475.         foreach ($orderItems as $item) {
  476.             $trackingCode $item->getOrder()->getDeliveries()->first()->getTrackingCodes();
  477.             if ($item->payload['productId']) {
  478.                 $productList[] = $item->payload['productId'];
  479.                 $products $this->queryProductById($productList$context);
  480.                 $item->product $products;
  481.             }
  482.             $item->trackingCode $trackingCode;
  483.         }
  484.         return $orderItems;
  485.     }
  486.     public function getCustomerTag(SalesChannelContext $context)
  487.     {
  488.         $extensionId null;
  489.         if ($context->getCustomer()->getExtension('customerInterests') && isset(
  490.                 $context->getCustomer()->getExtension('customerInterests')->all()['id']
  491.             )) {
  492.             $extensionId $context->getCustomer()->getExtension('customerInterests')->all()['id'];
  493.         }
  494.         if (!empty($extensionId)) {
  495.             $criteria = new Criteria();
  496.             $criteria->addFilter(new EqualsFilter('customerId'$extensionId));
  497.             $criteria->addAssociation('tag.media');
  498.             return $this->customerTagRepository->search($criteria$context->getContext())->getEntities();
  499.         }
  500.         return [];
  501.     }
  502.     public function queryProductById(array $productIdSalesChannelContext $context)
  503.     {
  504.         $criteria = new Criteria($productId);
  505.         $criteria->addAssociation('media');
  506.         return $this->productRepository->search($criteria$context->getContext())->getEntities();
  507.     }
  508.     public function customerTransactionSlipUpload(
  509.         UploadedFile        $file,
  510.         CustomerEntity      $customer,
  511.         string              $orderId,
  512.         SalesChannelContext $context
  513.     ): ?MediaEntity
  514.     {
  515.         $folderName 'Customer Media';
  516.         $fileName explode('.',
  517.                 $file->getClientOriginalName())[0] . 'customer-' $customer->getId() . '-order-' $orderId '-transaction-slip';
  518.         return $this->customerUpload($file$folderName$fileName$context);
  519.     }
  520.     public function customerAvatarUpload(
  521.         UploadedFile        $file,
  522.         CustomerEntity      $customer,
  523.         SalesChannelContext $context
  524.     ): ?MediaEntity
  525.     {
  526.         $folderName 'Customer Media';
  527.         $fileName 'customer-avatar-' $customer->getId();
  528.         return $this->customerUpload($file$folderName$fileName$context);
  529.     }
  530.     public function customerUpload(
  531.         UploadedFile        $file,
  532.         string              $folderName,
  533.         string              $fileName,
  534.         SalesChannelContext $context
  535.     ): ?MediaEntity
  536.     {
  537.         $sourcePath $file->getPathname();
  538.         $sourceMime = (string) $file->getMimeType();
  539.         $sourceExt  strtolower($file->getClientOriginalExtension() ?: '');
  540.         if ($this->isHeic($sourceExt$sourceMime)) {
  541.             $converted $this->convertHeicToJpeg($sourcePath);
  542.             if ($converted === null) {
  543.                 return null;
  544.             }
  545.             [$sourcePath$sourceMime$sourceExt] = $converted;
  546.         }
  547.         $mediaFile = new MediaFile(
  548.             $sourcePath,
  549.             $sourceMime,
  550.             $sourceExt,
  551.             filesize($sourcePath) ?: $file->getSize()
  552.         );
  553.         $media $this->getMediaByFileName($fileName$context->getContext());
  554.         $mediaId $media?->getId();
  555.         $mediaFolderId $media?->getMediaFolderId();
  556.         if (empty($mediaId)) {
  557.             if (empty($mediaFolderId)) {
  558.                 $mediaFolder $this->getMediaFolder(
  559.                     $folderName,
  560.                     $context->getContext(),
  561.                 );
  562.                 $mediaFolderId $mediaFolder?->getId();
  563.             }
  564.             $mediaId $this->createMediaInFolder(
  565.                 $mediaFolderId,
  566.                 $context->getContext(),
  567.             );
  568.         }
  569.         $mediaId $this->mediaService->saveMediaFile(
  570.             $mediaFile,
  571.             $fileName,
  572.             $context->getContext(),
  573.             $folderName,
  574.             $mediaId,
  575.         );
  576.         return $this->getMedia($mediaId$context->getContext());
  577.     }
  578.     private function isHeic(string $extstring $mime): bool
  579.     {
  580.         if (in_array($ext, ['heic''heif'], true)) {
  581.             return true;
  582.         }
  583.         return false;
  584.     }
  585.     private function convertHeicToJpeg(string $sourcePath): ?array
  586.     {
  587.         if (!class_exists(\Imagick::class)) {
  588.             return null;
  589.         }
  590.         $imagick = new \Imagick();
  591.         try {
  592.             $imagick->readImage($sourcePath);
  593.             if ($imagick->getNumberImages() > 1) {
  594.                 $imagick->setIteratorIndex(0);
  595.             }
  596.             $imagick->setImageFormat('jpeg');
  597.             $imagick->setImageCompression(\Imagick::COMPRESSION_JPEG);
  598.             $imagick->setImageCompressionQuality(90);
  599.             $tmpBase tempnam(sys_get_temp_dir(), 'heic_');
  600.             $jpgPath $tmpBase '.jpg';
  601.             @unlink($tmpBase);
  602.             $imagick->writeImage($jpgPath);
  603.             return [$jpgPath'image/jpeg''jpg'];
  604.         } catch (\Throwable $e) {
  605.             return null;
  606.         } finally {
  607.             $imagick->clear();
  608.             $imagick->destroy();
  609.         }
  610.     }
  611.     private function getMediaDefaultFolderId(string $folderContext $context): string
  612.     {
  613.         $criteria = new Criteria();
  614.         $criteria->addFilter(new EqualsFilter('entity'$folder));
  615.         $criteria->setLimit(1);
  616.         $mediaDefaultFolder $this->mediaDefaultFolderRepository->search($criteria$context)->first();
  617.         if (!$mediaDefaultFolder) {
  618.             $id Uuid::randomHex();
  619.             $this->mediaDefaultFolderRepository->create([
  620.                 [
  621.                     'id' => $id,
  622.                     'entity' => $folder,
  623.                     'associationFields' => []
  624.                 ]
  625.             ], $context);
  626.         } else {
  627.             $id $mediaDefaultFolder->getId();
  628.         }
  629.         return $id;
  630.     }
  631.     private function getMediaFolder(string $folderContext $context): ?MediaFolderEntity
  632.     {
  633.         $criteria = new Criteria();
  634.         $criteria->addFilter(new EqualsFilter('name'$folder));
  635.         $criteria->addFilter(new EqualsFilter('defaultFolder.entity''customer'));
  636.         $criteria->addAssociation('defaultFolder');
  637.         $criteria->setLimit(1);
  638.         $mediaFolder $this->mediaFolderRepository->search($criteria$context)->first();
  639.         if (!$mediaFolder) {
  640.             $mediaDefaultFolderId $this->getMediaDefaultFolderId('customer'$context);
  641.             $defaultMediaFolder $this->mediaFolderRepository->search((new Criteria())
  642.                 ->addFilter(new EqualsFilter('defaultFolder.entity''user'))
  643.                 ->setLimit(1), $context)->first();
  644.             $newMediaFolder = new MediaFolderEntity();
  645.             $newMediaFolder->setId(Uuid::randomHex());
  646.             $newMediaFolder->setName($folder);
  647.             $newMediaFolder->setDefaultFolderId($mediaDefaultFolderId);
  648.             $newMediaFolder->setConfigurationId($defaultMediaFolder->getConfigurationId());
  649.             $this->createMediaFolder($newMediaFolder$context);
  650.             $mediaFolder $this->mediaFolderRepository->search(new Criteria([$newMediaFolder->getId()]),
  651.                 $context)->first();
  652.         }
  653.         return $mediaFolder;
  654.     }
  655.     private function createMediaFolder(MediaFolderEntity $mediaFolderContext $context): void
  656.     {
  657.         $payload = [
  658.             'id' => $mediaFolder->getId(),
  659.             'configurationId' => $mediaFolder->getConfigurationId(),
  660.             'name' => $mediaFolder->getName(),
  661.         ];
  662.         if ($mediaFolder->getDefaultFolderId()) {
  663.             $payload['defaultFolderId'] = $mediaFolder->getDefaultFolderId();
  664.         } else {
  665.             if ($mediaFolder->getParentId()) {
  666.                 $payload['parentId'] = $mediaFolder->getParentId();
  667.                 $payload['useParentConfiguration'] = true;
  668.             }
  669.         }
  670.         $this->mediaFolderRepository->create([$payload], $context);
  671.     }
  672.     public function createMediaInFolder(string $mediaFolderIdContext $context): string
  673.     {
  674.         $mediaId Uuid::randomHex();
  675.         $this->mediaRepository->create([
  676.             [
  677.                 'id' => $mediaId,
  678.                 'private' => false,
  679.                 'mediaFolderId' => $mediaFolderId,
  680.             ]
  681.         ], $context);
  682.         return $mediaId;
  683.     }
  684.     private function getMediaByFileName(string $fileNameContext $context): ?MediaEntity
  685.     {
  686.         $criteria = new Criteria();
  687.         $criteria->addFilter(new EqualsFilter('fileName'$fileName));
  688.         $criteria->setLimit(1);
  689.         return $this->mediaRepository->search($criteria$context)->first();
  690.     }
  691.     private function getMedia(string $mediaIdContext $context): ?MediaEntity
  692.     {
  693.         $criteria = new Criteria([$mediaId]);
  694.         $criteria->setLimit(1);
  695.         return $this->mediaRepository->search($criteria$context)->first();
  696.     }
  697.     public function getCustomerInvitedCount(SalesChannelContext $context): array
  698.     {
  699.         $customer $context->getCustomer();
  700.         $emailInvitedCount 0;
  701.         $emailJoinedCount 0;
  702.         if (!empty($customer) && $customer->getAffiliateCode() === $customer->getCampaignCode()) {
  703.             $currentEmailListInvited = !empty($customer->getCustomFields()['emailListInvited']) ? (is_array($customer->getCustomFields()['emailListInvited']) ? $customer->getCustomFields()['emailListInvited'] : json_decode($customer->getCustomFields()['emailListInvited'])) : [];
  704.             $emailInvitedCount count($currentEmailListInvited);
  705.             if (!empty($currentEmailListInvited)) {
  706.                 $emailList = [];
  707.                 foreach ($currentEmailListInvited as $email) {
  708.                     $emailList[] = $email['email'];
  709.                 }
  710.                 $emailJoinedCount $this->customerRepository->search((new Criteria())->addFilter(
  711.                     new EqualsAnyFilter('email'$emailList),
  712.                     new EqualsFilter('campaignCode'$customer->getAffiliateCode()),
  713.                     new NotFilter(MultiFilter::CONNECTION_AND, [
  714.                         new EqualsFilter('id'$customer->getId()),
  715.                     ]),
  716.                 ), $context->getContext())->count();
  717.             }
  718.             return [
  719.                 'invitedCount' => $emailInvitedCount,
  720.                 'joinedCount' => $emailJoinedCount,
  721.             ];
  722.         }
  723.         return [
  724.             'invitedCount' => $emailInvitedCount,
  725.             'joinedCount' => $emailJoinedCount,
  726.         ];
  727.     }
  728.     public function getCustomerInvitedList(SalesChannelContext $context): array
  729.     {
  730.         $customer $context->getCustomer();
  731.         if (!empty($customer) && $customer->getAffiliateCode() === $customer->getCampaignCode()) {
  732.             $currentEmailListInvited = !empty($customer->getCustomFields()['emailListInvited']) ? (is_array($customer->getCustomFields()['emailListInvited']) ? $customer->getCustomFields()['emailListInvited'] : json_decode($customer->getCustomFields()['emailListInvited'])) : [];
  733.             if (!empty($currentEmailListInvited)) {
  734.                 $emailList = [];
  735.                 foreach ($currentEmailListInvited as $email) {
  736.                     $emailList[] = $email['email'];
  737.                 }
  738.                 $customers $this->customerRepository->search((new Criteria())->addFilter(
  739.                     new EqualsAnyFilter('email'$emailList),
  740.                     new EqualsFilter('campaignCode'$customer->getAffiliateCode()),
  741.                     new NotFilter(MultiFilter::CONNECTION_AND, [
  742.                         new EqualsFilter('id'$customer->getId()),
  743.                     ]),
  744.                 ), $context->getContext());
  745.                 foreach ($currentEmailListInvited as &$item) {
  746.                     $item['customer'] = $customers->filter(fn(CustomerEntity $customer) => $customer->getEmail() === $item['email'])->first();
  747.                 }
  748.             }
  749.             return $currentEmailListInvited;
  750.         }
  751.         return [];
  752.     }
  753.     public function getOrderById($orderIdSalesChannelContext $context)
  754.     {
  755.         $criteria = new Criteria();
  756.         $criteria->addAssociation('stateMachineState');
  757.         $criteria->addAssociation('lineItems.cover');
  758.         $criteria->addAssociation('lineItems.product.cover');
  759.         $criteria->addAssociation('deliveries');
  760.         $criteria->addAssociation('language')
  761.         ->addAssociation('transactions.paymentMethod');
  762.         $criteria->addFilter(new EqualsFilter('id'$orderId));
  763.         return $this->orderRepository->search($criteria$context->getContext())->first();
  764.     }
  765.     public function getSubscriptions(SalesChannelContext $context)
  766.     {
  767.         $customerId $context->getCustomerId();
  768.         $criteria = new Criteria();
  769.         $criteria->addFilter(new EqualsFilter('customerId'$customerId));
  770.         $criteria->addAssociation('product.cover');
  771.         $criteria->addAssociation('customer');
  772.         $criteria->addAssociation('address');
  773.         $criteria->addSorting(new FieldSorting('createdAt'FieldSorting::DESCENDING));
  774.         return $this->subscriptionRepository->search($criteria$context->getContext())->getElements();
  775.     }
  776.     public function sumPriceSubscriptions($subscriptions)
  777.     {
  778.         $price 0;
  779.         foreach ($subscriptions as $subscription) {
  780.             $price += $subscription->getProduct()->getPrice()->last()->getGross();
  781.         }
  782.         return $price;
  783.     }
  784.     public function getOrderCreditType($orderIdSalesChannelContext $context)
  785.     {
  786.         $criteria = new Criteria();
  787.         $criteria->addFilter(new EqualsFilter('orderId'$orderId));
  788.         return $this->rewardPointRepository->search($criteria$context->getContext())->first()?->getType();
  789.     }
  790. }