Skip to content

Commit 3b8528c

Browse files
Merge branch '2.4-develop' into PlatformHealthScope11June25
2 parents 51037c2 + ae6f305 commit 3b8528c

File tree

5 files changed

+453
-5
lines changed

5 files changed

+453
-5
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All rights reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\PageCache\Model\App;
9+
10+
use Magento\Store\Model\StoreManager;
11+
12+
/**
13+
* Class CacheIdentifierForSavePlugin
14+
*
15+
* Should add design exceptions to identifier for built-in cache when saving
16+
*/
17+
class CacheIdentifierForSavePlugin
18+
{
19+
/**
20+
* @var \Magento\Framework\View\DesignExceptions
21+
*/
22+
private $designExceptions;
23+
24+
/**
25+
* @var \Magento\Framework\App\RequestInterface
26+
*/
27+
private $request;
28+
29+
/**
30+
* @var \Magento\PageCache\Model\Config
31+
*/
32+
private $config;
33+
34+
/**
35+
* @param \Magento\Framework\View\DesignExceptions $designExceptions
36+
* @param \Magento\Framework\App\RequestInterface $request
37+
* @param \Magento\PageCache\Model\Config $config
38+
*/
39+
public function __construct(
40+
\Magento\Framework\View\DesignExceptions $designExceptions,
41+
\Magento\Framework\App\RequestInterface $request,
42+
\Magento\PageCache\Model\Config $config
43+
) {
44+
$this->designExceptions = $designExceptions;
45+
$this->request = $request;
46+
$this->config = $config;
47+
}
48+
49+
/**
50+
* Adds a theme key to identifier for a built-in cache if user-agent theme rule is actual
51+
*
52+
* @param \Magento\PageCache\Model\App\Request\Http\IdentifierForSave $identifier
53+
* @param string $result
54+
* @return string
55+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
56+
*/
57+
public function afterGetValue(\Magento\PageCache\Model\App\Request\Http\IdentifierForSave $identifier, $result)
58+
{
59+
if ($this->config->getType() === \Magento\PageCache\Model\Config::BUILT_IN && $this->config->isEnabled()) {
60+
$identifierPrefix = '';
61+
62+
$ruleDesignException = $this->designExceptions->getThemeByRequest($this->request);
63+
if ($ruleDesignException !== false) {
64+
$identifierPrefix .= 'DESIGN' . '=' . $ruleDesignException . '|';
65+
}
66+
67+
if ($runType = $this->request->getServerValue(StoreManager::PARAM_RUN_TYPE)) {
68+
$identifierPrefix .= StoreManager::PARAM_RUN_TYPE . '=' . $runType . '|';
69+
}
70+
71+
if ($runCode = $this->request->getServerValue(StoreManager::PARAM_RUN_CODE)) {
72+
$identifierPrefix .= StoreManager::PARAM_RUN_CODE . '=' . $runCode . '|';
73+
}
74+
75+
return $identifierPrefix . $result;
76+
}
77+
return $result;
78+
}
79+
}

