From b16430296a8297e4f3e7ec708865279053a5935b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 24 Oct 2025 19:41:03 +0000
Subject: [PATCH 1/5] Initial plan
From 91453f4f3b52671990eebec7052ce74d887021d4 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 24 Oct 2025 19:51:52 +0000
Subject: [PATCH 2/5] Add state management to optimize salesrule attribute
loading
Co-authored-by: ihor-sviziev <1873745+ihor-sviziev@users.noreply.github.com>
---
.../Plugin/QuoteConfigProductAttributes.php | 22 +++++-
.../Model/Plugin/TotalsCollectorPlugin.php | 59 +++++++++++++++
.../Model/Quote/TotalsCollectionState.php | 42 +++++++++++
.../QuoteConfigProductAttributesTest.php | 29 +++++++-
.../Plugin/TotalsCollectorPluginTest.php | 72 +++++++++++++++++++
.../Model/Quote/TotalsCollectionStateTest.php | 56 +++++++++++++++
app/code/Magento/SalesRule/etc/di.xml | 3 +
7 files changed, 280 insertions(+), 3 deletions(-)
create mode 100644 app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php
create mode 100644 app/code/Magento/SalesRule/Model/Quote/TotalsCollectionState.php
create mode 100644 app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php
create mode 100644 app/code/Magento/SalesRule/Test/Unit/Model/Quote/TotalsCollectionStateTest.php
diff --git a/app/code/Magento/SalesRule/Model/Plugin/QuoteConfigProductAttributes.php b/app/code/Magento/SalesRule/Model/Plugin/QuoteConfigProductAttributes.php
index 764658b3ac00a..790fe51ec582d 100644
--- a/app/code/Magento/SalesRule/Model/Plugin/QuoteConfigProductAttributes.php
+++ b/app/code/Magento/SalesRule/Model/Plugin/QuoteConfigProductAttributes.php
@@ -8,6 +8,7 @@
use Magento\Quote\Model\Quote\Config;
use Magento\SalesRule\Model\ResourceModel\Rule as RuleResource;
+use Magento\SalesRule\Model\Quote\TotalsCollectionState;
class QuoteConfigProductAttributes
{
@@ -21,17 +22,29 @@ class QuoteConfigProductAttributes
*/
private $activeAttributeCodes;
+ /**
+ * @var TotalsCollectionState
+ */
+ private $totalsCollectionState;
+
/**
* @param RuleResource $ruleResource
+ * @param TotalsCollectionState $totalsCollectionState
*/
- public function __construct(RuleResource $ruleResource)
- {
+ public function __construct(
+ RuleResource $ruleResource,
+ TotalsCollectionState $totalsCollectionState
+ ) {
$this->ruleResource = $ruleResource;
+ $this->totalsCollectionState = $totalsCollectionState;
}
/**
* Append sales rule product attribute keys to select by quote item collection
*
+ * Only loads attributes when totals collection is in progress to avoid unnecessary
+ * database queries and EAV attribute loading on every cart view.
+ *
* @param Config $subject
* @param array $attributeKeys
*
@@ -40,6 +53,11 @@ public function __construct(RuleResource $ruleResource)
*/
public function afterGetProductAttributes(Config $subject, array $attributeKeys): array
{
+ // Only load salesrule attributes when actually collecting totals
+ if (!$this->totalsCollectionState->isCollecting()) {
+ return $attributeKeys;
+ }
+
if ($this->activeAttributeCodes === null) {
$this->activeAttributeCodes = array_column($this->ruleResource->getActiveAttributes(), 'attribute_code');
}
diff --git a/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php b/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php
new file mode 100644
index 0000000000000..d5e0754ee57c9
--- /dev/null
+++ b/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php
@@ -0,0 +1,59 @@
+totalsCollectionState = $totalsCollectionState;
+ }
+
+ /**
+ * Set flag before totals collection starts
+ *
+ * @param TotalsCollector $subject
+ * @param Quote $quote
+ * @return array
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function beforeCollect(TotalsCollector $subject, Quote $quote): array
+ {
+ $this->totalsCollectionState->setIsCollecting(true);
+ return [$quote];
+ }
+
+ /**
+ * Unset flag after totals collection completes
+ *
+ * @param TotalsCollector $subject
+ * @param Total $result
+ * @return Total
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function afterCollect(TotalsCollector $subject, Total $result): Total
+ {
+ $this->totalsCollectionState->setIsCollecting(false);
+ return $result;
+ }
+}
diff --git a/app/code/Magento/SalesRule/Model/Quote/TotalsCollectionState.php b/app/code/Magento/SalesRule/Model/Quote/TotalsCollectionState.php
new file mode 100644
index 0000000000000..9bc80a4a47de2
--- /dev/null
+++ b/app/code/Magento/SalesRule/Model/Quote/TotalsCollectionState.php
@@ -0,0 +1,42 @@
+isCollecting = $state;
+ }
+
+ /**
+ * Check if totals collection is in progress
+ *
+ * @return bool
+ */
+ public function isCollecting(): bool
+ {
+ return $this->isCollecting;
+ }
+}
diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/QuoteConfigProductAttributesTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/QuoteConfigProductAttributesTest.php
index a06584cec2273..92e32281727ee 100644
--- a/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/QuoteConfigProductAttributesTest.php
+++ b/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/QuoteConfigProductAttributesTest.php
@@ -10,6 +10,7 @@
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
use Magento\Quote\Model\Quote\Config;
use Magento\SalesRule\Model\Plugin\QuoteConfigProductAttributes;
+use Magento\SalesRule\Model\Quote\TotalsCollectionState;
use Magento\SalesRule\Model\ResourceModel\Rule;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
@@ -26,15 +27,22 @@ class QuoteConfigProductAttributesTest extends TestCase
*/
protected $ruleResource;
+ /**
+ * @var TotalsCollectionState|MockObject
+ */
+ protected $totalsCollectionState;
+
protected function setUp(): void
{
$objectManager = new ObjectManager($this);
$this->ruleResource = $this->createMock(Rule::class);
+ $this->totalsCollectionState = $this->createMock(TotalsCollectionState::class);
$this->plugin = $objectManager->getObject(
QuoteConfigProductAttributes::class,
[
- 'ruleResource' => $this->ruleResource
+ 'ruleResource' => $this->ruleResource,
+ 'totalsCollectionState' => $this->totalsCollectionState
]
);
}
@@ -45,6 +53,10 @@ public function testAfterGetProductAttributes()
$attributeCode = 'code of the attribute';
$expected = [0 => $attributeCode];
+ $this->totalsCollectionState->expects($this->once())
+ ->method('isCollecting')
+ ->willReturn(true);
+
$this->ruleResource->expects($this->once())
->method('getActiveAttributes')
->willReturn(
@@ -55,4 +67,19 @@ public function testAfterGetProductAttributes()
$this->assertEquals($expected, $this->plugin->afterGetProductAttributes($subject, []));
}
+
+ public function testAfterGetProductAttributesNotCollecting()
+ {
+ $subject = $this->createMock(Config::class);
+ $inputAttributes = ['existing_attribute'];
+
+ $this->totalsCollectionState->expects($this->once())
+ ->method('isCollecting')
+ ->willReturn(false);
+
+ $this->ruleResource->expects($this->never())
+ ->method('getActiveAttributes');
+
+ $this->assertEquals($inputAttributes, $this->plugin->afterGetProductAttributes($subject, $inputAttributes));
+ }
}
diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php
new file mode 100644
index 0000000000000..f626ed5f53417
--- /dev/null
+++ b/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php
@@ -0,0 +1,72 @@
+totalsCollectionState = $this->createMock(TotalsCollectionState::class);
+ $this->totalsCollector = $this->createMock(TotalsCollector::class);
+ $this->quote = $this->createMock(Quote::class);
+
+ $this->plugin = new TotalsCollectorPlugin($this->totalsCollectionState);
+ }
+
+ public function testBeforeCollectSetsState()
+ {
+ $this->totalsCollectionState->expects($this->once())
+ ->method('setIsCollecting')
+ ->with(true);
+
+ $result = $this->plugin->beforeCollect($this->totalsCollector, $this->quote);
+
+ $this->assertEquals([$this->quote], $result);
+ }
+
+ public function testAfterCollectUnsetsState()
+ {
+ $total = $this->createMock(Total::class);
+
+ $this->totalsCollectionState->expects($this->once())
+ ->method('setIsCollecting')
+ ->with(false);
+
+ $result = $this->plugin->afterCollect($this->totalsCollector, $total);
+
+ $this->assertSame($total, $result);
+ }
+}
diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Quote/TotalsCollectionStateTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Quote/TotalsCollectionStateTest.php
new file mode 100644
index 0000000000000..7da89194e6abe
--- /dev/null
+++ b/app/code/Magento/SalesRule/Test/Unit/Model/Quote/TotalsCollectionStateTest.php
@@ -0,0 +1,56 @@
+state = new TotalsCollectionState();
+ }
+
+ public function testDefaultStateIsFalse()
+ {
+ $this->assertFalse($this->state->isCollecting());
+ }
+
+ public function testSetIsCollectingTrue()
+ {
+ $this->state->setIsCollecting(true);
+ $this->assertTrue($this->state->isCollecting());
+ }
+
+ public function testSetIsCollectingFalse()
+ {
+ $this->state->setIsCollecting(true);
+ $this->state->setIsCollecting(false);
+ $this->assertFalse($this->state->isCollecting());
+ }
+
+ public function testMultipleToggle()
+ {
+ $this->assertFalse($this->state->isCollecting());
+
+ $this->state->setIsCollecting(true);
+ $this->assertTrue($this->state->isCollecting());
+
+ $this->state->setIsCollecting(false);
+ $this->assertFalse($this->state->isCollecting());
+
+ $this->state->setIsCollecting(true);
+ $this->assertTrue($this->state->isCollecting());
+ }
+}
diff --git a/app/code/Magento/SalesRule/etc/di.xml b/app/code/Magento/SalesRule/etc/di.xml
index cd841b36338e4..2be44da6494ac 100644
--- a/app/code/Magento/SalesRule/etc/di.xml
+++ b/app/code/Magento/SalesRule/etc/di.xml
@@ -54,6 +54,9 @@
+
+
+
From a219c3fb66af632f6ace702f00fdcbc573ca46d1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 24 Oct 2025 19:54:39 +0000
Subject: [PATCH 3/5] Force reload of items collection during totals collection
to ensure salesrule attributes are loaded
Co-authored-by: ihor-sviziev <1873745+ihor-sviziev@users.noreply.github.com>
---
.../Model/Plugin/TotalsCollectorPlugin.php | 11 +++++++-
.../Plugin/TotalsCollectorPluginTest.php | 28 ++++++++++++++++++-
2 files changed, 37 insertions(+), 2 deletions(-)
diff --git a/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php b/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php
index d5e0754ee57c9..0b8c0447cdec8 100644
--- a/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php
+++ b/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php
@@ -30,7 +30,10 @@ public function __construct(TotalsCollectionState $totalsCollectionState)
}
/**
- * Set flag before totals collection starts
+ * Set flag before totals collection starts and ensure items are reloaded
+ *
+ * Forces quote items collection to be reloaded to ensure salesrule attributes
+ * are included when totals collection requires them.
*
* @param TotalsCollector $subject
* @param Quote $quote
@@ -40,6 +43,12 @@ public function __construct(TotalsCollectionState $totalsCollectionState)
public function beforeCollect(TotalsCollector $subject, Quote $quote): array
{
$this->totalsCollectionState->setIsCollecting(true);
+
+ // Force reload of items collection to ensure salesrule attributes are loaded
+ if ($quote->hasItemsCollection()) {
+ $quote->unsetData('items_collection');
+ }
+
return [$quote];
}
diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php
index f626ed5f53417..d20aa6cfe4dbd 100644
--- a/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php
+++ b/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php
@@ -46,8 +46,34 @@ protected function setUp(): void
$this->plugin = new TotalsCollectorPlugin($this->totalsCollectionState);
}
- public function testBeforeCollectSetsState()
+ public function testBeforeCollectSetsStateAndClearsCache()
{
+ $this->quote->expects($this->once())
+ ->method('hasItemsCollection')
+ ->willReturn(true);
+
+ $this->quote->expects($this->once())
+ ->method('unsetData')
+ ->with('items_collection');
+
+ $this->totalsCollectionState->expects($this->once())
+ ->method('setIsCollecting')
+ ->with(true);
+
+ $result = $this->plugin->beforeCollect($this->totalsCollector, $this->quote);
+
+ $this->assertEquals([$this->quote], $result);
+ }
+
+ public function testBeforeCollectSetsStateWithoutClearingCacheIfNotSet()
+ {
+ $this->quote->expects($this->once())
+ ->method('hasItemsCollection')
+ ->willReturn(false);
+
+ $this->quote->expects($this->never())
+ ->method('unsetData');
+
$this->totalsCollectionState->expects($this->once())
->method('setIsCollecting')
->with(true);
From 681e56c5fe107a2f4838372a813745eee90978a7 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 24 Oct 2025 19:57:47 +0000
Subject: [PATCH 4/5] Fix code formatting in TotalsCollectorPlugin
Co-authored-by: ihor-sviziev <1873745+ihor-sviziev@users.noreply.github.com>
---
.../Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php b/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php
index 0b8c0447cdec8..f3e2465f04654 100644
--- a/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php
+++ b/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php
@@ -48,7 +48,7 @@ public function beforeCollect(TotalsCollector $subject, Quote $quote): array
if ($quote->hasItemsCollection()) {
$quote->unsetData('items_collection');
}
-
+
return [$quote];
}
From bf149f585cc19afeddae40d54e7bd9add5f2f5b2 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 26 Oct 2025 08:53:41 +0000
Subject: [PATCH 5/5] Refactor to use cache-based approach instead of state
management
Co-authored-by: ihor-sviziev <1873745+ihor-sviziev@users.noreply.github.com>
---
.../Plugin/QuoteConfigProductAttributes.php | 55 ++++++++---
.../Model/Plugin/TotalsCollectorPlugin.php | 68 -------------
.../Model/Quote/TotalsCollectionState.php | 42 --------
.../ClearProductAttributesCacheObserver.php | 47 +++++++++
.../QuoteConfigProductAttributesTest.php | 75 +++++++++-----
.../Plugin/TotalsCollectorPluginTest.php | 98 -------------------
.../Model/Quote/TotalsCollectionStateTest.php | 56 -----------
...learProductAttributesCacheObserverTest.php | 44 +++++++++
app/code/Magento/SalesRule/etc/di.xml | 3 -
app/code/Magento/SalesRule/etc/events.xml | 2 +
10 files changed, 185 insertions(+), 305 deletions(-)
delete mode 100644 app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php
delete mode 100644 app/code/Magento/SalesRule/Model/Quote/TotalsCollectionState.php
create mode 100644 app/code/Magento/SalesRule/Observer/ClearProductAttributesCacheObserver.php
delete mode 100644 app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php
delete mode 100644 app/code/Magento/SalesRule/Test/Unit/Model/Quote/TotalsCollectionStateTest.php
create mode 100644 app/code/Magento/SalesRule/Test/Unit/Observer/ClearProductAttributesCacheObserverTest.php
diff --git a/app/code/Magento/SalesRule/Model/Plugin/QuoteConfigProductAttributes.php b/app/code/Magento/SalesRule/Model/Plugin/QuoteConfigProductAttributes.php
index 790fe51ec582d..f7ee3e6c27914 100644
--- a/app/code/Magento/SalesRule/Model/Plugin/QuoteConfigProductAttributes.php
+++ b/app/code/Magento/SalesRule/Model/Plugin/QuoteConfigProductAttributes.php
@@ -6,12 +6,23 @@
namespace Magento\SalesRule\Model\Plugin;
+use Magento\Framework\App\CacheInterface;
+use Magento\Framework\Serialize\SerializerInterface;
use Magento\Quote\Model\Quote\Config;
use Magento\SalesRule\Model\ResourceModel\Rule as RuleResource;
-use Magento\SalesRule\Model\Quote\TotalsCollectionState;
class QuoteConfigProductAttributes
{
+ /**
+ * Cache key for active salesrule attributes
+ */
+ private const CACHE_KEY = 'salesrule_active_product_attributes';
+
+ /**
+ * Cache tag for salesrule attributes
+ */
+ private const CACHE_TAG = 'salesrule';
+
/**
* @var RuleResource
*/
@@ -23,27 +34,34 @@ class QuoteConfigProductAttributes
private $activeAttributeCodes;
/**
- * @var TotalsCollectionState
+ * @var CacheInterface
*/
- private $totalsCollectionState;
+ private $cache;
+
+ /**
+ * @var SerializerInterface
+ */
+ private $serializer;
/**
* @param RuleResource $ruleResource
- * @param TotalsCollectionState $totalsCollectionState
+ * @param CacheInterface $cache
+ * @param SerializerInterface $serializer
*/
public function __construct(
RuleResource $ruleResource,
- TotalsCollectionState $totalsCollectionState
+ CacheInterface $cache,
+ SerializerInterface $serializer
) {
$this->ruleResource = $ruleResource;
- $this->totalsCollectionState = $totalsCollectionState;
+ $this->cache = $cache;
+ $this->serializer = $serializer;
}
/**
* Append sales rule product attribute keys to select by quote item collection
*
- * Only loads attributes when totals collection is in progress to avoid unnecessary
- * database queries and EAV attribute loading on every cart view.
+ * Uses cache to avoid database queries on every request.
*
* @param Config $subject
* @param array $attributeKeys
@@ -53,13 +71,22 @@ public function __construct(
*/
public function afterGetProductAttributes(Config $subject, array $attributeKeys): array
{
- // Only load salesrule attributes when actually collecting totals
- if (!$this->totalsCollectionState->isCollecting()) {
- return $attributeKeys;
- }
-
if ($this->activeAttributeCodes === null) {
- $this->activeAttributeCodes = array_column($this->ruleResource->getActiveAttributes(), 'attribute_code');
+ $cachedData = $this->cache->load(self::CACHE_KEY);
+
+ if ($cachedData !== false) {
+ $this->activeAttributeCodes = $this->serializer->unserialize($cachedData);
+ } else {
+ $this->activeAttributeCodes = array_column(
+ $this->ruleResource->getActiveAttributes(),
+ 'attribute_code'
+ );
+ $this->cache->save(
+ $this->serializer->serialize($this->activeAttributeCodes),
+ self::CACHE_KEY,
+ [self::CACHE_TAG]
+ );
+ }
}
return array_merge($attributeKeys, $this->activeAttributeCodes);
diff --git a/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php b/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php
deleted file mode 100644
index f3e2465f04654..0000000000000
--- a/app/code/Magento/SalesRule/Model/Plugin/TotalsCollectorPlugin.php
+++ /dev/null
@@ -1,68 +0,0 @@
-totalsCollectionState = $totalsCollectionState;
- }
-
- /**
- * Set flag before totals collection starts and ensure items are reloaded
- *
- * Forces quote items collection to be reloaded to ensure salesrule attributes
- * are included when totals collection requires them.
- *
- * @param TotalsCollector $subject
- * @param Quote $quote
- * @return array
- * @SuppressWarnings(PHPMD.UnusedFormalParameter)
- */
- public function beforeCollect(TotalsCollector $subject, Quote $quote): array
- {
- $this->totalsCollectionState->setIsCollecting(true);
-
- // Force reload of items collection to ensure salesrule attributes are loaded
- if ($quote->hasItemsCollection()) {
- $quote->unsetData('items_collection');
- }
-
- return [$quote];
- }
-
- /**
- * Unset flag after totals collection completes
- *
- * @param TotalsCollector $subject
- * @param Total $result
- * @return Total
- * @SuppressWarnings(PHPMD.UnusedFormalParameter)
- */
- public function afterCollect(TotalsCollector $subject, Total $result): Total
- {
- $this->totalsCollectionState->setIsCollecting(false);
- return $result;
- }
-}
diff --git a/app/code/Magento/SalesRule/Model/Quote/TotalsCollectionState.php b/app/code/Magento/SalesRule/Model/Quote/TotalsCollectionState.php
deleted file mode 100644
index 9bc80a4a47de2..0000000000000
--- a/app/code/Magento/SalesRule/Model/Quote/TotalsCollectionState.php
+++ /dev/null
@@ -1,42 +0,0 @@
-isCollecting = $state;
- }
-
- /**
- * Check if totals collection is in progress
- *
- * @return bool
- */
- public function isCollecting(): bool
- {
- return $this->isCollecting;
- }
-}
diff --git a/app/code/Magento/SalesRule/Observer/ClearProductAttributesCacheObserver.php b/app/code/Magento/SalesRule/Observer/ClearProductAttributesCacheObserver.php
new file mode 100644
index 0000000000000..68e14e2bef4c9
--- /dev/null
+++ b/app/code/Magento/SalesRule/Observer/ClearProductAttributesCacheObserver.php
@@ -0,0 +1,47 @@
+cache = $cache;
+ }
+
+ /**
+ * Clear salesrule product attributes cache
+ *
+ * @param Observer $observer
+ * @return void
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function execute(Observer $observer): void
+ {
+ $this->cache->remove(self::CACHE_KEY);
+ }
+}
diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/QuoteConfigProductAttributesTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/QuoteConfigProductAttributesTest.php
index 92e32281727ee..c9b2f211c5463 100644
--- a/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/QuoteConfigProductAttributesTest.php
+++ b/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/QuoteConfigProductAttributesTest.php
@@ -7,10 +7,11 @@
namespace Magento\SalesRule\Test\Unit\Model\Plugin;
+use Magento\Framework\App\CacheInterface;
+use Magento\Framework\Serialize\SerializerInterface;
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
use Magento\Quote\Model\Quote\Config;
use Magento\SalesRule\Model\Plugin\QuoteConfigProductAttributes;
-use Magento\SalesRule\Model\Quote\TotalsCollectionState;
use Magento\SalesRule\Model\ResourceModel\Rule;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
@@ -18,7 +19,7 @@
class QuoteConfigProductAttributesTest extends TestCase
{
/**
- * @var QuoteConfigProductAttributes|MockObject
+ * @var QuoteConfigProductAttributes
*/
protected $plugin;
@@ -28,58 +29,84 @@ class QuoteConfigProductAttributesTest extends TestCase
protected $ruleResource;
/**
- * @var TotalsCollectionState|MockObject
+ * @var CacheInterface|MockObject
*/
- protected $totalsCollectionState;
+ protected $cache;
+
+ /**
+ * @var SerializerInterface|MockObject
+ */
+ protected $serializer;
protected function setUp(): void
{
$objectManager = new ObjectManager($this);
$this->ruleResource = $this->createMock(Rule::class);
- $this->totalsCollectionState = $this->createMock(TotalsCollectionState::class);
+ $this->cache = $this->createMock(CacheInterface::class);
+ $this->serializer = $this->createMock(SerializerInterface::class);
$this->plugin = $objectManager->getObject(
QuoteConfigProductAttributes::class,
[
'ruleResource' => $this->ruleResource,
- 'totalsCollectionState' => $this->totalsCollectionState
+ 'cache' => $this->cache,
+ 'serializer' => $this->serializer
]
);
}
- public function testAfterGetProductAttributes()
+ public function testAfterGetProductAttributesWithCache()
{
$subject = $this->createMock(Config::class);
$attributeCode = 'code of the attribute';
$expected = [0 => $attributeCode];
+ $serializedData = '["' . $attributeCode . '"]';
- $this->totalsCollectionState->expects($this->once())
- ->method('isCollecting')
- ->willReturn(true);
+ $this->cache->expects($this->once())
+ ->method('load')
+ ->with('salesrule_active_product_attributes')
+ ->willReturn($serializedData);
- $this->ruleResource->expects($this->once())
- ->method('getActiveAttributes')
- ->willReturn(
- [
- ['attribute_code' => $attributeCode, 'enabled' => true],
- ]
- );
+ $this->serializer->expects($this->once())
+ ->method('unserialize')
+ ->with($serializedData)
+ ->willReturn([$attributeCode]);
+
+ $this->ruleResource->expects($this->never())
+ ->method('getActiveAttributes');
$this->assertEquals($expected, $this->plugin->afterGetProductAttributes($subject, []));
}
- public function testAfterGetProductAttributesNotCollecting()
+ public function testAfterGetProductAttributesWithoutCache()
{
$subject = $this->createMock(Config::class);
- $inputAttributes = ['existing_attribute'];
+ $attributeCode = 'code of the attribute';
+ $expected = [0 => $attributeCode];
+ $serializedData = '["' . $attributeCode . '"]';
- $this->totalsCollectionState->expects($this->once())
- ->method('isCollecting')
+ $this->cache->expects($this->once())
+ ->method('load')
+ ->with('salesrule_active_product_attributes')
->willReturn(false);
- $this->ruleResource->expects($this->never())
- ->method('getActiveAttributes');
+ $this->ruleResource->expects($this->once())
+ ->method('getActiveAttributes')
+ ->willReturn(
+ [
+ ['attribute_code' => $attributeCode, 'enabled' => true],
+ ]
+ );
- $this->assertEquals($inputAttributes, $this->plugin->afterGetProductAttributes($subject, $inputAttributes));
+ $this->serializer->expects($this->once())
+ ->method('serialize')
+ ->with([$attributeCode])
+ ->willReturn($serializedData);
+
+ $this->cache->expects($this->once())
+ ->method('save')
+ ->with($serializedData, 'salesrule_active_product_attributes', ['salesrule']);
+
+ $this->assertEquals($expected, $this->plugin->afterGetProductAttributes($subject, []));
}
}
diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php
deleted file mode 100644
index d20aa6cfe4dbd..0000000000000
--- a/app/code/Magento/SalesRule/Test/Unit/Model/Plugin/TotalsCollectorPluginTest.php
+++ /dev/null
@@ -1,98 +0,0 @@
-totalsCollectionState = $this->createMock(TotalsCollectionState::class);
- $this->totalsCollector = $this->createMock(TotalsCollector::class);
- $this->quote = $this->createMock(Quote::class);
-
- $this->plugin = new TotalsCollectorPlugin($this->totalsCollectionState);
- }
-
- public function testBeforeCollectSetsStateAndClearsCache()
- {
- $this->quote->expects($this->once())
- ->method('hasItemsCollection')
- ->willReturn(true);
-
- $this->quote->expects($this->once())
- ->method('unsetData')
- ->with('items_collection');
-
- $this->totalsCollectionState->expects($this->once())
- ->method('setIsCollecting')
- ->with(true);
-
- $result = $this->plugin->beforeCollect($this->totalsCollector, $this->quote);
-
- $this->assertEquals([$this->quote], $result);
- }
-
- public function testBeforeCollectSetsStateWithoutClearingCacheIfNotSet()
- {
- $this->quote->expects($this->once())
- ->method('hasItemsCollection')
- ->willReturn(false);
-
- $this->quote->expects($this->never())
- ->method('unsetData');
-
- $this->totalsCollectionState->expects($this->once())
- ->method('setIsCollecting')
- ->with(true);
-
- $result = $this->plugin->beforeCollect($this->totalsCollector, $this->quote);
-
- $this->assertEquals([$this->quote], $result);
- }
-
- public function testAfterCollectUnsetsState()
- {
- $total = $this->createMock(Total::class);
-
- $this->totalsCollectionState->expects($this->once())
- ->method('setIsCollecting')
- ->with(false);
-
- $result = $this->plugin->afterCollect($this->totalsCollector, $total);
-
- $this->assertSame($total, $result);
- }
-}
diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Quote/TotalsCollectionStateTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Quote/TotalsCollectionStateTest.php
deleted file mode 100644
index 7da89194e6abe..0000000000000
--- a/app/code/Magento/SalesRule/Test/Unit/Model/Quote/TotalsCollectionStateTest.php
+++ /dev/null
@@ -1,56 +0,0 @@
-state = new TotalsCollectionState();
- }
-
- public function testDefaultStateIsFalse()
- {
- $this->assertFalse($this->state->isCollecting());
- }
-
- public function testSetIsCollectingTrue()
- {
- $this->state->setIsCollecting(true);
- $this->assertTrue($this->state->isCollecting());
- }
-
- public function testSetIsCollectingFalse()
- {
- $this->state->setIsCollecting(true);
- $this->state->setIsCollecting(false);
- $this->assertFalse($this->state->isCollecting());
- }
-
- public function testMultipleToggle()
- {
- $this->assertFalse($this->state->isCollecting());
-
- $this->state->setIsCollecting(true);
- $this->assertTrue($this->state->isCollecting());
-
- $this->state->setIsCollecting(false);
- $this->assertFalse($this->state->isCollecting());
-
- $this->state->setIsCollecting(true);
- $this->assertTrue($this->state->isCollecting());
- }
-}
diff --git a/app/code/Magento/SalesRule/Test/Unit/Observer/ClearProductAttributesCacheObserverTest.php b/app/code/Magento/SalesRule/Test/Unit/Observer/ClearProductAttributesCacheObserverTest.php
new file mode 100644
index 0000000000000..904b642b0731b
--- /dev/null
+++ b/app/code/Magento/SalesRule/Test/Unit/Observer/ClearProductAttributesCacheObserverTest.php
@@ -0,0 +1,44 @@
+cache = $this->createMock(CacheInterface::class);
+ $this->observer = new ClearProductAttributesCacheObserver($this->cache);
+ }
+
+ public function testExecuteClearsCache()
+ {
+ $event = $this->createMock(Observer::class);
+
+ $this->cache->expects($this->once())
+ ->method('remove')
+ ->with('salesrule_active_product_attributes');
+
+ $this->observer->execute($event);
+ }
+}
diff --git a/app/code/Magento/SalesRule/etc/di.xml b/app/code/Magento/SalesRule/etc/di.xml
index 2be44da6494ac..cd841b36338e4 100644
--- a/app/code/Magento/SalesRule/etc/di.xml
+++ b/app/code/Magento/SalesRule/etc/di.xml
@@ -54,9 +54,6 @@
-
-
-
diff --git a/app/code/Magento/SalesRule/etc/events.xml b/app/code/Magento/SalesRule/etc/events.xml
index f430ef2ee8758..20e448a5ef3a1 100644
--- a/app/code/Magento/SalesRule/etc/events.xml
+++ b/app/code/Magento/SalesRule/etc/events.xml
@@ -32,9 +32,11 @@
+
+