[DX] Open up variant map providers to different criteria than just the channel

This commit is contained in:
Jan Goralski 2023-04-06 15:41:24 +02:00
parent c75523ad1e
commit b7661ab725
No known key found for this signature in database
GPG key ID: 95D91BA380F31EDD
16 changed files with 130 additions and 73 deletions

View file

@ -13,7 +13,6 @@ declare(strict_types=1);
namespace Sylius\Bundle\CoreBundle\Templating\Helper;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Core\Model\ProductInterface;
use Sylius\Component\Core\Provider\ProductVariantMap\ProductVariantsMapProviderInterface;
use Symfony\Component\Templating\Helper\Helper;
@ -24,9 +23,9 @@ class ProductVariantsMapHelper extends Helper
{
}
public function getMap(ProductInterface $product, ChannelInterface $channel): array
public function getMap(ProductInterface $product, array $context): array
{
return $this->productVariantsMapProvider->provide($product, $channel);
return $this->productVariantsMapProvider->provide($product, $context);
}
public function getName(): string

View file

@ -1,5 +1,5 @@
{% if product.isConfigurable() and product.getVariantSelectionMethod() == 'match' and not product.enabledVariants.empty() %}
{% include '@SyliusShop/Product/Show/_variantsPricing.html.twig' with {'pricing': sylius_product_variants_map(product, sylius.channel), 'variants': product.enabledVariants} %}
{% include '@SyliusShop/Product/Show/_variantsPricing.html.twig' with {'pricing': sylius_product_variants_map(product, {'channel': sylius.channel}), 'variants': product.enabledVariants} %}
{% endif %}
{% include '@SyliusShop/Product/Show/_inventory.html.twig' %}

View file

@ -18,15 +18,19 @@ use Sylius\Component\Core\Model\ProductVariantInterface;
final class ProductVariantAppliedPromotionsMapProvider implements ProductVariantMapProviderInterface
{
public function provide(ProductVariantInterface $variant, ChannelInterface $channel): array
public function provide(ProductVariantInterface $variant, array $context): array
{
return [
'applied_promotions' => $variant->getAppliedPromotionsForChannel($channel)->toArray(),
'applied_promotions' => $variant->getAppliedPromotionsForChannel($context['channel'])->toArray(),
];
}
public function supports(ProductVariantInterface $variant, ChannelInterface $channel): bool
public function supports(ProductVariantInterface $variant, array $context): bool
{
return !$variant->getAppliedPromotionsForChannel($channel)->isEmpty();
return
isset($context['channel']) &&
$context['channel'] instanceof ChannelInterface &&
!$variant->getAppliedPromotionsForChannel($context['channel'])->isEmpty()
;
}
}

View file

@ -23,18 +23,20 @@ final class ProductVariantLowestPriceMapProvider implements ProductVariantMapPro
{
}
public function provide(ProductVariantInterface $variant, ChannelInterface $channel): array
public function provide(ProductVariantInterface $variant, array $context): array
{
return [
'lowest-price-before-discount' => $this->calculator->calculateLowestPriceBeforeDiscount($variant, ['channel' => $channel]),
'lowest-price-before-discount' => $this->calculator->calculateLowestPriceBeforeDiscount($variant, $context),
];
}
public function supports(ProductVariantInterface $variant, ChannelInterface $channel): bool
public function supports(ProductVariantInterface $variant, array $context): bool
{
return
null !== $variant->getChannelPricingForChannel($channel) &&
null !== $this->calculator->calculateLowestPriceBeforeDiscount($variant, ['channel' => $channel])
isset($context['channel']) &&
$context['channel'] instanceof ChannelInterface &&
null !== $variant->getChannelPricingForChannel($context['channel']) &&
null !== $this->calculator->calculateLowestPriceBeforeDiscount($variant, $context)
;
}
}

View file

@ -13,12 +13,11 @@ declare(strict_types=1);
namespace Sylius\Component\Core\Provider\ProductVariantMap;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Core\Model\ProductVariantInterface;
interface ProductVariantMapProviderInterface
{
public function provide(ProductVariantInterface $variant, ChannelInterface $channel): array;
public function provide(ProductVariantInterface $variant, array $context): array;
public function supports(ProductVariantInterface $variant, ChannelInterface $channel): bool;
public function supports(ProductVariantInterface $variant, array $context): bool;
}

View file

