<?php
namespace Klikensteen\ThemeCustomChanges\Subscriber;
use Shopware\Core\Content\Product\ProductEntity;
use Shopware\Core\Content\Property\PropertyGroupCollection;
use Shopware\Core\System\SalesChannel\Entity\SalesChannelRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Storefront\Page\Product\ProductPageLoadedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Shopware\Core\Framework\Context;
/**
* Class AllVariantDetailPage
*
* Used to collect the information of the variants
*/
class KlikensteenAllVariantDetailPage implements EventSubscriberInterface
{
/**
* @var SalesChannelRepository
*/
private $productRepository;
/**
* @var ContainerInterface
*/
protected $container;
/**
* AllVariantDetailPage constructor.
*
* @param SalesChannelRepository $productRepository
*/
public function __construct(
SalesChannelRepository $productRepository,
ContainerInterface $container
)
{
$this->productRepository = $productRepository;
$this->container = $container;
}
/**
* @return string[]
*/
public static function getSubscribedEvents(): array
{
return [
ProductPageLoadedEvent::class => 'addAllVariantToDetailPage'
];
}
/**
* Used to prepare the information of the variants for the overview table.
*
* @param ProductPageLoadedEvent $event
*/
public function addAllVariantToDetailPage(ProductPageLoadedEvent $event): void
{
$salesChannelContext = $event->getSalesChannelContext();
$taxState = $salesChannelContext->getTaxState();
// Determines the current product
$product = $event
->getPage()
->getProduct()
;
// Check for a parent product and skip if it's not a variant
$parentId = $product->getParentId();
if (empty($parentId)) {
$totalTax = 0;
foreach(($product->getCalculatedPrice()->getCalculatedTaxes()->getElements()) as $tax) {
$totalTax +=$tax->getTax();
}
$event->getPage()->assign([
'KlikensteenCalculatedPrice'=> $product->getCalculatedPrice(),
'KlikensteenCalculatedPriceTax'=> $totalTax
]);
return;
}
// Get all siblings (products with the same parent)
$siblings = $this->getSiblings(
$parentId,
$event->getSalesChannelContext()
);
// If the siblings are empty, the method can be exited
if (empty($siblings)) {
return;
}
// Determines the required data of the variants and writes them into the $variants array
$variants = [];
foreach ($siblings->getElements() as $index => $sibling) {
if (!$sibling->getActive()) {
continue;
}
// The stock is determined.
// Set to true by default.
// If there is no more available stock it is set to false.
$stock = true;
if ($sibling->getAvailableStock() < 1) {
$stock = false;
}
// Determines the price, base price, the unit and the name of the unit
$referencePrice = 0;
$referenceUnitName = '';
$referenceUnit = 1;
$price = $sibling->getCalculatedPrice()->getTotalPrice();
if (null != $sibling->getCalculatedPrice()->getReferencePrice()) {
$referencePrice = $sibling->getCalculatedPrice()->getReferencePrice()->getPrice();
$referenceUnitName = $sibling->getCalculatedPrice()->getReferencePrice()->getUnitName();
$referenceUnit = $sibling->getCalculatedPrice()->getReferencePrice()->getReferenceUnit();
}
// The names of the variant options are determined.
$variantOptions = [];
foreach ($sibling->getVariation() as $variation) {
$variantOptions[] = $variation['option'];
}
// The url of the image is determined. If there is no image, null is returned.
$image = null;
$altTag = null;
foreach ($sibling->getMedia()->getElements() as $media) {
if ($sibling->getCoverId() == $media->getId()) {
$image = $media->getMedia()->getUrl();
}
if (!empty($media->getMedia()->getAlt())) {
$altTag = $media->getMedia()->getAlt();
} else {
// Artikelnummer
$altTag = $sibling->getProductNumber();
}
}
// The delivery time is determined.
$deliveryTime = null;
if (null != $sibling->getDeliveryTime()){
$deliveryTime = $sibling->getDeliveryTime()->getName();
}
// Determines the position of the variant in the order
// in which the variants should later appear
$positions = $this->getPosition($event->getPage()->getConfiguratorSettings());
$position = 0;
foreach($sibling->getOptions() as $option) {
foreach ($positions as $index => $positionId) {
if ($option->getId() == $index) {
$position = $position.$positionId;
}
}
}
// All information is written to the array
$variants[$position] = [
'media' => $image,
'altTag' => $altTag,
'productNumber' => $sibling->getProductNumber(),
'manufacturerNumber' => $sibling->getManufacturerNumber(),
'variation' => null,
'price' => $price,
'referenceUnitPrice' => $referencePrice,
'referenceUnitName' => $referenceUnitName,
'referenceUnit' => $referenceUnit,
'inStock' => $stock,
'options' => $variantOptions,
'deliveryTime' => $deliveryTime,
'id' => $sibling->getId(),
'available' => $sibling->getAvailable(),
'minPurchase' => $sibling->getMinPurchase(),
'purchaseSteps' => $sibling->getPurchaseSteps(),
'availableStock' => $sibling->getAvailableStock(),
'translated' => $sibling->getTranslated(),
'restockTime' => $sibling->getRestockTime(),
'deliveryTimeTranslation' => $deliveryTime,
'priceList'=>$sibling->getCalculatedPrice(),
'pricesFullList'=> $this->getRulePrices( $parentId),
'priceRuleData'=> json_decode($this->getRulePrices( $parentId)),
'minimum_range'=> $this->getMinimumPrice($parentId,$taxState),
'taxState'=> $taxState
];
}
// Sorts the variants by position to match the order of the Configurator options
ksort($variants);
// The array is passed to the page and can be found at page.extensions.allVariants.
$event
->getPage()
->assign(['KlikensteenAllVariants'=> $variants]);
}
/**
* Determines the variants and all associated information based on the passed parentId.
*
* @param string $parent
* @param SalesChannelContext $context
* @return ProductEntity|null
*/
private function getSiblings(
string $parent,
SalesChannelContext $context
): ?EntitySearchResult {
$criteria = new Criteria();
$criteria
->addFilter(new EqualsFilter('parentId', $parent))
->addAssociation('manufacturer.media')
->addAssociation('options.group')
->addAssociation('properties.group')
->addAssociation('mainCategories.category')
->addAssociation('deliveryTime.deliveryTime')
->getAssociation('media');
return $this
->productRepository
->search($criteria, $context);
}
/**
* Sets the order of the Configurator options
*
* @param PropertyGroupCollection $configSettings
* @return array
*/
private function getPosition(PropertyGroupCollection $configSettings): array {
$positions = [];
$i = 1;
foreach ($configSettings as $configSetting) {
foreach ($configSetting->getOptions() as $index => $option) {
$positions[$index] = $i;
$i++;
}
}
return $positions;
}
private function getRulePrices($prodId ) {
$criteria = (new Criteria())->addFilter(new EqualsFilter('product_price.productId', $prodId));
$result = $this->container->get('product_price.repository')->search($criteria, Context::createDefaultContext());
$response = [];
foreach($result->getElements() as $prices) {
$amounts = $prices->getPrice()->getElements();
foreach($amounts as $amount) {
$amt = [
'net' => $amount->getNet(),
'gross' => $amount->getGross(),
];
}
$response[] = [
'quantityStart' => $prices->getQuantityStart(),
'quantityEnd' => $prices->getQuantityEnd(),
'net' => $amt['net'],
'gross' => $amt['gross'],
];
}
return !empty($response) ? json_encode($response) : false;
}
private function getMinimumPrice($prodId, $taxState) {
$criteria = (new Criteria())->addFilter(new EqualsFilter('product_price.productId', $prodId));
$result = $this->container->get('product_price.repository')->search($criteria, Context::createDefaultContext());
$response = [];
foreach($result->getElements() as $prices) {
$amounts = $prices->getPrice()->getElements();
foreach($amounts as $amount) {
$amt = [
'net' => $amount->getNet(),
'gross' => $amount->getGross(),
];
}
$start = $prices->getQuantityStart()>0?$prices->getQuantityStart():0;
$response[$start] = [
'quantityStart' => $prices->getQuantityStart(),
'quantityEnd' => $prices->getQuantityEnd(),
'net' => $amt['net'],
'gross' => $amt['gross'],
];
}
usort($response, function ($a, $b) {
return $a['quantityStart'] - $b['quantityStart'];
});
if($taxState == 'net') {
return isset($response[0]['net']) ? $response[0]['net'] : null;
}
return isset($response[0]['gross']) ? $response[0]['gross'] : null;
}
}