<?php declare(strict_types=1);
namespace Acris\ProductBadges\Components\ProductBadges;
use Acris\ProductBadges\Components\ProductBadges\Struct\ProductStreamStruct;
use Acris\ProductBadges\Custom\ProductBadgesCollection;
use Acris\ProductBadges\Custom\ProductBadgesEntity;
use Acris\ProductBadges\Storefront\Subscriber\ProductBadgeSubscriber;
use Shopware\Core\Checkout\Promotion\PromotionEntity;
use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity;
use Shopware\Core\Content\ProductStream\ProductStreamCollection;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection;
use Shopware\Core\Framework\Struct\ArrayEntity;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
class ProductBadgesService
{
public const PRODUCT_BADGE_DISPLAY_CONDITION_RULES_AND_DYNAMIC_PRODUCT_GROUPS = 'product_group_and_rule';
public const PRODUCT_BADGE_DISPLAY_CONDITION_PROMOTIONS = 'promotion';
public const PRODUCT_BADGE_DISPLAY_CONDITION_REPLACE_DEFAULT_BADGE = 'replace_default_badges';
/**
* @var ProductBadgesGateway
*/
private $productBadgesGateway;
public function __construct(
ProductBadgesGateway $productBadgesGateway
) {
$this->productBadgesGateway = $productBadgesGateway;
}
public function addBadgesToProducts(EntityCollection $productCollection, SalesChannelContext $salesChannelContext)
{
$productStreamStruct = $this->getAllProductStreams($productCollection, $salesChannelContext->getContext());
$originalProductBadgesCollection = $this->productBadgesGateway->getAllProductBadges($productStreamStruct->getAllProductStreamIds(), $salesChannelContext);
if ($originalProductBadgesCollection->count() === 0) return;
/** @var SalesChannelProductEntity $product */
foreach ($productCollection->getElements() as $product) {
if ($product->hasExtension(ProductBadgeSubscriber::ACRIS_STREAM_IDS_EXTENSION) && !empty($product->getExtension(ProductBadgeSubscriber::ACRIS_STREAM_IDS_EXTENSION))) {
$productStreamIds = $product->getExtension(ProductBadgeSubscriber::ACRIS_STREAM_IDS_EXTENSION)->get('ids');
} else {
$productStreamIds = $productStreamStruct->getProductStreamIdsForProductId($product->getId());
$product->addExtension(ProductBadgeSubscriber::ACRIS_STREAM_IDS_EXTENSION, new ArrayEntity(['ids' => $productStreamIds]));
}
$productBadgesCollection = $this->getProductBadgesForProduct($originalProductBadgesCollection, $productStreamIds);
if ($productBadgesCollection->count() === 0) {
continue;
}
$this->replaceDefaultBadges($productBadgesCollection, $salesChannelContext);
$this->assignProductBadgesToProducts($product, $productBadgesCollection);
}
}
private function assignProductBadgesToProducts(SalesChannelProductEntity $product, EntityCollection $productBadgesCollection)
{
/** @var ProductBadgesEntity $productBadge */
foreach ($productBadgesCollection->getElements() as $productBadge) {
if ($this->checkIndividualPromotionDisplay($product, $productBadge) == false) {
continue;
}
$this->addBadgeToExtension($product, $productBadge);
}
/* We have to proof the sale badge - should be only displayed if product has no promotions or */
/** @var ProductBadgesEntity $productBadge */
foreach ($productBadgesCollection->getElements() as $productBadge) {
if ($productBadge->getIsDefault() !== true || $productBadge->getInternalName() !== 'Sale') {
continue;
}
if ($this->shouldRemoveSaleBadge($product) === true) {
$this->removeBadgeFromExtension($product, $productBadge);
}
}
}
private function addBadgeToExtension(SalesChannelProductEntity $product, ProductBadgesEntity $productBadge): void
{
if ($product->hasExtension('acrisProductBadges')) {
/** @var ProductBadgesCollection $productBadges */
$productBadges = $product->getExtension('acrisProductBadges');
$productBadges->add($productBadge);
} else {
$productBadges = new ProductBadgesCollection();
$productBadges->add($productBadge);
$product->addExtension('acrisProductBadges', $productBadges);
}
}
private function removeBadgeFromExtension(SalesChannelProductEntity $product, ProductBadgesEntity $productBadge): void
{
if ($product->hasExtension('acrisProductBadges')) {
/** @var ProductBadgesCollection $productBadges */
$productBadges = $product->getExtension('acrisProductBadges');
if ($productBadges->has($productBadge->getId())) {
$productBadges->remove($productBadge->getId());
}
}
}
private function checkIndividualPromotionDisplay(SalesChannelProductEntity $product, ProductBadgesEntity $productBadge): bool
{
if ($productBadge->getDisplayCondition() === self::PRODUCT_BADGE_DISPLAY_CONDITION_PROMOTIONS) {
if ($product->hasExtension('acrisPromotion') && $product->getExtension('acrisPromotion') instanceof ArrayEntity) {
$promotionIds = $product->getExtension('acrisPromotion')->get('promotionIds');
foreach ($promotionIds as $promotionId) {
if (in_array($promotionId, $productBadge->getPromotions()->getIds()) === true) {
return true;
}
}
}
return false;
}
return true;
}
private function shouldRemoveSaleBadge(SalesChannelProductEntity $product): bool
{
// in a second step we add the sale badge again
if ($product->hasExtension('acrisPromotion') === false || !$product->getExtension('acrisPromotion') instanceof ArrayEntity || empty($product->getExtension('acrisPromotion')->get('promotionIds')) === true) {
return false;
}
if ($product->hasExtension('acrisProductBadges') !== true || empty($product->getExtension('acrisProductBadges')) === true) {
return false;
}
$promotionIds = $product->getExtension('acrisPromotion')->get('promotionIds');
$productBadges = $product->getExtension('acrisProductBadges');
/** @var ProductBadgesEntity $productBadge */
foreach ($productBadges as $productBadge) {
if ($productBadge->getDisplayCondition() === self::PRODUCT_BADGE_DISPLAY_CONDITION_PROMOTIONS) {
/** @var PromotionEntity $promotion */
foreach ($productBadge->getPromotions() as $promotion) {
if (in_array($promotion->getId(), $promotionIds)) {
if (($key = array_search($promotion->getId(), $promotionIds)) !== false) {
unset($promotionIds[$key]);
}
}
}
}
}
if (empty($promotionIds) === true) {
return true;
}
return false;
}
private function replaceDefaultBadges(EntityCollection $productBadgesCollection, SalesChannelContext $salesChannelContext): void
{
if (!$productBadgesCollection instanceof ProductBadgesCollection) return;
foreach ($productBadgesCollection->getElements() as $productBadge) {
if ($productBadge->getDisplayCondition() === self::PRODUCT_BADGE_DISPLAY_CONDITION_REPLACE_DEFAULT_BADGE && !empty($productBadge->getReplaceDefaultBadgeId()) && $productBadgesCollection->has($productBadge->getReplaceDefaultBadgeId()) && $productBadge->getId() !== $productBadge->getReplaceDefaultBadgeId()) {
$defaultBadge = $productBadgesCollection->get($productBadge->getReplaceDefaultBadgeId());
$rulesExists = $productBadge->getReplaceRules()->count() <= 0;
if ($productBadge->getReplaceRules()->count() > 0) {
foreach ($productBadge->getReplaceRules() as $rule) {
if (in_array($rule->getId(), $salesChannelContext->getRuleIds())) $rulesExists = true;
}
}
if ($rulesExists) {
$productBadge->setDefaultType($defaultBadge->getDefaultType());
$productBadge->setIsDefault(true);
$productBadge->setInternalName($defaultBadge->getInternalName());
$productBadgesCollection->remove($productBadge->getReplaceDefaultBadgeId());
}
if ($productBadge->getActive() !== true || $defaultBadge->getActive() !== true || $rulesExists !== true) $productBadgesCollection->remove($productBadge->getId());
} else {
if ($productBadge->getActive() !== true) $productBadgesCollection->remove($productBadge->getId());
}
}
}
private function getAllProductStreams(EntityCollection $productCollection, Context $context): ProductStreamStruct
{
$productStreamStruct = new ProductStreamStruct();
$productIds = [];
/** @var SalesChannelProductEntity $product */
foreach ($productCollection->getElements() as $product) {
if ($product->hasExtension(ProductBadgeSubscriber::ACRIS_STREAM_IDS_EXTENSION)) {
$productStreamStruct->addProductStreamIds($product->getId(), $product->getExtension(ProductBadgeSubscriber::ACRIS_STREAM_IDS_EXTENSION)->get('ids'));
// new way of ACRIS - the association is directly added to the product criteria by sales_channel.product.process.criteria
} elseif($product->getStreams() instanceof ProductStreamCollection) {
$productStreamStruct->addProductStreamIds($product->getId(), $product->getStreams()->getIds());
} else {
$productIds[] = $product->getId();
}
}
if (!empty($productIds)) {
$this->productBadgesGateway->getProductStreamIds($productStreamStruct, $productIds, $context);
}
return $productStreamStruct;
}
private function getProductBadgesForProduct(EntityCollection $originalCollection, array $productStreamIds): EntityCollection
{
return $originalCollection->filter(
function (ProductBadgesEntity $productBadge) use ($productStreamIds) {
if ($productBadge->getDisplayCondition() !== self::PRODUCT_BADGE_DISPLAY_CONDITION_RULES_AND_DYNAMIC_PRODUCT_GROUPS) return true;
if (empty($productBadge->getProductStreams()) || $productBadge->getProductStreams()->count() === 0) return false;
return count(array_intersect($productBadge->getProductStreams()->getIds(), $productStreamIds)) > 0;
}
);
}
}