<?php
namespace MegaParentProductListing\Components\Product\SalesChannel\Listing;
use Shopware\Core\Content\Product\SalesChannel\Detail\AbstractProductDetailRoute;
use Shopware\Core\Content\Product\SalesChannel\Detail\ProductDetailRouteResponse;
use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
use Shopware\Core\Framework\Routing\Annotation\Entity;
use Shopware\Core\Framework\Routing\Annotation\RouteScope;
use Shopware\Core\Framework\Routing\Annotation\Since;
use Shopware\Core\System\SalesChannel\Entity\SalesChannelRepositoryInterface;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
/**
* @RouteScope(scopes={"store-api"})
*/
class ProductDetailRouteDecorator extends AbstractProductDetailRoute
{
private AbstractProductDetailRoute $decoratedService;
private SalesChannelRepositoryInterface $productRepository;
public function __construct(
AbstractProductDetailRoute $decoratedService,
SalesChannelRepositoryInterface $productRepository
)
{
$this->decoratedService = $decoratedService;
$this->productRepository = $productRepository;
}
public function getDecorated(): AbstractProductDetailRoute
{
return $this->decoratedService;
}
/**
* @Since("6.3.2.0")
* @Entity("product")
* @OA\Post(
* path="/product/{productId}",
* summary="Fetch a single product",
* description="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.",
* operationId="readProductDetail",
* tags={"Store API","Product"},
* @OA\Parameter(
* name="productId",
* description="Product ID",
* @OA\Schema(type="string"),
* in="path",
* required=true
* ),
* @OA\Response(
* response="200",
* description="Product information along with variant groups and options",
* @OA\JsonContent(ref="#/components/schemas/ProductDetailResponse")
* )
* )
* @Route("/store-api/product/{productId}", name="store-api.product.detail", methods={"POST"})
*/
public function load(string $productId, Request $request, SalesChannelContext $context, Criteria $criteria): ProductDetailRouteResponse
{
$productId = $this->findBestVariant($productId, $context);
return $this->decoratedService->load($productId, $request, $context, $criteria);
}
private function findBestVariant(string $productId, SalesChannelContext $context): string
{
/** @var SalesChannelProductEntity $product */
$product = $this->productRepository->search(new Criteria([$productId]), $context)->first();
if($product && empty($product->getParentId()) && !empty($product->getMainVariantId())) {
return $product->getMainVariantId();
}
$criteria = (new Criteria())
->addFilter(new EqualsFilter('parentId', $productId))
->addSorting(new FieldSorting('available'))
->addSorting(new FieldSorting('price'))
->addSorting(new FieldSorting('id'))
->setLimit(1);
$variantId = $this->productRepository->searchIds($criteria, $context);
return $variantId->firstId() ?? $productId;
}
}