vendor/shopware/core/Content/Product/SalesChannel/Detail/ProductDetailRoute.php line 98

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Content\Product\SalesChannel\Detail;
  3. use OpenApi\Annotations as OA;
  4. use Shopware\Core\Content\Category\Service\CategoryBreadcrumbBuilder;
  5. use Shopware\Core\Content\Product\Aggregate\ProductVisibility\ProductVisibilityDefinition;
  6. use Shopware\Core\Content\Product\Exception\ProductNotFoundException;
  7. use Shopware\Core\Content\Product\SalesChannel\ProductAvailableFilter;
  8. use Shopware\Core\Content\Product\SalesChannel\ProductCloseoutFilter;
  9. use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity;
  10. use Shopware\Core\Framework\DataAbstractionLayer\Exception\InconsistentCriteriaIdsException;
  11. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
  14. use Shopware\Core\Framework\Plugin\Exception\DecorationPatternException;
  15. use Shopware\Core\Framework\Routing\Annotation\Entity;
  16. use Shopware\Core\Framework\Routing\Annotation\RouteScope;
  17. use Shopware\Core\Framework\Routing\Annotation\Since;
  18. use Shopware\Core\System\SalesChannel\Entity\SalesChannelRepositoryInterface;
  19. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  20. use Shopware\Core\System\SystemConfig\SystemConfigService;
  21. use Symfony\Component\HttpFoundation\Request;
  22. use Symfony\Component\Routing\Annotation\Route;
  23. /**
  24.  * @RouteScope(scopes={"store-api"})
  25.  */
  26. class ProductDetailRoute extends AbstractProductDetailRoute
  27. {
  28.     /**
  29.      * @var SalesChannelRepositoryInterface
  30.      */
  31.     private $repository;
  32.     /**
  33.      * @var SystemConfigService
  34.      */
  35.     private $config;
  36.     /**
  37.      * @var ProductConfiguratorLoader
  38.      */
  39.     private $configuratorLoader;
  40.     /**
  41.      * @var CategoryBreadcrumbBuilder
  42.      */
  43.     private $breadcrumbBuilder;
  44.     public function __construct(
  45.         SalesChannelRepositoryInterface $repository,
  46.         SystemConfigService $config,
  47.         ProductConfiguratorLoader $configuratorLoader,
  48.         CategoryBreadcrumbBuilder $breadcrumbBuilder
  49.     ) {
  50.         $this->repository $repository;
  51.         $this->config $config;
  52.         $this->configuratorLoader $configuratorLoader;
  53.         $this->breadcrumbBuilder $breadcrumbBuilder;
  54.     }
  55.     public function getDecorated(): AbstractProductDetailRoute
  56.     {
  57.         throw new DecorationPatternException(self::class);
  58.     }
  59.     /**
  60.      * @Since("6.3.2.0")
  61.      * @Entity("product")
  62.      * @OA\Post(
  63.      *      path="/product/{productId}",
  64.      *      summary="This route is used to load a single product with the corresponding details. In addition to loading the data, the best variant of the product is determined when a parent id is passed.",
  65.      *      operationId="readProductDetail",
  66.      *      tags={"Store API","Product"},
  67.      *      @OA\Parameter(name="productId", description="Product ID", @OA\Schema(type="string"), in="path", required=true),
  68.      *      @OA\Response(
  69.      *          response="200",
  70.      *          description="Found product",
  71.      *          @OA\JsonContent(ref="#/components/schemas/product_flat")
  72.      *     )
  73.      * )
  74.      * @Route("/store-api/v{version}/product/{productId}", name="store-api.product.detail", methods={"POST"})
  75.      */
  76.     public function load(string $productIdRequest $requestSalesChannelContext $contextCriteria $criteria): ProductDetailRouteResponse
  77.     {
  78.         $productId $this->findBestVariant($productId$context);
  79.         $this->addFilters($context$criteria);
  80.         $criteria->setIds([$productId]);
  81.         $product $this->repository
  82.             ->search($criteria$context)
  83.             ->first();
  84.         if (!$product instanceof SalesChannelProductEntity) {
  85.             throw new ProductNotFoundException($productId);
  86.         }
  87.         $product->setSeoCategory(
  88.             $this->breadcrumbBuilder->getProductSeoCategory($product$context)
  89.         );
  90.         $configurator $this->configuratorLoader->load($product$context);
  91.         return new ProductDetailRouteResponse($product$configurator);
  92.     }
  93.     private function addFilters(SalesChannelContext $contextCriteria $criteria): void
  94.     {
  95.         $criteria->addFilter(
  96.             new ProductAvailableFilter($context->getSalesChannel()->getId(), ProductVisibilityDefinition::VISIBILITY_LINK)
  97.         );
  98.         $salesChannelId $context->getSalesChannel()->getId();
  99.         $hideCloseoutProductsWhenOutOfStock $this->config->get('core.listing.hideCloseoutProductsWhenOutOfStock'$salesChannelId);
  100.         if ($hideCloseoutProductsWhenOutOfStock) {
  101.             $filter = new ProductCloseoutFilter();
  102.             $filter->addQuery(new EqualsFilter('product.parentId'null));
  103.             $criteria->addFilter($filter);
  104.         }
  105.     }
  106.     /**
  107.      * @throws InconsistentCriteriaIdsException
  108.      */
  109.     private function findBestVariant(string $productIdSalesChannelContext $context): string
  110.     {
  111.         $criteria = (new Criteria())
  112.             ->addFilter(new EqualsFilter('product.parentId'$productId))
  113.             ->addSorting(new FieldSorting('product.price'))
  114.             ->addSorting(new FieldSorting('product.available'))
  115.             ->setLimit(1);
  116.         $variantId $this->repository->searchIds($criteria$context);
  117.         if (\count($variantId->getIds()) > 0) {
  118.             return $variantId->getIds()[0];
  119.         }
  120.         return $productId;
  121.     }
  122. }