123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- namespace Magento\Catalog\Setup\Patch\Data;
- use Magento\Catalog\Api\Data\CategoryInterface;
- use Magento\Catalog\Api\Data\ProductInterface;
- use Magento\Framework\DB\Query\Generator;
- use Magento\Framework\EntityManager\MetadataPool;
- use Magento\Framework\Exception\LocalizedException;
- use Magento\Framework\Setup\ModuleDataSetupInterface;
- use Magento\Framework\Setup\Patch\DataPatchInterface;
- use Magento\Framework\Setup\Patch\PatchVersionInterface;
- /**
- * Class UpgradeWebsiteAttributes
- * @package Magento\Catalog\Setup\Patch
- *
- * IMPORTANT: This class const/methods can not be reused because it needs to be isolated
- */
- class UpgradeWebsiteAttributes implements DataPatchInterface, PatchVersionInterface
- {
- /**
- * ATTENTION: These constants must not be reused anywhere outside
- */
- const ATTRIBUTE_WEBSITE = 2;
- const GLOBAL_STORE_VIEW_ID = 0;
- const MASK_ATTRIBUTE_VALUE = '%d_%d_%d';
- /**
- * Map table names to metadata classes where link field might be found
- */
- private $tableMetaDataClass = [
- 'catalog_category_entity_datetime' => CategoryInterface::class,
- 'catalog_category_entity_decimal' => CategoryInterface::class,
- 'catalog_category_entity_int' => CategoryInterface::class,
- 'catalog_category_entity_text' => CategoryInterface::class,
- 'catalog_category_entity_varchar' => CategoryInterface::class,
- 'catalog_product_entity_datetime' => ProductInterface::class,
- 'catalog_product_entity_decimal' => ProductInterface::class,
- 'catalog_product_entity_int' => ProductInterface::class,
- 'catalog_product_entity_text' => ProductInterface::class,
- 'catalog_product_entity_varchar' => ProductInterface::class,
- ];
- /**
- * Internal format :
- * [
- * website_id => [
- * store_view_id_1,
- * store_view_id_2,
- * ...
- * ]
- * ]
- *
- * @var array
- */
- private $groupedStoreViews = [];
- /**
- * @var array
- */
- private $processedAttributeValues = [];
- /**
- * @var Generator
- */
- private $batchQueryGenerator;
- /**
- * @var MetadataPool
- */
- private $metaDataPool;
- /**
- * @var array
- */
- private $linkFields = [];
- /**
- * @var ModuleDataSetupInterface
- */
- private $moduleDataSetup;
- /**
- * UpgradeWebsiteAttributes constructor.
- * @param Generator $batchQueryGenerator
- * @param MetadataPool $metadataPool
- * @param ModuleDataSetupInterface $moduleDataSetup
- */
- public function __construct(
- Generator $batchQueryGenerator,
- MetadataPool $metadataPool,
- ModuleDataSetupInterface $moduleDataSetup
- ) {
- $this->batchQueryGenerator = $batchQueryGenerator;
- $this->metaDataPool = $metadataPool;
- $this->moduleDataSetup = $moduleDataSetup;
- }
- /**
- * {@inheritdoc}
- */
- public function apply()
- {
- foreach (array_keys($this->tableMetaDataClass) as $tableName) {
- $this->upgradeTable($tableName);
- }
- }
- /**
- * @param string $tableName
- * @return void
- */
- private function upgradeTable($tableName)
- {
- foreach ($this->fetchAttributeValues($tableName) as $attributeValueItems) {
- $this->processAttributeValues($attributeValueItems, $tableName);
- }
- }
- /**
- * Aligns website attribute values
- * @param array $attributeValueItems
- * @param string $tableName
- * @return void
- */
- private function processAttributeValues(array $attributeValueItems, $tableName)
- {
- $this->resetProcessedAttributeValues();
- foreach ($attributeValueItems as $attributeValueItem) {
- if ($this->isProcessedAttributeValue($attributeValueItem, $tableName)) {
- continue;
- }
- $insertions = $this->generateAttributeValueInsertions($attributeValueItem, $tableName);
- if (!empty($insertions)) {
- $this->executeInsertions($insertions, $tableName);
- }
- $this->markAttributeValueProcessed($attributeValueItem, $tableName);
- }
- }
- /**
- * Yields batch of AttributeValues
- *
- * @param string $tableName
- * @yield array
- * @return \Generator
- */
- private function fetchAttributeValues($tableName)
- {
- //filter store groups which have more than 1 store
- $multipleStoresInWebsite = array_values(
- array_reduce(
- array_filter($this->getGroupedStoreViews(), function ($storeViews) {
- return is_array($storeViews) && count($storeViews) > 1;
- }),
- 'array_merge',
- []
- )
- );
- if (count($multipleStoresInWebsite) < 1) {
- return [];
- }
- $connection = $this->moduleDataSetup->getConnection();
- $batchSelectIterator = $this->batchQueryGenerator->generate(
- 'value_id',
- $connection
- ->select()
- ->from(
- ['cpei' => $this->moduleDataSetup->getTable($tableName)],
- '*'
- )
- ->join(
- [
- 'cea' => $this->moduleDataSetup->getTable('catalog_eav_attribute'),
- ],
- 'cpei.attribute_id = cea.attribute_id',
- ''
- )
- ->join(
- [
- 'st' => $this->moduleDataSetup->getTable('store'),
- ],
- 'st.store_id = cpei.store_id',
- 'st.website_id'
- )
- ->where(
- 'cea.is_global = ?',
- self::ATTRIBUTE_WEBSITE
- )
- ->where(
- 'cpei.store_id IN (?)',
- $multipleStoresInWebsite
- ),
- 1000
- );
- foreach ($batchSelectIterator as $select) {
- yield $connection->fetchAll($select);
- }
- }
- /**
- * @return array
- */
- private function getGroupedStoreViews()
- {
- if (!empty($this->groupedStoreViews)) {
- return $this->groupedStoreViews;
- }
- $connection = $this->moduleDataSetup->getConnection();
- $query = $connection
- ->select()
- ->from(
- $this->moduleDataSetup->getTable('store'),
- '*'
- );
- $storeViews = $connection->fetchAll($query);
- $this->groupedStoreViews = [];
- foreach ($storeViews as $storeView) {
- if ($storeView['store_id'] != 0) {
- $this->groupedStoreViews[$storeView['website_id']][] = $storeView['store_id'];
- }
- }
- return $this->groupedStoreViews;
- }
- /**
- * @param array $attributeValue
- * @param string $tableName
- * @return bool
- */
- private function isProcessedAttributeValue(array $attributeValue, $tableName)
- {
- return in_array(
- $this->getAttributeValueKey(
- $attributeValue[$this->getTableLinkField($tableName)],
- $attributeValue['attribute_id'],
- $attributeValue['website_id']
- ),
- $this->processedAttributeValues
- );
- }
- /**
- * Resets processed attribute values
- * @return void
- */
- private function resetProcessedAttributeValues()
- {
- $this->processedAttributeValues = [];
- }
- /**
- * @param array $attributeValue
- * @param string $tableName
- * @return void
- */
- private function markAttributeValueProcessed(array $attributeValue, $tableName)
- {
- $this->processedAttributeValues[] = $this->getAttributeValueKey(
- $attributeValue[$this->getTableLinkField($tableName)],
- $attributeValue['attribute_id'],
- $attributeValue['website_id']
- );
- }
- /**
- * @param int $entityId
- * @param int $attributeId
- * @param int $websiteId
- * @return string
- */
- private function getAttributeValueKey($entityId, $attributeId, $websiteId)
- {
- return sprintf(
- self::MASK_ATTRIBUTE_VALUE,
- $entityId,
- $attributeId,
- $websiteId
- );
- }
- /**
- * @param array $attributeValue
- * @param string $tableName
- * @return array|null
- */
- private function generateAttributeValueInsertions(
- array $attributeValue,
- $tableName
- ) {
- $groupedStoreViews = $this->getGroupedStoreViews();
- if (empty($groupedStoreViews[$attributeValue['website_id']])) {
- return null;
- }
- $currentStoreViewIds = $groupedStoreViews[$attributeValue['website_id']];
- $insertions = [];
- foreach ($currentStoreViewIds as $index => $storeViewId) {
- $insertions[] = [
- ':attribute_id' . $index => $attributeValue['attribute_id'],
- ':store_id' . $index => $storeViewId,
- ':entity_id' . $index => $attributeValue[$this->getTableLinkField($tableName)],
- ':value' . $index => $attributeValue['value'],
- ];
- }
- return $insertions;
- }
- /**
- * @param array $insertions
- * @param string $tableName
- * @return void
- */
- private function executeInsertions(array $insertions, $tableName)
- {
- $rawQuery = sprintf(
- 'INSERT INTO
- %s(attribute_id, store_id, %s, `value`)
- VALUES
- %s
- ON duplicate KEY UPDATE `value` = VALUES(`value`)',
- $this->moduleDataSetup->getTable($tableName),
- $this->getTableLinkField($tableName),
- $this->prepareInsertValuesStatement($insertions)
- );
- $this->moduleDataSetup->getConnection()->query($rawQuery, $this->getPlaceholderValues($insertions));
- }
- /**
- * Maps $insertions hierarchy to single-level $placeholder => $value array
- *
- * @param array $insertions
- * @return array
- */
- private function getPlaceholderValues(array $insertions)
- {
- $placeholderValues = [];
- foreach ($insertions as $insertion) {
- $placeholderValues = array_merge(
- $placeholderValues,
- $insertion
- );
- }
- return $placeholderValues;
- }
- /**
- * Extracts from $insertions values placeholders and turns it into query statement view
- *
- * @param array $insertions
- * @return string
- */
- private function prepareInsertValuesStatement(array $insertions)
- {
- $statement = '';
- foreach ($insertions as $insertion) {
- $statement .= sprintf('(%s),', implode(',', array_keys($insertion)));
- }
- return rtrim($statement, ',');
- }
- /**
- * @param string $tableName
- * @return string
- * @throws LocalizedException
- */
- private function getTableLinkField($tableName)
- {
- if (!isset($this->tableMetaDataClass[$tableName])) {
- throw new LocalizedException(
- sprintf(
- 'Specified table: %s is not defined in tables list',
- $tableName
- )
- );
- }
- if (!isset($this->linkFields[$tableName])) {
- $this->linkFields[$tableName] = $this->metaDataPool
- ->getMetadata($this->tableMetaDataClass[$tableName])
- ->getLinkField();
- }
- return $this->linkFields[$tableName];
- }
- /**
- * {@inheritdoc}
- */
- public static function getDependencies()
- {
- return [
- UpgradeWidgetData::class,
- ];
- }
- /**
- * {@inheritdoc}
- */
- public static function getVersion()
- {
- return '2.2.2';
- }
- /**
- * {@inheritdoc}
- */
- public function getAliases()
- {
- return [];
- }
- }
|