src/Controller/ProductController.php line 319

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Controller;
  4. use App\Doctrine\ORM\ProductReadonlyRepository;
  5. use App\Doctrine\ORM\ProductRepository;
  6. use App\Doctrine\ORM\ProductVariantCalculationRepository;
  7. use App\Doctrine\ORM\TileDetailsRepositoryInterface;
  8. use App\Entity\Product\Product;
  9. use App\Entity\Product\ProductAttribute;
  10. use App\Entity\Taxonomy\Taxon;
  11. use App\Twig\PriceExtension;
  12. use Doctrine\ORM\EntityManagerInterface;
  13. use League\Csv\Writer;
  14. use Sylius\Bundle\MoneyBundle\Templating\Helper\ConvertMoneyHelperInterface;
  15. use Sylius\Component\Channel\Context\ChannelContextInterface;
  16. use Sylius\Component\Core\Model\OrderItemInterface;
  17. use Sylius\Component\Core\Model\ProductInterface;
  18. use Sylius\Component\Core\Model\TaxonInterface;
  19. use Sylius\Component\Core\Repository\OrderItemRepositoryInterface;
  20. use Sylius\Component\Currency\Context\CurrencyContextInterface;
  21. use Sylius\Component\Locale\Context\LocaleContextInterface;
  22. use Sylius\Component\Locale\Provider\LocaleProviderInterface;
  23. use Sylius\Component\Taxonomy\Repository\TaxonRepositoryInterface;
  24. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  25. use Symfony\Component\HttpFoundation\JsonResponse;
  26. use Symfony\Component\HttpFoundation\Request;
  27. use Symfony\Component\HttpFoundation\Response;
  28. use Symfony\Component\HttpFoundation\StreamedResponse;
  29. use Symfony\Component\Routing\Annotation\Route;
  30. class ProductController extends AbstractController
  31. {
  32.     private ?Taxon $tileTaxon null;
  33.     public function __construct(
  34.         private LocaleContextInterface $localeContext,
  35.         private TaxonRepositoryInterface $taxonRepository,
  36.         private TileDetailsRepositoryInterface $tileDetailsRepository,
  37.         private ProductVariantCalculationRepository $productVariantCalculationRepository,
  38.         private EntityManagerInterface $entityManager,
  39.         private ProductRepository $productRepository,
  40.         private OrderItemRepositoryInterface $orderItemRepository,
  41.         private PriceExtension $priceExtension,
  42.         //private ConvertMoneyHelperInterface $convertMoneyHelper,
  43.         private ChannelContextInterface $channelContext,
  44.         private CurrencyContextInterface $currencyContext,
  45.         //private ProductReadonlyRepository $productReadonlyRepository,
  46.     ) {
  47.     }
  48.     private array $finishingPiecesTaxons;
  49.     public function renderHoverPart(string|int|ProductInterface $productRequest $request): Response
  50.     {
  51.         $locale $this->localeContext->getLocaleCode();
  52.         $cacheKey 'product-box-hover1-'.$locale.'-'.$product;
  53.         if (is_string($product)) {
  54.             $response apcu_fetch($cacheKey);
  55.             if ($response !== false) {
  56.                 return $response;
  57.             }
  58.         }
  59.         $product $this->getProduct($product);
  60.         /** @var ProductAttribute[] $attributes */
  61.         $attributes $product->getAttributesByLocale($locale'en_US')
  62.             ->slice(08);
  63.         //if ($this->tileTaxon === null) {
  64.         //    $this->tileTaxon = $this->taxonRepository->findOneBy(['code' => 'ALL_TILES']);
  65.         //}
  66.         $description '';
  67.         if (!$this->hasTaxonByCode($product'ALL_TILES')) {
  68.             $attributes = [];
  69.             $description $product->getShortDescription();
  70.         }
  71.         $response $this->render('@SyliusShop/Product/boxHover.html.twig', [
  72.             'product' => $product,
  73.             'collection' => $this->getProductCollection($product),
  74.             'brand' => $product->getBrand(),
  75.             'attributes' => $attributes,
  76.             'description' => $description,
  77.         ]);
  78.         return $response;
  79.         //
  80.         //$response->setPublic();
  81.         //$response->setTtl(604800);
  82.         //$response->setMaxAge(time() + 604800);
  83.         //
  84.         //apcu_store($cacheKey, $response, 3600);
  85.         //
  86.         //return $response;
  87.     }
  88.     private function hasTaxonByCode(ProductInterface $productstring $code): bool
  89.     {
  90.         foreach ($product->getTaxons() as $taxon) {
  91.             if ($taxon->getCode() === $code) {
  92.                 return true;
  93.             }
  94.         }
  95.         return false;
  96.     }
  97.     public function renderBox(string|int|ProductInterface $productRequest $request): Response
  98.     {
  99.         $product $this->getProduct($product);
  100.         $response $this->render('@SyliusShop/Product/_box.html.twig', [
  101.             'product' => $product,
  102.         ]);
  103.         return $response;
  104.         $response->setPublic();
  105.         $response->setMaxAge(604800);
  106.         return $response;
  107.     }
  108.     public function renderAttributeList(string|int|ProductInterface $productRequest $request): mixed
  109.     {
  110.         $product $this->getProduct($product);
  111.         /** @var ProductAttribute[] $attributes */
  112.         $attributes $product->getAttributesByLocale($this->localeContext->getLocaleCode(), 'en_US')
  113.             ->slice(01);
  114.         try {
  115.             $response $this->render('@SyliusShop/Product/showAttributesList.html.twig', [
  116.                 'product' => $product,
  117.                 'collection' => $this->getProductCollection($product),
  118.                 'brand' => $product->getBrand(),
  119.                 'attributes' => $attributes,
  120.             ]);
  121.         } catch (\Exception $exception) {
  122.             if (isset($_GET['hehehe'])) {
  123.                 dump($exception);
  124.                 exit;
  125.             }
  126.         }
  127.         return $response;
  128.         $response->setPublic();
  129.         $response->setMaxAge(604800);
  130.         return $response;
  131.     }
  132.     public function renderLowestMarketPrice(string|int|ProductInterface $productRequest $request): mixed
  133.     {
  134.         $product $this->getProduct($product);
  135.         $response $this->render('@SyliusShop/Product/_lowestMarketPrice.html.twig', [
  136.             'product' => $product,
  137.         ]);
  138.         return $response;
  139.         $response->setPublic();
  140.         $response->setMaxAge(604800);
  141.         return $response;
  142.     }
  143.     public function renderCartAttributeList(string|int|ProductInterface $productstring|int|OrderItemInterface $item): mixed
  144.     {
  145.         $product $this->getProduct($product);
  146.         $tileDetails $this->tileDetailsRepository->findOneByProduct($product);
  147.         if (!$item instanceof OrderItemInterface) {
  148.             $item $this->orderItemRepository->find((int) $item->getId());
  149.         }
  150.         $response $this->render('@SyliusShop/Layout/Modal/cart_item_attributes.html.twig', [
  151.             'product' => $product,
  152.             'item' => $item,
  153.             'tileDetails' => $tileDetails,
  154.             'isTile' => $this->hasTileTaxon($product),
  155.         ]);
  156.         return $response;
  157.         $response->setPublic();
  158.         $response->setMaxAge(604800);
  159.         return $response;
  160.     }
  161.     public function renderImages(array|string|int|ProductInterface $productRequest $request): Response
  162.     {
  163.         $product $this->getProduct($product);
  164.         $response $this->render('@SyliusShop/Product/Show/newImages.html.twig', [
  165.             'product' => $product,
  166.         ]);
  167.         return $response;
  168.         $response->setPublic();
  169.         $response->setMaxAge(604800);
  170.         return $response;
  171.     }
  172.     public function renderImagesEsi(string $namebool $isFinishingPiecestring $images ''Request $request): Response
  173.     {
  174.         $product = [
  175.             'name' => $name,
  176.             'isFinishingPiece' => $isFinishingPiece,
  177.             'images' => array_filter(json_decode($imagestrue)),
  178.         ];
  179.         $response $this->render('@SyliusShop/Product/Show/newImages.html.twig', [
  180.             'product' => $product,
  181.         ]);
  182.         return $response;
  183.         $response->setPublic();
  184.         $response->setMaxAge(604800);
  185.         return $response;
  186.     }
  187.     public function renderImagesLower(string|int|ProductInterface $productRequest $request): Response
  188.     {
  189.         $product $this->getProduct($product);
  190.         $response $this->render('@SyliusShop/Product/Show/newImagesLow.html.twig', [
  191.             'product' => $product,
  192.         ]);
  193.         return $response;
  194.         $response->setPublic();
  195.         $response->setMaxAge(604800);
  196.         return $response;
  197.     }
  198.     public function renderTopAttributes(string|int|ProductInterface $productRequest $request): Response
  199.     {
  200.         $product $this->getProduct($product);
  201.         $attributes $product->getAttributesByLocale($this->localeContext->getLocaleCode(), 'en_US');
  202.         $attributeList = [];
  203.         foreach ($attributes as $attribute) {
  204.             $code $attribute->getCode();
  205.             $attributeList[$code] = $attribute;
  206.         }
  207.         $orderedList = [];
  208.         $top = ['SLIP_CLASS''EDGE''SHADE_VARIATION''THICKNESS']; // todo: move to CMS
  209.         foreach ($top as $item) {
  210.             $orderedList[$item] = $attributeList[$item] ?? null;
  211.         }
  212.         $response $this->render('@SyliusShop/Product/Show/_topAttributes.html.twig', [
  213.             'product' => $product,
  214.             'attributes' => $orderedList,
  215.         ]);
  216.         return $response;
  217.         $response->setPublic();
  218.         $response->setMaxAge(604800);
  219.         return $response;
  220.     }
  221.     public function renderPricing(string|int|ProductInterface $productRequest $request): Response
  222.     {
  223.         $product $this->getProduct($product);
  224.         $tileDetails $this->tileDetailsRepository->findOneByProduct($product);
  225.         $response $this->render('@SyliusShop/Product/Show/newPrice.html.twig', [
  226.             'product' => $product,
  227.             'isTile' => $this->hasTileTaxon($product),
  228.             'tileDetails' => $tileDetails,
  229.             'variations' => $product->getEnabledVariants(),
  230.             'options' => $product->getOptions(),
  231.         ]);
  232.         return $response;
  233.         $response->setPublic();
  234.         $response->setMaxAge(0);
  235.         return $response;
  236.     }
  237.     public function renderCartItemPricing(string|int|ProductInterface $productOrderItemInterface $item): Response
  238.     {
  239.         $product $this->getProduct($product);
  240.         $tileDetails $product->getTileDetails()->first();
  241.         if (empty($tileDetails)) {
  242.             $tileDetails null;
  243.         }
  244.         $response $this->render('@SyliusShop/Layout/Modal/cart_item_pricing.html.twig', [
  245.             'product' => $product,
  246.             'isTile' => $this->hasTileTaxon($product),
  247.             'tileDetails' => $tileDetails,
  248.             'item' => $item,
  249.         ]);
  250.         return $response;
  251.         $response->setPublic();
  252.         $response->setMaxAge(604800);
  253.         return $response;
  254.     }
  255.     public function renderNewAttributes(string|int|ProductInterface $productRequest $request): Response
  256.     {
  257.         $product $this->getProduct($product);
  258.         $response $this->render('@SyliusShop/Product/Show/newAttributes.html.twig', [
  259.             'product' => $product,
  260.             'attributes' => $product->getAttributesByLocale($this->localeContext->getLocaleCode(), 'en_US'),
  261.         ]);
  262.         return $response;
  263.         $response->setPublic();
  264.         $response->setMaxAge(604800);
  265.         return $response;
  266.     }
  267.     public function renderProductsFromCollection(string|int|ProductInterface $product): Response
  268.     {
  269.         $product $this->getProduct($product);
  270.         $taxons $product->getTaxons();
  271.         /** @var Product[] $products */
  272.         $products = [];
  273.         $taxon null;
  274.         foreach ($taxons as $taxon) {
  275.             if ($taxon->getLevel() === && $taxon->getRoot()->getCode() === 'collection') {
  276.                 $products $this->productRepository
  277.                     ->createListQueryBuilder($this->localeContext->getLocaleCode(), $taxon->getId())
  278.                     ->andWhere('o.id != :prd_id')
  279.                     ->andWhere('o.enabled = :enabled')
  280.                     ->setParameter('prd_id'$product->getId())
  281.                     ->setParameter('enabled'true)
  282.                     ->setMaxResults(12)
  283.                     ->orderBy('RAND()''ASC')
  284.                     ->getQuery()
  285.                     ->getResult()
  286.                 ;
  287.                 break;
  288.             }
  289.         }
  290.         $finishingPiecesTaxon $this->taxonRepository->findOneBy(['code' => 'FINISHING_PIECES']);
  291.         foreach ($products as $key => $item) {
  292.             if ($finishingPiecesTaxon !== null && $product->hasTaxon($finishingPiecesTaxon)) {
  293.                 unset($products[$key]);
  294.             }
  295.         }
  296.         $response $this->render('@SyliusShop/Product/showProductsFromCollection.html.twig', [
  297.             'products' => $products,
  298.             'taxon' => $taxon,
  299.         ]);
  300.         return $response;
  301.         $response->setPublic();
  302.         $response->setMaxAge(604800);
  303.         return $response;
  304.     }
  305.     #[Route('api/v3/prices')]
  306.     public function getPrices(Request $request): JsonResponse
  307.     {
  308.         $result = [];
  309.         $data $request->toArray();
  310.         $channel $this->channelContext->getChannel();
  311.         if (isset($data['product'])) {
  312.             foreach ((array) $data['product'] as $product) {
  313.                 $product $this->getProduct($product);
  314.                 $variant $product->getVariants()->first();
  315.                 if ($variant === false) {
  316.                     continue;
  317.                 }
  318.                 $price $this->priceExtension->calculatePriceWithTax($variant, ['channel' => $channel]);
  319.                 $convertedPrice $this->convertMoneyHelper->convertAmount($price$channel->getBaseCurrency()?->getCode(), $this->currencyContext->getCurrencyCode());
  320.                 $result[] = [
  321.                     'product' => $product->getId(),
  322.                     'price' => $convertedPrice,
  323.                     'lowestMarketingPrice' => '',
  324.                     'onSale' => false,
  325.                     'oldPrice' => '',
  326.                 ];
  327.             }
  328.         }
  329.         return $this->json($result);
  330.     }
  331.     public function renderAdminVariationCalculation(string|int|ProductInterface $productRequest $request): Response
  332.     {
  333.         $product $this->getProduct($product);
  334.         $isCalculated true;
  335.         $isTile = !$product->getTileDetails()->isEmpty();
  336.         if (!$isTile) {
  337.             $queued $this->productVariantCalculationRepository->findOneBy(['product' => $product'status' => 'queued']);
  338.             $processing $this->productVariantCalculationRepository->findOneBy(['product' => $product'status' => 'processing']);
  339.             $isCalculated $queued === null && $processing === null;
  340.         }
  341.         $response $this->render('@SyliusAdmin/Product/Show/variantionCalculation.html.twig', [
  342.             'product' => $product,
  343.             'isTile' => $isTile,
  344.             'isPricesCalculated' => $isCalculated,
  345.             'attributes' => $product->getAttributesByLocale($this->localeContext->getLocaleCode(), 'en_US'),
  346.         ]);
  347.         return $response;
  348.         $response->setPublic();
  349.         $response->setMaxAge(604800);
  350.         return $response;
  351.     }
  352.     private function getProductCollection(ProductInterface $product): ?TaxonInterface
  353.     {
  354.         $taxons $product->getTaxons();
  355.         foreach ($taxons as $taxon) {
  356.             if ($taxon->getLevel() === && $this->isTaxonCollection($taxon)) {
  357.                 return $taxon;
  358.             }
  359.         }
  360.         return null;
  361.     }
  362.     private function isTaxonCollection(TaxonInterface $taxon): bool
  363.     {
  364.         return $taxon->getRoot()?->getCode() === 'collection';
  365.     }
  366.     private function hasTileTaxon(ProductInterface $product): bool
  367.     {
  368.         // TODO: Implement hasTileTaxon
  369.         return false;
  370.     }
  371.     #[Route(path'/admin/import-export/brands/products/export'name'app_admin_import_admin_products_export')]
  372.     public function export(LocaleProviderInterface $localeProvider): Response
  373.     {
  374.         $headers = ['PRODUCT_CODE''BRAND_CODE''PRICE''ORIGINAL_PRICE'];
  375.         //$products = $this->productRepository->findBy(['enabled' => true]);
  376.         return new StreamedResponse(
  377.             function () use ($headers) {
  378.                 $fp fopen('php://output''wb');
  379.                 $writer Writer::createFromStream($fp);
  380.                 $sep ';';
  381.                 $writer->setDelimiter($sep);
  382.                 $writer->insertAll([$headers]);
  383.                 $this->outputProductData($writer);
  384.             },
  385.             200,
  386.             [
  387.                 'content-type' => 'text/csv',
  388.                 'content-disposition' => sprintf('attachment;filename="brands_products_%s.csv"'date('Ymd_his')),
  389.             ]
  390.         );
  391.     }
  392.     private function outputProductData(Writer $writer): void
  393.     {
  394.         //$offset = 0;
  395.         //$limit = 5000;
  396.         $statement $this->entityManager->getConnection()->prepare(
  397.             'SELECT p.code, b.code,
  398.                     COALESCE(CASE
  399.                         WHEN d.unit = "mq" THEN ROUND(MIN(cp.price / d.sqmPerBox) / 100, 2)
  400.                         WHEN d.unit = "mt" THEN ROUND(MIN(cp.price / d.lmPerBox) / 100, 2)
  401.                         WHEN d.unit = "pcs" THEN ROUND(MIN(cp.price / d.pcsPerBox) / 100, 2)
  402.                     ELSE ROUND(MIN(cp.price) / 100, 2) END, 0) as `price`,
  403.                     COALESCE(CASE
  404.                         WHEN d.unit = "mq" THEN ROUND(MIN(cp.original_price / d.sqmPerBox) / 100, 2)
  405.                         WHEN d.unit = "mt" THEN ROUND(MIN(cp.original_price / d.lmPerBox) / 100, 2)
  406.                         WHEN d.unit = "pcs" THEN ROUND(MIN(cp.original_price / d.pcsPerBox) / 100, 2)
  407.                     ELSE ROUND(MIN(cp.original_price) / 100, 2) END, 0) as `original_price`
  408.                 FROM sylius_product p
  409.                 LEFT JOIN loevgaard_brand b ON b.id = p.brand_id
  410.                 LEFT JOIN sylius_product_tile_details d on d.product_id = p.id
  411.                 LEFT JOIN sylius_product_variant v ON v.product_id = p.id
  412.                 LEFT JOIN sylius_channel_pricing cp ON cp.product_variant_id = v.id
  413.                 WHERE p.enabled = true
  414.                 GROUP BY p.id
  415.         ');
  416.         $result $statement->executeQuery();
  417.         while (($line $result->fetchNumeric()) !== false) {
  418.             $writer->insertOne($line);
  419.         }
  420.         //
  421.         //do {
  422.         //    $results = $this->productRepository->findBy(['enabled' => true], limit: $limit, offset: $offset);
  423.         //    /** @var Product $product */
  424.         //    foreach ($results as $product) {
  425.         //        $line = [
  426.         //            $product->getCode(),
  427.         //            $product->getBrand()?->getCode(),
  428.         //        ];
  429.         //        $writer->insertOne($line);
  430.         //    }
  431.         //    $offset += $limit;
  432.         //} while (!empty($results));
  433.     }
  434.     #[Route(path'/export/products-without-images/csv'methods: ['GET'], name'products_without_images')]
  435.     public function getCsvListWithoutImages(Request $request): Response
  436.     {
  437.         $content[] = 'CODE';
  438.         //$andWhere = '';
  439.         //$taxons = [];
  440.         $exclude array_filter(explode(','$request->query->get('exclude''')));
  441.         //$codes = [];
  442.         //foreach ($exclude as $key => $categoryCode) {
  443.         //    $taxons['taxon'.$key] = $this->taxonRepository->findOneBy(['code' => $categoryCode]);
  444.         //    $andWhere .= ' and pt.taxon != :taxon'.$key;
  445.         //}
  446.         //
  447.         //if (!empty($only = $request->query->get('only', ''))) {
  448.         //    $taxons['only'] = $this->taxonRepository->findOneBy(['code' => $only]);
  449.         //    $andWhere .= ' and pt.taxon = :only';
  450.         //}
  451.         set_time_limit(99999);
  452.         //$dql = "SELECT distinct p.code, t.code as cat from App\Entity\Product\Product p join p.productTaxons pt join pt.taxon t where p.images is empty and t.code not in(:codes) ";
  453.         //$dql = "SELECT p from App\Entity\Product\Product p join p.productTaxons pt where p.images is empty";
  454.         $dql "SELECT p.code from App\Entity\Product\Product p where p.images is empty and p.id not in (select distinct p2.id from App\Entity\Product\Product p2 join p2.productTaxons pt join pt.taxon t where p2.images is empty and t.code in(:codes))";
  455.         if ($request->query->get('enabled') !== null) {
  456.             $dql .= ' and p.enabled = :enabled';
  457.         }
  458.         $query $this->entityManager->createQuery($dql);
  459.         //$query->setParameters($taxons);
  460.         $query->setParameter('codes'$exclude);
  461.         if ($request->query->get('enabled') !== null) {
  462.             $query->setParameter('enabled'$request->query->get('enabled'));
  463.         }
  464.         $rows $query->getArrayResult();
  465.         /** @var Product $product */
  466.         foreach ($rows as $product) {
  467.             //if (!empty($exclude)) {
  468.             //    foreach ($taxons as $taxon) {
  469.             //        if ($product->getTaxons()->contains($taxon)) {
  470.             //            continue 2;
  471.             //        }
  472.             //    }
  473.             //}
  474.             //$values = array_values($row);
  475.             $content[] = sprintf('"%s"'$product['code']/*, $values[1]*/);
  476.         }
  477.         return new Response(implode("\r\n"$content), 200, ['Content-Type' => 'text/csv']);
  478.     }
  479.     private function getProduct(string|int|ProductInterface $product): ProductInterface
  480.     {
  481.         if (is_scalar($product)) {
  482.             $product $this->productRepository->find((int) $product);
  483.         }
  484.         return $product;
  485.     }
  486. }