Skip to content

Commit e5d0279

Browse files
ENGCOM-8474: #21853: Allow mview indexers to use different entity columns. #21857
2 parents 3788230 + 7327dc0 commit e5d0279

File tree

2 files changed

+100
-48
lines changed

2 files changed

+100
-48
lines changed

lib/internal/Magento/Framework/Mview/Test/Unit/View/SubscriptionTest.php

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ public function testCreate()
207207
->method('getColumnName')
208208
->willReturn('entity_id');
209209

210-
$this->viewMock->expects($this->exactly(3))
210+
$this->viewMock->expects($this->atLeastOnce())
211211
->method('getChangelog')
212212
->willReturn($changelogMock);
213213

@@ -243,20 +243,23 @@ public function testCreate()
243243
$otherViewMock->expects($this->exactly(1))
244244
->method('getId')
245245
->willReturn('other_id');
246-
$otherViewMock->expects($this->exactly(1))
246+
$otherViewMock->expects($this->exactly(4))
247247
->method('getSubscriptions')
248-
->willReturn([['name' => $this->tableName], ['name' => 'otherTableName']]);
249-
$otherViewMock->expects($this->exactly(3))
248+
->willReturn(
249+
[
250+
$this->tableName => ['name' => $this->tableName, 'column' => 'columnName'],
251+
'otherTableName' => ['name' => 'otherTableName', 'column' => 'columnName']
252+
]
253+
);
254+
$otherViewMock->expects($this->atLeastOnce())
250255
->method('getChangelog')
251256
->willReturn($otherChangelogMock);
252257

253258
$this->viewMock->expects($this->any())
254259
->method('getId')
255260
->willReturn('this_id');
256-
$this->viewMock->expects($this->never())
257-
->method('getSubscriptions');
258261

259-
$this->viewCollectionMock->expects($this->exactly(1))
262+
$this->viewCollectionMock->expects($this->once())
260263
->method('getViewsByStateMode')
261264
->with(StateInterface::MODE_ENABLED)
262265
->willReturn([$this->viewMock, $otherViewMock]);
@@ -268,6 +271,16 @@ public function testCreate()
268271
$this->connectionMock->expects($this->exactly(3))
269272
->method('createTrigger')
270273
->with($triggerMock);
274+
275+
$this->viewMock->expects($this->exactly(3))
276+
->method('getSubscriptions')
277+
->willReturn(
278+
[
279+
$this->tableName => ['name' => $this->tableName, 'column' => 'columnName'],
280+
'otherTableName' => ['name' => 'otherTableName', 'column' => 'columnName']
281+
]
282+
);
283+
271284
$this->model->create();
272285
}
273286

@@ -319,21 +332,24 @@ public function testRemove()
319332
true,
320333
[]
321334
);
322-
$otherViewMock->expects($this->exactly(1))
335+
$otherViewMock->expects($this->atLeastOnce())
323336
->method('getId')
324337
->willReturn('other_id');
325-
$otherViewMock->expects($this->exactly(1))
326-
->method('getSubscriptions')
327-
->willReturn([['name' => $this->tableName], ['name' => 'otherTableName']]);
328-
$otherViewMock->expects($this->exactly(3))
338+
$otherViewMock->expects($this->atLeastOnce())
329339
->method('getChangelog')
330340
->willReturn($otherChangelogMock);
331341

332-
$this->viewMock->expects($this->any())
342+
$this->viewMock->expects($this->atLeastOnce())
333343
->method('getId')
334344
->willReturn('this_id');
335-
$this->viewMock->expects($this->never())
336-
->method('getSubscriptions');
345+
$otherViewMock->expects($this->atLeastOnce())
346+
->method('getSubscriptions')
347+
->willReturn(
348+
[
349+
$this->tableName => ['name' => $this->tableName, 'column' => 'columnName'],
350+
'otherTableName' => ['name' => 'otherTableName', 'column' => 'columnName']
351+
]
352+
);
337353

338354
$this->viewCollectionMock->expects($this->exactly(1))
339355
->method('getViewsByStateMode')
@@ -411,6 +427,18 @@ public function testBuildStatementIgnoredColumnSubscriptionLevel(): void
411427
->method('getColumnName')
412428
->willReturn('entity_id');
413429

