27 const FLAG_NAME =
'catalog_website_attribute_is_sync_required';
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,
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,
63 private $groupedStoreViews = [];
68 private $processedAttributeValues = [];
73 private $resourceConnection;
88 private $batchQueryGenerator;
93 private $metaDataPool;
98 private $linkFields = [];
114 $this->connection = $this->resourceConnection->getConnection();
116 $this->batchQueryGenerator = $batchQueryGenerator;
127 $this->markSynchronizationInProgress();
128 $this->connection->beginTransaction();
130 foreach (array_keys($this->tableMetaDataClass) as
$tableName) {
134 $this->markSynchronized();
135 $this->connection->commit();
136 }
catch (\Exception $exception) {
137 $this->connection->rollBack();
148 return self::FLAG_REQUIRES_SYNCHRONIZATION === $this->flagManager->getFlagData(self::FLAG_NAME);
157 $this->flagManager->saveFlag(self::FLAG_NAME, self::FLAG_REQUIRES_SYNCHRONIZATION);
164 private function markSynchronizationInProgress()
166 $this->flagManager->saveFlag(self::FLAG_NAME, self::FLAG_SYNCHRONIZATION_IN_PROGRESS);
173 private function markSynchronized()
175 $this->flagManager->saveFlag(self::FLAG_NAME, self::FLAG_SYNCHRONIZED);
184 foreach ($this->fetchAttributeValues(
$tableName) as $attributeValueItems) {
185 $this->processAttributeValues($attributeValueItems,
$tableName);
195 private function processAttributeValues(array $attributeValueItems,
$tableName)
197 $this->resetProcessedAttributeValues();
199 foreach ($attributeValueItems as $attributeValueItem) {
200 if ($this->isAttributeValueProcessed($attributeValueItem,
$tableName)) {
204 $insertions = $this->generateAttributeValueInsertions($attributeValueItem,
$tableName);
205 if (!empty($insertions)) {
206 $this->executeInsertions($insertions,
$tableName);
209 $this->markAttributeValueProcessed($attributeValueItem,
$tableName);
220 private function fetchAttributeValues(
$tableName)
222 $batchSelectIterator = $this->batchQueryGenerator->generate(
227 [
'cpei' => $this->resourceConnection->getTableName(
$tableName)],
232 'cea' => $this->resourceConnection->getTableName(
'catalog_eav_attribute'),
234 'cpei.attribute_id = cea.attribute_id',
239 'st' => $this->resourceConnection->getTableName(
'store'),
241 'st.store_id = cpei.store_id',
246 self::ATTRIBUTE_WEBSITE
249 'cpei.store_id <> ?',
250 self::GLOBAL_STORE_VIEW_ID
254 foreach ($batchSelectIterator as
$select) {
255 yield $this->connection->fetchAll(
$select);
262 private function getGroupedStoreViews()
264 if (!empty($this->groupedStoreViews)) {
265 return $this->groupedStoreViews;
268 $query = $this->connection
271 $this->resourceConnection->getTableName(
'store'),
275 $storeViews = $this->connection->fetchAll(
$query);
277 $this->groupedStoreViews = [];
279 foreach ($storeViews as $storeView) {
280 if ($storeView[
'store_id'] != 0) {
281 $this->groupedStoreViews[$storeView[
'website_id']][] = $storeView[
'store_id'];
285 return $this->groupedStoreViews;
293 private function isAttributeValueProcessed(array $attributeValue,
$tableName)
296 $this->getAttributeValueKey(
297 $attributeValue[$this->getTableLinkField(
$tableName)],
298 $attributeValue[
'attribute_id'],
299 $attributeValue[
'website_id']
301 $this->processedAttributeValues
309 private function resetProcessedAttributeValues()
311 $this->processedAttributeValues = [];
319 private function markAttributeValueProcessed(array $attributeValue,
$tableName)
321 $this->processedAttributeValues[] = $this->getAttributeValueKey(
322 $attributeValue[$this->getTableLinkField(
$tableName)],
323 $attributeValue[
'attribute_id'],
324 $attributeValue[
'website_id']
334 private function getAttributeValueKey($entityId, $attributeId,
$websiteId)
337 self::MASK_ATTRIBUTE_VALUE,
349 private function generateAttributeValueInsertions(array $attributeValue,
$tableName)
351 $groupedStoreViews = $this->getGroupedStoreViews();
352 if (empty($groupedStoreViews[$attributeValue[
'website_id']])) {
356 $currentStoreViewIds = $groupedStoreViews[$attributeValue[
'website_id']];
359 foreach ($currentStoreViewIds as
$index => $storeViewId) {
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'],
376 private function executeInsertions(array $insertions,
$tableName)
380 %s(attribute_id, store_id, %s, `value`) 383 ON duplicate KEY UPDATE `value` = VALUES(`value`)',
384 $this->resourceConnection->getTableName(
$tableName),
386 $this->prepareInsertValuesStatement($insertions)
389 $this->connection->query($rawQuery, $this->getPlaceholderValues($insertions));
398 private function getPlaceholderValues(array $insertions)
400 $placeholderValues = [];
401 foreach ($insertions as $insertion) {
402 $placeholderValues = array_merge(
408 return $placeholderValues;
417 private function prepareInsertValuesStatement(array $insertions)
421 foreach ($insertions as $insertion) {
422 $statement .= sprintf(
'(%s),', implode(
',', array_keys($insertion)));
425 return rtrim($statement,
',');
433 private function getTableLinkField(
$tableName)
435 if (!isset($this->tableMetaDataClass[
$tableName])) {
436 throw new LocalizedException(
438 'Specified table: %s is not defined in tables list',
445 $this->linkFields[
$tableName] = $this->metaDataPool
446 ->getMetadata($this->tableMetaDataClass[
$tableName])
const MASK_ATTRIBUTE_VALUE
const FLAG_REQUIRES_SYNCHRONIZATION
scheduleSynchronization()
__construct(ResourceConnection $resourceConnection, FlagManager $flagManager, Generator $batchQueryGenerator, MetadataPool $metadataPool)
isSynchronizationRequired()
const FLAG_SYNCHRONIZATION_IN_PROGRESS
const GLOBAL_STORE_VIEW_ID