@ -13,12 +13,11 @@ declare(strict_types=1);
namespace Sylius\Component\Core\Provider\ProductVariantMap;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Core\Model\ProductVariantInterface;
final class ProductVariantOptionsMapProvider implements ProductVariantMapProviderInterface
{
public function provide(ProductVariantInterface $variant, ChannelInterface $channel): array
public function provide(ProductVariantInterface $variant, array $context): array
{
$data = [];
foreach ($variant->getOptionValues() as $optionValue) {
@ -28,7 +27,7 @@ final class ProductVariantOptionsMapProvider implements ProductVariantMapProvide
return $data;
}
public function supports(ProductVariantInterface $variant, ChannelInterface $channel): bool
public function supports(ProductVariantInterface $variant, array $context): bool
{
return !$variant->getOptionValues()->isEmpty();
}

View file

@ -23,26 +23,28 @@ final class ProductVariantOriginalPriceMapProvider implements ProductVariantMapP
{
}
public function provide(ProductVariantInterface $variant, ChannelInterface $channel): array
public function provide(ProductVariantInterface $variant, array $context): array
{
return [
'original-price' => $this->calculator->calculateOriginal($variant, ['channel' => $channel]),
'original-price' => $this->calculator->calculateOriginal($variant, $context),
];
}
public function supports(ProductVariantInterface $variant, ChannelInterface $channel): bool
public function supports(ProductVariantInterface $variant, array $context): bool
{
return
null !== $variant->getChannelPricingForChannel($channel) &&
$this->isPriceLowerThanOriginalPrice($variant, $channel)
isset($context['channel']) &&
$context['channel'] instanceof ChannelInterface &&
null !== $variant->getChannelPricingForChannel($context['channel']) &&
$this->isPriceLowerThanOriginalPrice($variant, $context)
;
}
private function isPriceLowerThanOriginalPrice(ProductVariantInterface $variant, ChannelInterface $channel): bool
private function isPriceLowerThanOriginalPrice(ProductVariantInterface $variant, array $context): bool
{
return
$this->calculator->calculate($variant, ['channel' => $channel]) <
$this->calculator->calculateOriginal($variant, ['channel' => $channel])
$this->calculator->calculate($variant, $context) <
$this->calculator->calculateOriginal($variant, $context)
;
}
}

View file

@ -23,15 +23,19 @@ final class ProductVariantPriceMapProvider implements ProductVariantMapProviderI
{
}
public function provide(ProductVariantInterface $variant, ChannelInterface $channel): array
public function provide(ProductVariantInterface $variant, array $context): array
{
return [
'value' => $this->calculator->calculate($variant, ['channel' => $channel]),
'value' => $this->calculator->calculate($variant, $context),
];
}
public function supports(ProductVariantInterface $variant, ChannelInterface $channel): bool
public function supports(ProductVariantInterface $variant, array $context): bool
{
return null !== $variant->getChannelPricingForChannel($channel);
return
isset($context['channel']) &&
$context['channel'] instanceof ChannelInterface &&
null !== $variant->getChannelPricingForChannel($context['channel'])
;
}
}

View file

