Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
WebsiteAttributesSynchronizer.php
Go to the documentation of this file.
1 <?php
8 
17 
23 {
24  const FLAG_SYNCHRONIZED = 0;
27  const FLAG_NAME = 'catalog_website_attribute_is_sync_required';
28 
29  const ATTRIBUTE_WEBSITE = 2;
31 
32  const MASK_ATTRIBUTE_VALUE = '%d_%d_%d';
33 
37  private $tableMetaDataClass = [
38  'catalog_category_entity_datetime' => CategoryInterface::class,
39  'catalog_category_entity_decimal' => CategoryInterface::class,
40  'catalog_category_entity_int' => CategoryInterface::class,
41  'catalog_category_entity_text' => CategoryInterface::class,
42  'catalog_category_entity_varchar' => CategoryInterface::class,
43 
44  'catalog_product_entity_datetime' => ProductInterface::class,
45  'catalog_product_entity_decimal' => ProductInterface::class,
46  'catalog_product_entity_int' => ProductInterface::class,
47  'catalog_product_entity_text' => ProductInterface::class,
48  'catalog_product_entity_varchar' => ProductInterface::class,
49  ];
50 
63  private $groupedStoreViews = [];
64 
68  private $processedAttributeValues = [];
69 
73  private $resourceConnection;
74 
78  private $connection;
79 
83  private $flagManager;
84 
88  private $batchQueryGenerator;
89 
93  private $metaDataPool;
94 
98  private $linkFields = [];
99 
107  public function __construct(
108  ResourceConnection $resourceConnection,
109  FlagManager $flagManager,
110  Generator $batchQueryGenerator,
112  ) {
113  $this->resourceConnection = $resourceConnection;
114  $this->connection = $this->resourceConnection->getConnection();
115  $this->flagManager = $flagManager;
116  $this->batchQueryGenerator = $batchQueryGenerator;
117  $this->metaDataPool = $metadataPool;
118  }
119 
125  public function synchronize()
126  {
127  $this->markSynchronizationInProgress();
128  $this->connection->beginTransaction();
129  try {
130  foreach (array_keys($this->tableMetaDataClass) as $tableName) {
131  $this->synchronizeTable($tableName);
132  }
133 
134  $this->markSynchronized();
135  $this->connection->commit();
136  } catch (\Exception $exception) {
137  $this->connection->rollBack();
138  $this->scheduleSynchronization();
139  throw $exception;
140  }
141  }
142 
146  public function isSynchronizationRequired()
147  {
148  return self::FLAG_REQUIRES_SYNCHRONIZATION === $this->flagManager->getFlagData(self::FLAG_NAME);
149  }
150 
155  public function scheduleSynchronization()
156  {
157  $this->flagManager->saveFlag(self::FLAG_NAME, self::FLAG_REQUIRES_SYNCHRONIZATION);
158  }
159 
164  private function markSynchronizationInProgress()
165  {
166  $this->flagManager->saveFlag(self::FLAG_NAME, self::FLAG_SYNCHRONIZATION_IN_PROGRESS);
167  }
168 
173  private function markSynchronized()
174  {
175  $this->flagManager->saveFlag(self::FLAG_NAME, self::FLAG_SYNCHRONIZED);
176  }
177 
182  private function synchronizeTable($tableName)
183  {
184  foreach ($this->fetchAttributeValues($tableName) as $attributeValueItems) {
185  $this->processAttributeValues($attributeValueItems, $tableName);
186  }
187  }
188 
195  private function processAttributeValues(array $attributeValueItems, $tableName)
196  {
197  $this->resetProcessedAttributeValues();
198 
199  foreach ($attributeValueItems as $attributeValueItem) {
200  if ($this->isAttributeValueProcessed($attributeValueItem, $tableName)) {
201  continue;
202  }
203 
204  $insertions = $this->generateAttributeValueInsertions($attributeValueItem, $tableName);
205  if (!empty($insertions)) {
206  $this->executeInsertions($insertions, $tableName);
207  }
208 
209  $this->markAttributeValueProcessed($attributeValueItem, $tableName);
210  }
211  }
212 
220  private function fetchAttributeValues($tableName)
221  {
222  $batchSelectIterator = $this->batchQueryGenerator->generate(
223  'value_id',
224  $this->connection
225  ->select()
226  ->from(
227  ['cpei' => $this->resourceConnection->getTableName($tableName)],
228  '*'
229  )
230  ->join(
231  [
232  'cea' => $this->resourceConnection->getTableName('catalog_eav_attribute'),
233  ],
234  'cpei.attribute_id = cea.attribute_id',
235  ''
236  )
237  ->join(
238  [
239  'st' => $this->resourceConnection->getTableName('store'),
240  ],
241  'st.store_id = cpei.store_id',
242  'st.website_id'
243  )
244  ->where(
245  'cea.is_global = ?',
246  self::ATTRIBUTE_WEBSITE
247  )
248  ->where(
249  'cpei.store_id <> ?',
250  self::GLOBAL_STORE_VIEW_ID
251  )
252  );
253 
254  foreach ($batchSelectIterator as $select) {
255  yield $this->connection->fetchAll($select);
256  }
257  }
258 
262  private function getGroupedStoreViews()
263  {
264  if (!empty($this->groupedStoreViews)) {
265  return $this->groupedStoreViews;
266  }
267 
268  $query = $this->connection
269  ->select()
270  ->from(
271  $this->resourceConnection->getTableName('store'),
272  '*'
273  );
274 
275  $storeViews = $this->connection->fetchAll($query);
276 
277  $this->groupedStoreViews = [];
278 
279  foreach ($storeViews as $storeView) {
280  if ($storeView['store_id'] != 0) {
281  $this->groupedStoreViews[$storeView['website_id']][] = $storeView['store_id'];
282  }
283  }
284 
285  return $this->groupedStoreViews;
286  }
287 
293  private function isAttributeValueProcessed(array $attributeValue, $tableName)
294  {
295  return in_array(
296  $this->getAttributeValueKey(
297  $attributeValue[$this->getTableLinkField($tableName)],
298  $attributeValue['attribute_id'],
299  $attributeValue['website_id']
300  ),
301  $this->processedAttributeValues
302  );
303  }
304 
309  private function resetProcessedAttributeValues()
310  {
311  $this->processedAttributeValues = [];
312  }
313 
319  private function markAttributeValueProcessed(array $attributeValue, $tableName)
320  {
321  $this->processedAttributeValues[] = $this->getAttributeValueKey(
322  $attributeValue[$this->getTableLinkField($tableName)],
323  $attributeValue['attribute_id'],
324  $attributeValue['website_id']
325  );
326  }
327 
334  private function getAttributeValueKey($entityId, $attributeId, $websiteId)
335  {
336  return sprintf(
337  self::MASK_ATTRIBUTE_VALUE,
338  $entityId,
339  $attributeId,
340  $websiteId
341  );
342  }
343 
349  private function generateAttributeValueInsertions(array $attributeValue, $tableName)
350  {
351  $groupedStoreViews = $this->getGroupedStoreViews();
352  if (empty($groupedStoreViews[$attributeValue['website_id']])) {
353  return null;
354  }
355 
356  $currentStoreViewIds = $groupedStoreViews[$attributeValue['website_id']];
357  $insertions = [];
358 
359  foreach ($currentStoreViewIds as $index => $storeViewId) {
360  $insertions[] = [
361  ':attribute_id' . $index => $attributeValue['attribute_id'],
362  ':store_id' . $index => $storeViewId,
363  ':entity_id' . $index => $attributeValue[$this->getTableLinkField($tableName)],
364  ':value' . $index => $attributeValue['value'],
365  ];
366  }
367 
368  return $insertions;
369  }
370 
376  private function executeInsertions(array $insertions, $tableName)
377  {
378  $rawQuery = sprintf(
379  'INSERT INTO
380  %s(attribute_id, store_id, %s, `value`)
381  VALUES
382  %s
383  ON duplicate KEY UPDATE `value` = VALUES(`value`)',
384  $this->resourceConnection->getTableName($tableName),
385  $this->getTableLinkField($tableName),
386  $this->prepareInsertValuesStatement($insertions)
387  );
388 
389  $this->connection->query($rawQuery, $this->getPlaceholderValues($insertions));
390  }
391 
398  private function getPlaceholderValues(array $insertions)
399  {
400  $placeholderValues = [];
401  foreach ($insertions as $insertion) {
402  $placeholderValues = array_merge(
403  $placeholderValues,
404  $insertion
405  );
406  }
407 
408  return $placeholderValues;
409  }
410 
417  private function prepareInsertValuesStatement(array $insertions)
418  {
419  $statement = '';
420 
421  foreach ($insertions as $insertion) {
422  $statement .= sprintf('(%s),', implode(',', array_keys($insertion)));
423  }
424 
425  return rtrim($statement, ',');
426  }
427 
433  private function getTableLinkField($tableName)
434  {
435  if (!isset($this->tableMetaDataClass[$tableName])) {
436  throw new LocalizedException(
437  sprintf(
438  'Specified table: %s is not defined in tables list',
439  $tableName
440  )
441  );
442  }
443 
444  if (!isset($this->linkFields[$tableName])) {
445  $this->linkFields[$tableName] = $this->metaDataPool
446  ->getMetadata($this->tableMetaDataClass[$tableName])
447  ->getLinkField();
448  }
449 
450  return $this->linkFields[$tableName];
451  }
452 }
$tableName
Definition: trigger.php:13
__construct(ResourceConnection $resourceConnection, FlagManager $flagManager, Generator $batchQueryGenerator, MetadataPool $metadataPool)
$index
Definition: list.phtml:44