vendor/boldr/shop-bundle/src/Presenter/Product/ProductPresenter.php line 258

Open in your IDE?
  1. <?php
  2. namespace Boldr\Shop\ShopBundle\Presenter\Product;
  3. use Boldr\Shop\ShopBundle\CurrencyFormatter;
  4. use Boldr\Shop\ShopBundle\Price\PriceModifierManager;
  5. use Boldr\Shop\ShopBundle\Price\Catalog\{ CatalogPriceQueryCatalogPriceCalculator };
  6. use Boldr\Shop\ShopBundle\Option\OptionManager;
  7. use Boldr\Shop\ShopBundle\Presenter\Category\CategoryPresenter;
  8. use Boldr\Shop\ShopBundle\Presenter\Currency\CurrencyPresenter;
  9. use Boldr\Shop\ShopBundle\Presenter\Option\OptionPresenter;
  10. use Boldr\Shop\ShopBundle\Presenter\Price\PriceModifierView;
  11. use Boldr\Shop\ShopBundle\Entity\{ AttributeProductProductTranslationProductVariantProductAttributeProductImageCustomer };
  12. use Boldr\Shop\ShopBundle\Stock\StockManagerInterface;
  13. use Boldr\Cms\CmsBundle\Content\{ AssetsContentManagerMediaResizer };
  14. use Boldr\Cms\CmsBundle\Presenter\Attachment\AttachmentPresenter;
  15. use Symfony\Contracts\Service\ServiceSubscriberInterface;
  16. use Psr\Container\ContainerInterface;
  17. use Doctrine\ORM\EntityManagerInterface;
  18. use Symfony\Contracts\Cache\TagAwareCacheInterface;
  19. class ProductPresenter implements ServiceSubscriberInterface
  20. {
  21.     protected ContainerInterface $container;
  22.     public static function getSubscribedServices(): array
  23.     {
  24.         return [
  25.             CatalogPriceCalculator::class,
  26.             OptionManager::class,
  27.             MediaResizer::class,
  28.             ContentManager::class,
  29.             CurrencyFormatter::class,
  30.             CategoryPresenter::class,
  31.             CurrencyPresenter::class,
  32.             OptionPresenter::class,
  33.             PriceModifierManager::class,
  34.             StockManagerInterface::class,
  35.             EntityManagerInterface::class,
  36.             AttachmentPresenter::class,
  37.             TagAwareCacheInterface::class
  38.         ];
  39.     }
  40.     // Processors
  41.     private iterable $productViewProcessors;
  42.     private iterable $productVariantViewProcessors;
  43.     private iterable $catalogProductViewProcessors;
  44.     private iterable $catalogProductVariantViewProcessors;
  45.     private iterable $attributeViewProcessors;
  46.     // Media
  47.     private array $productMediaSizes;
  48.     private string $resizedMediaFolder;
  49.     // Cache
  50.     private array $attributeViews = [];
  51.     private array $productViews = [];
  52.     private array $productVariantViews = [];
  53.     public function __construct(
  54.         ContainerInterface $container,
  55.         iterable $productViewProcessors,
  56.         iterable $productVariantViewProcessors,
  57.         iterable $catalogProductViewProcessors,
  58.         iterable $catalogProductVariantViewProcessors,
  59.         iterable $attributeViewProcessors,
  60.         string $resizedMediaFolder,
  61.         array $productMediaSizes,
  62.     ) {
  63.         $this->container $container;
  64.         $this->resizedMediaFolder $resizedMediaFolder;
  65.         $this->productMediaSizes $productMediaSizes;
  66.         $this->attributeViewProcessors $attributeViewProcessors;
  67.         $this->productViewProcessors $productViewProcessors;
  68.         $this->productVariantViewProcessors $productVariantViewProcessors;
  69.         $this->catalogProductViewProcessors $catalogProductViewProcessors;
  70.         $this->catalogProductVariantViewProcessors $catalogProductVariantViewProcessors;
  71.     }
  72.     public function createProductView(Product $productstring $locale): ProductView
  73.     {
  74.         $cacheKey $product->getId().'_'.$locale;
  75.         if (isset($this->productViews[$cacheKey]))
  76.             return $this->productViews[$cacheKey];
  77.         // Create ProductVariant views
  78.         $productVariantViews array_map(
  79.             fn($productVariant) => $this->createProductVariantView($productVariant$locale),
  80.             $product->getVariants()->toArray()
  81.         );
  82.         // Create Attribute views
  83.         $attributeViews array_map(
  84.             fn($attribute) => $this->createAttributeView($attribute$locale),
  85.             $this->getProductAttributes($product)
  86.         );
  87.         // Create category views
  88.         $categoryPresenter $this->container->get(CategoryPresenter::class);
  89.         $categoryViews array_map(
  90.             fn($category) => $categoryPresenter->createCategoryView($category$locale),
  91.             $product->getCategories()->toArray()
  92.         );
  93.         $primaryImageView $product->getPrimaryImageOptions() === null null $this->createProductImageView($product->getPrimaryImageOptions(), $locale);
  94.         $defaultVariantView $this->createProductVariantView($product->getDefaultVariant(), $locale);
  95.         $translation $product->getTranslations()->get($locale);
  96.         $view = new ProductView(
  97.             $product->getId(),
  98.             $translation->getName(),
  99.             $this->container->get(ContentManager::class)->renderAsHtml($translation->getDescription(), new Assets),
  100.             $productVariantViews,
  101.             $defaultVariantView,
  102.             $attributeViews,
  103.             $categoryViews,
  104.             $primaryImageView
  105.         );
  106.         // Let view processors process view to add their own data
  107.         $view array_reduce(
  108.             iterator_to_array($this->productViewProcessors),
  109.             static fn($view$productViewProcessor) => $productViewProcessor->processProductView($view$product$locale),
  110.             $view
  111.         );
  112.         $this->productViews[$cacheKey] = $view;
  113.         return $view;
  114.     }
  115.     public function createProductImageView(ProductImage $productImagestring $locale): ProductImageView
  116.     {
  117.         $cacheKey 'boldr_shop.product_image.'.$productImage->getId().'.'.$locale;
  118.         return $this->container->get(TagAwareCacheInterface::class)->get($cacheKey, function($item) use ($productImage$locale) {
  119.             $item->tag('boldr_shop.product.'.$productImage->getProduct()->getId());
  120.             $attachmentView $this->container->get(AttachmentPresenter::class)->createAttachmentView($productImage->getImage(), $this->productMediaSizes$locale);
  121.             return new ProductImageView(
  122.                 $attachmentView,
  123.                 $productImage->getCover(),
  124.                 $productImage->getPositionX(),
  125.                 $productImage->getPositionY()
  126.             );
  127.         });
  128.     }
  129.     private $productAttributeCache null;
  130.     private $productVariantAttributeCache null;
  131.     private function initAttributeCache(): void
  132.     {
  133.         if ($this->productVariantAttributeCache === null)
  134.         {
  135.             $this->productAttributeCache = [];
  136.             $this->productVariantAttributeCache = [];
  137.             $productAttributeRepository $this->container->get(EntityManagerInterface::class)->getRepository(ProductAttribute::class);
  138.             foreach ($productAttributeRepository->findAll() as $productAttribute)
  139.             {
  140.                 if ($productAttribute->getProductVariant() !== null)
  141.                 {
  142.                     $this->productVariantAttributeCache[$productAttribute->getProductVariant()->getId()][] = $productAttribute;
  143.                 }
  144.                 else
  145.                 {
  146.                     $this->productAttributeCache[$productAttribute->getProduct()->getId()][] = $productAttribute;
  147.                 }
  148.             }
  149.         }
  150.     }
  151.     private function getProductVariantAttributes(ProductVariant $productVariant): array
  152.     {
  153.         $this->initAttributeCache();
  154.         return $this->productVariantAttributeCache[$productVariant->getId()] ?? [];
  155.     }
  156.     private function getProductAttributes(Product $product): array
  157.     {
  158.         $this->initAttributeCache();
  159.         return $this->productAttributeCache[$product->getId()] ?? [];
  160.     }
  161.     public function createProductVariantView(ProductVariant $productVariantstring $locale): ProductVariantView
  162.     {
  163.         $cacheKey $productVariant->getId().'_'.$locale;
  164.         if (isset($this->productVariantViews[$cacheKey]))
  165.             return $this->productVariantViews[$cacheKey];
  166.         $name $productVariant->getTranslations()->get($locale)->getName();
  167.         if ($name === null || $name === '')
  168.         {
  169.             $name $productVariant->getProduct()->getTranslations()->get($locale)->getName();
  170.         }
  171.         $description null;
  172.         // $description = $productVariant->getTranslations()->get($locale)->getDescription();
  173.         if ($description === null)
  174.         {
  175.             $description $productVariant->getProduct()->getTranslations()->get($locale)->getDescription();
  176.         }
  177.         $attributes array_map(
  178.             fn($attribute) => $this->createAttributeView($attribute$locale),
  179.             $this->getProductVariantAttributes($productVariant)
  180.         );
  181.         $view = new ProductVariantView(
  182.             $productVariant->getId(),
  183.             $name,
  184.             $this->container->get(ContentManager::class)->renderAsHtml($description, new Assets),
  185.             $attributes
  186.         );
  187.         // Let view processors process view to add their own data
  188.         $view array_reduce(
  189.             iterator_to_array($this->productVariantViewProcessors),
  190.             static fn($view$productVariantViewProcessor) => $productVariantViewProcessor->processProductVariantView($view$productVariant$locale),
  191.             $view
  192.         );
  193.         $this->productVariantViews[$cacheKey] = $view;
  194.         return $view;
  195.     }
  196.     public function createAttributeView(Attribute $attributestring $locale): AttributeView
  197.     {
  198.         $cacheKey $attribute->getId().'_'.$locale;
  199.         if (isset($this->attributeViews[$cacheKey]))
  200.             return $this->attributeViews[$cacheKey];
  201.         $view = new AttributeView(
  202.             $attribute->getId(),
  203.             $attribute->getTranslations()->get($locale)->getName()
  204.         );
  205.         $view array_reduce(
  206.             iterator_to_array($this->attributeViewProcessors),
  207.             static fn($attributeViewProcessor) => $attributeViewProcessor->processAttributeView($view$attribute$locale),
  208.             $view
  209.         );
  210.         $this->attributeViews[$cacheKey] = $view;
  211.         return $view;
  212.     }
  213.     public function createCatalogProductView(Product $product, ?Customer $customerstring $currencystring $locale): CatalogProductView
  214.     {
  215.         $variants array_map(
  216.             fn($productVariant) => $this->createCatalogProductVariantView($productVariant, [], $customer$currency$locale),
  217.             $product->getVariants()->toArray()
  218.         );
  219.         $view = new CatalogProductView(
  220.             $this->createProductView($product$locale),
  221.             $variants
  222.         );
  223.         return array_reduce(
  224.             iterator_to_array($this->catalogProductViewProcessors),
  225.             static fn($catalogProductViewProcessor) => $catalogProductViewProcessor->processCatalogProductView($view$product$customer$locale),
  226.             $view
  227.         );
  228.     }
  229.     public function createCatalogProductVariantView(ProductVariant $productVariant, array $options, ?Customer $customerstring $currencystring $locale): CatalogProductVariantView
  230.     {
  231.         $optionManager $this->container->get(OptionManager::class);
  232.         $optionPresenter $this->container->get(OptionPresenter::class);
  233.         $stockManager $this->container->get(StockManagerInterface::class);
  234.         $optionViews = [];
  235.         // $optionViews = array_map(
  236.         //     fn($option) => $optionPresenter->createOptionView($option, $productVariant, $customer, $currency, $locale),
  237.         //     iterator_to_array($optionManager->getOptions($productVariant, $customer))
  238.         // );
  239.         $currencyView $this->container->get(CurrencyPresenter::class)->createCurrencyView($currency$locale);
  240.         $catalogPrice $this->container->get(CatalogPriceCalculator::class)->calculateCatalogPrice(new CatalogPriceQuery($productVariant$customer$currency));
  241.         $basePrice $catalogPrice->getUnitPrice();
  242.         $totalPrice $catalogPrice->getTotalPrice();
  243.         $modifierViews = [];
  244.         foreach ($catalogPrice->getModifiers() as $modifier)
  245.         {
  246.             if ($this->container->get(PriceModifierManager::class)->displayAsSurcharge($modifier->getType(), $customer))
  247.             {
  248.                 $modifierViews[] = new PriceModifierView($modifier->getType(), ''null$modifier->getAmount());
  249.             }
  250.             if (!$this->container->get(PriceModifierManager::class)->includeInItemPrice($modifier->getType(), $customer))
  251.             {
  252.                 $totalPrice -= $modifier->getAmount();
  253.             }
  254.         }
  255.         $inStock $stockManager->isQuantityAvailable($productVariant1null$customer);
  256.         $view = new CatalogProductVariantView(
  257.             $this->createProductVariantView($productVariant$locale),
  258.             $currencyView,
  259.             $basePrice,
  260.             $totalPrice,
  261.             $modifierViews,
  262.             $optionViews,
  263.             $inStock
  264.         );
  265.         return array_reduce(
  266.             iterator_to_array($this->catalogProductVariantViewProcessors),
  267.             static fn($catalogProductVariantViewProcessor) => $catalogProductVariantViewProcessor->processCatalogProductVariantView($view$productVariant$customer$locale),
  268.             $view
  269.         );
  270.     }
  271. }