app/code/Magento/PageCache/etc/di.xml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
<?xml version="1.0"?>
22
<!--
33
/**
4-
* Copyright © Magento, Inc. All rights reserved.
5-
* See COPYING.txt for license details.
4+
* Copyright 2015 Adobe
5+
* All rights reserved.
66
*/
77
-->
88
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
9+
<type name="Magento\Framework\App\PageCache\Identifier">
10+
<plugin name="core-app-area-design-exception-plugin" type="Magento\PageCache\Model\App\CacheIdentifierPlugin" sortOrder="10"/>
11+
</type>
12+
<type name="Magento\PageCache\Model\App\Request\Http\IdentifierForSave">
13+
<plugin name="core-app-area-design-exception-plugin-for-save" type="Magento\PageCache\Model\App\CacheIdentifierForSavePlugin" sortOrder="10"/>
14+
</type>
915
<type name="Magento\Framework\App\PageCache\Cache">
1016
<plugin name="fpc-type-plugin" type="Magento\PageCache\Model\App\PageCachePlugin"/>
1117
</type>
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All rights reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Framework\Mview\View;
9+
10+
use Magento\Framework\App\ResourceConnection;
11+
use Magento\Framework\DB\Ddl\Trigger;
12+
use Magento\Framework\DB\Ddl\TriggerFactory;
13+
use Magento\Framework\Indexer\Action\Dummy;
14+
use Magento\Framework\Mview\Config;
15+
use Magento\Framework\Mview\Config\Data;
16+
use Magento\Framework\Mview\View\AdditionalColumnsProcessor\DefaultProcessor;
17+
use Magento\Framework\Mview\ViewInterface;
18+
use Magento\TestFramework\Helper\Bootstrap;
19+
use PHPUnit\Framework\TestCase;
20+
21+
/**
22+
* Integration test for \Magento\Framework\Mview\View\Subscription
23+
*
24+
* @magentoDbIsolation disabled
25+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
26+
*/
27+
class SubscriptionTest extends TestCase
28+
{
29+
/**
30+
* @var Subscription
31+
*/
32+
private $subscription;
33+
34+
/**
35+
* @var ResourceConnection
36+
*/
37+
private $resource;
38+
39+
/**
40+
* @var TriggerFactory
41+
*/
42+
private $triggerFactory;
43+
44+
/**
45+
* @var CollectionInterface
46+
*/
47+
private $viewCollection;
48+
49+
/**
50+
* @var ViewInterface
51+
*/
52+
private $view;
53+
54+
/**
55+
* @var Config
56+
*/
57+
private $mviewConfig;
58+
59+
/**
60+
* @var SubscriptionStatementPostprocessorInterface
61+
*/
62+
private $statementPostprocessor;
63+
64+
/**
65+
* @inheritdoc
66+
*/
67+
protected function setUp(): void
68+
{
69+
$objectManager = Bootstrap::getObjectManager();
70+
$this->resource = $objectManager->get(ResourceConnection::class);
71+
$this->triggerFactory = $objectManager->get(TriggerFactory::class);
72+
$this->viewCollection = $objectManager->get(CollectionInterface::class);
73+
$this->mviewConfig = $objectManager->get(Config::class);
74+
$this->statementPostprocessor = $objectManager->get(SubscriptionStatementPostprocessorInterface::class);
75+
76+
// Create a test view
77+
$this->view = $objectManager->create(ViewInterface::class);
78+
$this->view->setId('test_view')
79+
->setData('subscriptions', [
80+
'catalog_product_entity' => [
81+
'name' => 'catalog_product_entity',
82+
'column' => 'entity_id',
83+
'subscription_model' => null,
84+
'processor' => DefaultProcessor::class
85+
]
86+
]);
87+
88+
// Create changelog for the view
89+
$changelog = $objectManager->create(Changelog::class);
90+
$changelog->setViewId('test_view');
91+
$changelog->create();
92+
93+
// Set up view state
94+
$state = $objectManager->create(StateInterface::class);
95+
$state->setViewId('test_view')
96+
->setMode(StateInterface::MODE_ENABLED)
97+
->setStatus(StateInterface::STATUS_IDLE)
98+
->save();
99+
100+
$this->view->setState($state);
101+
102+
// Configure the view in Mview configuration
103+
$configData = $objectManager->get(Data::class);
104+
$configData->merge([
105+
'test_view' => [
106+
'view_id' => 'test_view',
107+
'action_class' => Dummy::class,
108+
'group' => 'indexer',
109+
'subscriptions' => [
110+
'catalog_product_entity' => [
111+
'name' => 'catalog_product_entity',
112+
'column' => 'entity_id',
113+
'subscription_model' => null,
114+
'processor' => DefaultProcessor::class
115+
]
116+
]
117+
]
118+
]);
119+
120+
$this->subscription = new Subscription(
121+
$this->resource,
122+
$this->triggerFactory,
123+
$this->viewCollection,
124+
$this->view,
125+
'catalog_product_entity',
126+
'entity_id',
127+
['updated_at'],
128+
[],
129+
$this->mviewConfig,
130+
$this->statementPostprocessor
131+
);
132+
}
133+
134+
/**
135+
* @inheritdoc
136+
*/
137+
protected function tearDown(): void
138+
{
139+
// Clean up changelog table
140+
$changelog = $this->view->getChangelog();
141+
if ($changelog) {
142+
$changelog->drop();
143+
}
144+
145+
// Clean up state
146+
$state = $this->view->getState();
147+
if ($state) {
148+
$state->delete();
149+
}
150+
}
151+
152+
/**
153+
* Test creating database triggers
154+
*/
155+
public function testCreateTriggers(): void
156+
{
157+
// Create triggers
158+
$this->subscription->create();
159+
160+
// Verify triggers were created
161+
$connection = $this->resource->getConnection();
162+
$triggers = $this->subscription->getTriggers();
163+
164+
foreach ($triggers as $trigger) {
165+
$triggerName = $trigger->getName();
166+
$result = $connection->fetchOne(
167+
"SELECT TRIGGER_NAME FROM information_schema.TRIGGERS WHERE TRIGGER_NAME = ?",
168+
[$triggerName]
169+
);
170+
$this->assertNotEmpty(
171+
$result,
172+
sprintf('Trigger %s was not created', $triggerName)
173+
);
174+
}
175+
}
176+
177+
/**
178+
* Test removing database triggers
179+
*/
180+
public function testRemoveTriggers(): void
181+
{
182+
// First create triggers
183+
$this->subscription->create();
184+
185+
// Get trigger names before removal
186+
$triggers = $this->subscription->getTriggers();
187+
$triggerNames = array_map(function ($trigger) {
188+
return $trigger->getName();
189+
}, $triggers);
190+
191+
// Remove triggers
192+
$this->subscription->remove();
193+
194+
// Verify triggers were removed
195+
$connection = $this->resource->getConnection();
196+
foreach ($triggerNames as $triggerName) {
197+
$this->assertFalse(
198+
$connection->isTableExists($triggerName),
199+
sprintf('Trigger %s was not removed', $triggerName)
200+
);
201+
}
202+
}
203+
204+
/**
205+
* Test trigger statements for ignored columns
206+
*/
207+
public function testTriggerStatementsWithIgnoredColumns(): void
208+
{
209+
$this->subscription->create();
210+
$triggers = $this->subscription->getTriggers();
211+
212+
// Find the UPDATE trigger
213+
$updateTrigger = null;
214+
foreach ($triggers as $trigger) {
215+
if ($trigger->getEvent() === Trigger::EVENT_UPDATE) {
216+
$updateTrigger = $trigger;
217+
break;
218+
}
219+
}
220+
221+
$this->assertNotNull($updateTrigger, 'UPDATE trigger not found');
222+
223+
// Verify the trigger statements contain the ignored column check
224+
$statements = $updateTrigger->getStatements();
225+
$this->assertNotEmpty($statements, 'Trigger has no statements');
226+
227+
// Check that updated_at is NOT in the list of columns being checked
228+
$hasIgnoredColumnCheck = true;
229+
foreach ($statements as $statement) {
230+
if (strpos($statement, 'NOT(NEW.`updated_at` <=> OLD.`updated_at`)') !== false) {
231+
$hasIgnoredColumnCheck = false;
232+
break;
233+
}
234+
}
235+
236+
$this->assertTrue(
237+
$hasIgnoredColumnCheck,
238+
'Trigger contains check for ignored column'
239+
);
240+
}
241+
}

0 commit comments

Comments
 (0)