430+
$this->viewMock->expects($this->once())
431+
->method('getSubscriptions')
432+
->willReturn(
433+
[
434+
$this->tableName => ['name' => $this->tableName, 'column' => 'columnName'],
435+
'cataloginventory_stock_item' => ['name' => 'otherTableName', 'column' => 'columnName']
436+
]
437+
);
438+
$this->viewMock->expects($this->atLeastOnce())
439+
->method('getChangeLog')
440+
->willReturn($otherChangelogMock);
441+
414442
$model = new Subscription(
415443
$this->resourceMock,
416444
$this->triggerFactoryMock,
@@ -425,7 +453,7 @@ public function testBuildStatementIgnoredColumnSubscriptionLevel(): void
425453

426454
$method = new \ReflectionMethod($model, 'buildStatement');
427455
$method->setAccessible(true);
428-
$statement = $method->invoke($model, Trigger::EVENT_UPDATE, $otherChangelogMock);
456+
$statement = $method->invoke($model, Trigger::EVENT_UPDATE, $this->viewMock);
429457

430458
$this->assertStringNotContainsString($ignoredColumnName, $statement);
431459
$this->assertStringContainsString($notIgnoredColumnName, $statement);

lib/internal/Magento/Framework/Mview/View/Subscription.php

Lines changed: 56 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,31 @@
88

99
use Magento\Framework\App\ObjectManager;
1010
use Magento\Framework\App\ResourceConnection;
11+
use Magento\Framework\DB\Adapter\AdapterInterface;
1112
use Magento\Framework\DB\Ddl\Trigger;
13+
use Magento\Framework\DB\Ddl\TriggerFactory;
1214
use Magento\Framework\Mview\Config;
13-
use Magento\Framework\Mview\View\StateInterface;
15+
use Magento\Framework\Mview\ViewInterface;
1416

1517
/**
16-
* Class Subscription for handling partial indexation triggers
18+
* Mview subscription.
1719
*/
1820
class Subscription implements SubscriptionInterface
1921
{
2022
/**
2123
* Database connection
2224
*
23-
* @var \Magento\Framework\DB\Adapter\AdapterInterface
25+
* @var AdapterInterface
2426
*/
2527
protected $connection;
2628

2729
/**
28-
* @var \Magento\Framework\DB\Ddl\TriggerFactory
30+
* @var TriggerFactory
2931
*/
3032
protected $triggerFactory;
3133

3234
/**
33-
* @var \Magento\Framework\Mview\View\CollectionInterface
35+
* @var CollectionInterface
3436
*/
3537
protected $viewCollection;
3638

@@ -62,7 +64,7 @@ class Subscription implements SubscriptionInterface
6264
*
6365
* @var array
6466
*/
65-
private $ignoredUpdateColumns = [];
67+
private $ignoredUpdateColumns;
6668

6769
/**
6870
* List of columns that can be updated in a specific subscribed table
@@ -74,16 +76,17 @@ class Subscription implements SubscriptionInterface
7476
* @var Resource
7577
*/
7678
protected $resource;
79+
7780
/**
7881
* @var Config
7982
*/
8083
private $mviewConfig;
8184

8285
/**
8386
* @param ResourceConnection $resource
84-
* @param \Magento\Framework\DB\Ddl\TriggerFactory $triggerFactory
85-
* @param \Magento\Framework\Mview\View\CollectionInterface $viewCollection
86-
* @param \Magento\Framework\Mview\ViewInterface $view
87+
* @param TriggerFactory $triggerFactory
88+
* @param CollectionInterface $viewCollection
89+
* @param ViewInterface $view
8790
* @param string $tableName
8891
* @param string $columnName
8992
* @param array $ignoredUpdateColumns
@@ -92,9 +95,9 @@ class Subscription implements SubscriptionInterface
9295
*/
9396
public function __construct(
9497
ResourceConnection $resource,
95-
\Magento\Framework\DB\Ddl\TriggerFactory $triggerFactory,
96-
\Magento\Framework\Mview\View\CollectionInterface $viewCollection,
97-
\Magento\Framework\Mview\ViewInterface $view,
98+
TriggerFactory $triggerFactory,
99+
CollectionInterface $viewCollection,
100+
ViewInterface $view,
98101
$tableName,
99102
$columnName,
100103
$ignoredUpdateColumns = [],
@@ -114,9 +117,9 @@ public function __construct(
114117
}
115118

116119
/**
117-
* Create subsciption
120+
* Create subscription
118121
*
119-
* @return \Magento\Framework\Mview\View\SubscriptionInterface
122+
* @return SubscriptionInterface
120123
*/
121124
public function create()
122125
{
@@ -129,12 +132,12 @@ public function create()
129132
->setEvent($event)
130133
->setTable($this->resource->getTableName($this->tableName));
131134

132-
$trigger->addStatement($this->buildStatement($event, $this->getView()->getChangelog()));
135+
$trigger->addStatement($this->buildStatement($event, $this->getView()));
133136

134137
// Add statements for linked views
135138
foreach ($this->getLinkedViews() as $view) {
136-
/** @var \Magento\Framework\Mview\ViewInterface $view */
137-
$trigger->addStatement($this->buildStatement($event, $view->getChangelog()));
139+
/** @var ViewInterface $view */
140+
$trigger->addStatement($this->buildStatement($event, $view));
138141
}
139142

140143
$this->connection->dropTrigger($trigger->getName());
@@ -147,7 +150,7 @@ public function create()
147150
/**
148151
* Remove subscription
149152
*
150-
* @return \Magento\Framework\Mview\View\SubscriptionInterface
153+
* @return SubscriptionInterface
151154
*/
152155
public function remove()
153156
{
@@ -162,8 +165,8 @@ public function remove()
162165

163166
// Add statements for linked views
164167
foreach ($this->getLinkedViews() as $view) {
165-
/** @var \Magento\Framework\Mview\ViewInterface $view */
166-
$trigger->addStatement($this->buildStatement($event, $view->getChangelog()));
168+
/** @var ViewInterface $view */
169+
$trigger->addStatement($this->buildStatement($event, $view));
167170
}
168171

169172
$this->connection->dropTrigger($trigger->getName());
@@ -188,7 +191,7 @@ protected function getLinkedViews()
188191
$viewList = $this->viewCollection->getViewsByStateMode(StateInterface::MODE_ENABLED);
189192

190193
foreach ($viewList as $view) {
191-
/** @var \Magento\Framework\Mview\ViewInterface $view */
194+
/** @var ViewInterface $view */
192195
// Skip the current view
193196
if ($view->getId() == $this->getView()->getId()) {
194197
continue;
@@ -208,21 +211,22 @@ protected function getLinkedViews()
208211
/**
209212
* Prepare columns for trigger statement. Should be protected in order to serve new approach
210213
*
211-
* @param ChangelogInterface $changelog
214+
* @param ViewInterface $view
212215
* @param string $event
213216
* @return array
214217
* @throws \Exception
215218
*/
216-
protected function prepareColumns(ChangelogInterface $changelog, string $event): array
219+
protected function prepareColumns(ViewInterface $view, string $event): array
217220
{
221+
$changelog = $view->getChangelog();
218222
$prefix = $event === Trigger::EVENT_DELETE ? 'OLD.' : 'NEW.';
219223
$subscriptionData = $this->mviewConfig->getView($changelog->getViewId())['subscriptions'][$this->getTableName()];
220224
$columns = [
221225
'column_names' => [
222226
'entity_id' => $this->connection->quoteIdentifier($changelog->getColumnName())
223227
],
224228
'column_values' => [
225-
'entity_id' => $this->getEntityColumn($prefix)
229+
'entity_id' => $this->getEntityColumn($prefix, $view)
226230
]
227231
];
228232

@@ -241,12 +245,14 @@ protected function prepareColumns(ChangelogInterface $changelog, string $event):
241245
* Build trigger statement for INSERT, UPDATE, DELETE events
242246
*
243247
* @param string $event
244-
* @param \Magento\Framework\Mview\View\ChangelogInterface $changelog
248+
* @param ViewInterface $view
245249
* @return string
246250
*/
247-
protected function buildStatement($event, $changelog)
251+
protected function buildStatement(string $event, ViewInterface $view): string
248252
{
249253
$trigger = "%sINSERT IGNORE INTO %s (%s) VALUES (%s);";
254+
$changelog = $view->getChangelog();
255+
250256
switch ($event) {
251257
case Trigger::EVENT_UPDATE:
252258
$tableName = $this->resource->getTableName($this->getTableName());
@@ -279,13 +285,14 @@ protected function buildStatement($event, $changelog)
279285
}
280286
break;
281287
}
282-
$columns = $this->prepareColumns($changelog, $event);
288+
$columns = $this->prepareColumns($view, $event);
289+
283290
return sprintf(
284291
$trigger,
285292
$this->getProcessor()->getPreStatements(),
286293
$this->connection->quoteIdentifier($this->resource->getTableName($changelog->getName())),
287-
implode(", " , $columns['column_names']),
288-
implode(", ", $columns['column_values'])
294+
implode(', ', $columns['column_names']),
295+
implode(', ', $columns['column_values'])
289296
);
290297
}
291298

@@ -312,11 +319,28 @@ private function getProcessor(): AdditionalColumnProcessorInterface
312319

313320
/**
314321
* @param string $prefix
322+
* @param ViewInterface $view
323+
* @return string
324+
*/
325+
public function getEntityColumn(string $prefix, ViewInterface $view): string
326+
{
327+
return $prefix . $this->connection->quoteIdentifier($this->getSubscriptionColumn($view));
328+
}
329+
330+
/**
331+
* Returns subscription column name by view
332+
*
333+
* @param ViewInterface $view
315334
* @return string
316335
*/
317-
public function getEntityColumn(string $prefix): string
336+
private function getSubscriptionColumn(ViewInterface $view): string
318337
{
319-
return $prefix . $this->connection->quoteIdentifier($this->getColumnName());
338+
$subscriptions = $view->getSubscriptions();
339+
if (!isset($subscriptions[$this->getTableName()]['column'])) {
340+
throw new \RuntimeException(sprintf('Column name for view with id "%s" doesn\'t exist', $view->getId()));
341+
}
342+
343+
return $subscriptions[$this->getTableName()]['column'];
320344
}
321345

322346
/**
@@ -338,7 +362,7 @@ private function getAfterEventTriggerName($event)
338362
/**
339363
* Retrieve View related to subscription
340364
*
341-
* @return \Magento\Framework\Mview\ViewInterface
365+
* @return ViewInterface
342366
* @codeCoverageIgnore
343367
*/
344368
public function getView()

0 commit comments

Comments
 (0)