@ -13,7 +13,6 @@ declare(strict_types=1);
namespace Sylius\Component\Core\Provider\ProductVariantMap;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Core\Model\ProductInterface;
use Sylius\Component\Core\Model\ProductVariantInterface;
@ -24,25 +23,25 @@ final class ProductVariantsMapProvider implements ProductVariantsMapProviderInte
{
}
public function provide(ProductInterface $product, ChannelInterface $channel): array
public function provide(ProductInterface $product, array $context): array
{
$variantsMap = [];
/** @var ProductVariantInterface $variant */
foreach ($product->getEnabledVariants() as $variant) {
$variantsMap[] = $this->getMapForVariant($variant, $channel);
$variantsMap[] = $this->getMapForVariant($variant, $context);
}
return $variantsMap;
}
private function getMapForVariant(ProductVariantInterface $variant, ChannelInterface $channel): array
private function getMapForVariant(ProductVariantInterface $variant, array $context): array
{
$data = [];
foreach ($this->dataMapProviders as $dataMapProvider) {
if ($dataMapProvider->supports($variant, $channel)) {
$data += $dataMapProvider->provide($variant, $channel);
if ($dataMapProvider->supports($variant, $context)) {
$data += $dataMapProvider->provide($variant, $context);
}
}

View file

@ -13,10 +13,9 @@ declare(strict_types=1);
namespace Sylius\Component\Core\Provider\ProductVariantMap;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Core\Model\ProductInterface;
interface ProductVariantsMapProviderInterface
{
public function provide(ProductInterface $product, ChannelInterface $channel): array;
public function provide(ProductInterface $product, array $context): array;
}

View file

@ -27,6 +27,18 @@ final class ProductVariantAppliedPromotionsMapProviderSpec extends ObjectBehavio
$this->shouldImplement(ProductVariantMapProviderInterface::class);
}
function it_does_not_support_context_with_no_channel(
ProductVariantInterface $variant,
): void {
$this->supports($variant, [])->shouldReturn(false);
}
function it_does_not_support_context_with_channel_that_is_not_a_channel_interface(
ProductVariantInterface $variant,
): void {
$this->supports($variant, ['channel' => 'not_a_channel'])->shouldReturn(false);
}
function it_supports_variants_with_applied_promotions(
ChannelInterface $channel,
ProductVariantInterface $variantWithoutPromotions,
@ -38,8 +50,8 @@ final class ProductVariantAppliedPromotionsMapProviderSpec extends ObjectBehavio
]));
$variantWithoutPromotions->getAppliedPromotionsForChannel($channel)->willReturn(new ArrayCollection());
$this->supports($variantWithPromotions, $channel)->shouldReturn(true);
$this->supports($variantWithoutPromotions, $channel)->shouldReturn(false);
$this->supports($variantWithPromotions, ['channel' => $channel])->shouldReturn(true);
$this->supports($variantWithoutPromotions, ['channel' => $channel])->shouldReturn(false);
}
function it_provides_a_map_of_variant_applied_promotions(
@ -53,7 +65,7 @@ final class ProductVariantAppliedPromotionsMapProviderSpec extends ObjectBehavio
$secondPromotion->getWrappedObject(),
]));
$this->provide($variant, $channel)->shouldIterateLike([
$this->provide($variant, ['channel' => $channel])->shouldIterateLike([
'applied_promotions' => [
$firstPromotion,
$secondPromotion,

View file

@ -32,16 +32,31 @@ final class ProductVariantLowestPriceMapProviderSpec extends ObjectBehavior
$this->shouldImplement(ProductVariantMapProviderInterface::class);
}
function it_does_not_support_context_with_no_channel(
ProductVariantInterface $variant,
): void {
$this->supports($variant, [])->shouldReturn(false);
}
function it_does_not_support_context_with_channel_that_is_not_a_channel_interface(
ProductVariantInterface $variant,
): void {
$this->supports($variant, ['channel' => 'not_a_channel'])->shouldReturn(false);
}
function it_supports_variants_with_lowest_price_in_channel(
ProductVariantPricesCalculatorInterface $calculator,
ChannelInterface $channel,
ProductVariantInterface $variant,
ProductVariantInterface $variantWithoutChannelPricing,
ProductVariantInterface $variantWithChannelPricing,
ChannelPricingInterface $channelPricing,
): void {
$variant->getChannelPricingForChannel($channel)->willReturn($channelPricing);
$calculator->calculateLowestPriceBeforeDiscount($variant, ['channel' => $channel])->willReturn(1000);
$variantWithoutChannelPricing->getChannelPricingForChannel($channel)->willReturn(null);
$variantWithChannelPricing->getChannelPricingForChannel($channel)->willReturn($channelPricing);
$calculator->calculateLowestPriceBeforeDiscount($variantWithChannelPricing, ['channel' => $channel])->willReturn(1000);
$this->supports($variant, $channel)->shouldReturn(true);
$this->supports($variantWithChannelPricing, ['channel' => $channel])->shouldReturn(true);
$this->supports($variantWithoutChannelPricing, ['channel' => $channel])->shouldReturn(false);
}
function it_provides_lowest_price_of_variant_in_channel(
@ -51,7 +66,7 @@ final class ProductVariantLowestPriceMapProviderSpec extends ObjectBehavior
): void {
$calculator->calculateLowestPriceBeforeDiscount($variant, ['channel' => $channel])->willReturn(1000);
$this->provide($variant, $channel)->shouldIterateLike([
$this->provide($variant, ['channel' => $channel])->shouldIterateLike([
'lowest-price-before-discount' => 1000,
]);
}

View file

@ -38,8 +38,8 @@ final class ProductVariantOptionsMapProviderSpec extends ObjectBehavior
]));
$variantWithoutOptions->getOptionValues()->willReturn(new ArrayCollection());
$this->supports($variantWithOptions, $channel)->shouldReturn(true);
$this->supports($variantWithoutOptions, $channel)->shouldReturn(false);
$this->supports($variantWithOptions, [])->shouldReturn(true);
$this->supports($variantWithoutOptions, [])->shouldReturn(false);
}
function it_provides_a_map_of_variant_options(
@ -58,7 +58,7 @@ final class ProductVariantOptionsMapProviderSpec extends ObjectBehavior
$secondOptionValue->getWrappedObject(),
]));
$this->provide($variant, $channel)->shouldIterateLike([
$this->provide($variant, [])->shouldIterateLike([
'first_option' => 'first_option_value',
'second_option' => 'second_option_value',
]);

View file

@ -32,13 +32,25 @@ final class ProductVariantOriginalPriceMapProviderSpec extends ObjectBehavior
$this->shouldImplement(ProductVariantMapProviderInterface::class);
}
function it_does_not_support_context_with_no_channel(
ProductVariantInterface $variant,
): void {
$this->supports($variant, [])->shouldReturn(false);
}
function it_does_not_support_context_with_channel_that_is_not_a_channel_interface(
ProductVariantInterface $variant,
): void {
$this->supports($variant, ['channel' => 'not_a_channel'])->shouldReturn(false);
}
function it_does_not_support_variants_with_no_channel_pricing_in_channel(
ChannelInterface $channel,
ProductVariantInterface $variant,
): void {
$variant->getChannelPricingForChannel($channel)->willReturn(null);
$this->supports($variant, $channel)->shouldReturn(false);
$this->supports($variant, ['channel' => $channel])->shouldReturn(false);
}
function it_does_not_support_variants_with_price_equal_original_price(
@ -51,7 +63,7 @@ final class ProductVariantOriginalPriceMapProviderSpec extends ObjectBehavior
$calculator->calculate($variant, ['channel' => $channel])->willReturn(1000);
$calculator->calculateOriginal($variant, ['channel' => $channel])->willReturn(1000);
$this->supports($variant, $channel)->shouldReturn(false);
$this->supports($variant, ['channel' => $channel])->shouldReturn(false);
}
function it_does_not_support_variants_with_price_greater_than_original_price(
@ -64,7 +76,7 @@ final class ProductVariantOriginalPriceMapProviderSpec extends ObjectBehavior
$calculator->calculate($variant, ['channel' => $channel])->willReturn(1200);
$calculator->calculateOriginal($variant, ['channel' => $channel])->willReturn(1000);
$this->supports($variant, $channel)->shouldReturn(false);
$this->supports($variant, ['channel' => $channel])->shouldReturn(false);
}
function it_supports_variants_with_price_lower_than_original_price(
@ -77,7 +89,7 @@ final class ProductVariantOriginalPriceMapProviderSpec extends ObjectBehavior
$calculator->calculate($variant, ['channel' => $channel])->willReturn(1000);
$calculator->calculateOriginal($variant, ['channel' => $channel])->willReturn(1200);
$this->supports($variant, $channel)->shouldReturn(true);
$this->supports($variant, ['channel' => $channel])->shouldReturn(true);
}
function it_provides_original_price_of_variant_in_channel(
@ -87,6 +99,6 @@ final class ProductVariantOriginalPriceMapProviderSpec extends ObjectBehavior
): void {
$calculator->calculateOriginal($variant, ['channel' => $channel])->willReturn(1200);
$this->provide($variant, $channel)->shouldReturn(['original-price' => 1200]);
$this->provide($variant, ['channel' => $channel])->shouldReturn(['original-price' => 1200]);
}
}

View file

@ -32,6 +32,18 @@ final class ProductVariantPriceMapProviderSpec extends ObjectBehavior
$this->shouldImplement(ProductVariantMapProviderInterface::class);
}
function it_does_not_support_context_with_no_channel(
ProductVariantInterface $variant,
): void {
$this->supports($variant, [])->shouldReturn(false);
}
function it_does_not_support_context_with_channel_that_is_not_a_channel_interface(
ProductVariantInterface $variant,
): void {
$this->supports($variant, ['channel' => 'not_a_channel'])->shouldReturn(false);
}
function it_supports_variants_with_channel_pricing_in_channel(
ChannelInterface $channel,
ProductVariantInterface $variantWithoutChannelPricing,
@ -41,8 +53,8 @@ final class ProductVariantPriceMapProviderSpec extends ObjectBehavior
$variantWithChannelPricing->getChannelPricingForChannel($channel)->willReturn($channelPricing);
$variantWithoutChannelPricing->getChannelPricingForChannel($channel)->willReturn(null);
$this->supports($variantWithChannelPricing, $channel)->shouldReturn(true);
$this->supports($variantWithoutChannelPricing, $channel)->shouldReturn(false);
$this->supports($variantWithChannelPricing, ['channel' => $channel])->shouldReturn(true);
$this->supports($variantWithoutChannelPricing, ['channel' => $channel])->shouldReturn(false);
}
function it_provides_price_of_variant_in_channel(
@ -54,7 +66,7 @@ final class ProductVariantPriceMapProviderSpec extends ObjectBehavior
$variant->getChannelPricingForChannel($channel)->willReturn($channelPricing);
$calculator->calculate($variant, ['channel' => $channel])->willReturn(1000);
$this->provide($variant, $channel)->shouldIterateLike([
$this->provide($variant, ['channel' => $channel])->shouldIterateLike([
'value' => 1000,
]);
}

View file

@ -19,7 +19,6 @@ use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Core\Model\ProductInterface;
use Sylius\Component\Core\Model\ProductVariantInterface;
use Sylius\Component\Core\Provider\ProductVariantMap\ProductVariantMapProviderInterface;
use Sylius\Component\Core\Provider\ProductVariantMap\ProductVariantsMapProvider;
use Sylius\Component\Core\Provider\ProductVariantMap\ProductVariantsMapProviderInterface;
final class ProductVariantsMapProviderSpec extends ObjectBehavior
@ -55,33 +54,33 @@ final class ProductVariantsMapProviderSpec extends ObjectBehavior
$secondVariant->getWrappedObject(),
]));
$firstProvider->supports($firstVariant, $channel)->willReturn(true);
$firstProvider->provide($firstVariant, $channel)->willReturn([
$firstProvider->supports($firstVariant, ['channel' => $channel])->willReturn(true);
$firstProvider->provide($firstVariant, ['channel' => $channel])->willReturn([
'first-first' => ['some'],
]);
$secondProvider->supports($firstVariant, $channel)->willReturn(false);
$secondProvider->provide($firstVariant, $channel)->shouldNotBeCalled();
$secondProvider->supports($firstVariant, ['channel' => $channel])->willReturn(false);
$secondProvider->provide($firstVariant, ['channel' => $channel])->shouldNotBeCalled();
$thirdProvider->supports($firstVariant, $channel)->willReturn(true);
$thirdProvider->provide($firstVariant, $channel)->willReturn([
$thirdProvider->supports($firstVariant, ['channel' => $channel])->willReturn(true);
$thirdProvider->provide($firstVariant, ['channel' => $channel])->willReturn([
'first-third' => ['data'],
]);
$firstProvider->supports($secondVariant, $channel)->willReturn(false);
$firstProvider->provide($secondVariant, $channel)->shouldNotBeCalled();
$firstProvider->supports($secondVariant, ['channel' => $channel])->willReturn(false);
$firstProvider->provide($secondVariant, ['channel' => $channel])->shouldNotBeCalled();
$secondProvider->supports($secondVariant, $channel)->willReturn(true);
$secondProvider->provide($secondVariant, $channel)->willReturn([
$secondProvider->supports($secondVariant, ['channel' => $channel])->willReturn(true);
$secondProvider->provide($secondVariant, ['channel' => $channel])->willReturn([
'second-second' => ['more'],
]);
$thirdProvider->supports($secondVariant, $channel)->willReturn(true);
$thirdProvider->provide($secondVariant, $channel)->willReturn([
$thirdProvider->supports($secondVariant, ['channel' => $channel])->willReturn(true);
$thirdProvider->provide($secondVariant, ['channel' => $channel])->willReturn([
'second-third' => ['data'],
]);
$this->provide($product, $channel)->shouldIterateLike([
$this->provide($product, ['channel' => $channel])->shouldIterateLike([
[
'first-first' => ['some'],
'first-third' => ['data'],
@ -89,7 +88,7 @@ final class ProductVariantsMapProviderSpec extends ObjectBehavior
[
'second-second' => ['more'],
'second-third' => ['data'],
]
],
]);
}
}