diff --git a/Model/Sync/Catalog/Data.php b/Model/Sync/Catalog/Data.php index 619fe8fc..3840f8de 100644 --- a/Model/Sync/Catalog/Data.php +++ b/Model/Sync/Catalog/Data.php @@ -6,7 +6,6 @@ use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; use Yotpo\Core\Model\Config as YotpoCoreConfig; -use Magento\Framework\UrlInterface; use Magento\Catalog\Model\Product; use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable; use Magento\Catalog\Model\ProductRepository; @@ -51,109 +50,9 @@ class Data extends Main protected $collectionFactory; /** - * @var array + * @var MagentoProductToYotpoProductAdapter */ - protected $mappingAttributes = [ - 'row_id' => [ - 'default' => 1, - 'attr_code' => 'row_id' - ], - 'external_id' => [ - 'default' => 1, - 'attr_code' => 'entity_id' - ], - 'name' => [ - 'default' => 1, - 'attr_code' => 'name' - ], - 'description' => [ - 'default' => 0, - 'attr_code' => '', - 'method' => 'getProductDescription' - ], - 'url' => [ - 'default' => 1, - 'attr_code' => 'request_path', - 'type' => 'url' - ], - 'image_url' => [ - 'default' => 1, - 'attr_code' => 'image', - 'type' => 'image' - ], - 'price' => [ - 'default' => 0, - 'attr_code' => '', - 'method' => 'getProductPrice' - ], - 'currency' => [ - 'default' => 0, - 'attr_code' => '', - 'method' => 'getCurrentCurrency' - ], - 'inventory_quantity' => [ - 'default' => 0, - 'attr_code' => '', - 'method' => 'getProductQty' - ], - 'is_discontinued' => [ - 'default' => 0, - 'attr_code' => '', - 'method' => '' - ], - 'group_name' => [ - 'default' => 0, - 'attr_code' => 'attr_product_group', - 'method' => 'getDataFromConfig' - ], - 'brand' => [ - 'default' => 0, - 'attr_code' => 'attr_brand', - 'method' => 'getDataFromConfig' - ], - 'sku' => [ - 'default' => 1, - 'attr_code' => 'sku' - ], - 'mpn' => [ - 'default' => 0, - 'attr_code' => 'attr_mpn', - 'method' => 'getDataFromConfig' - ], - 'handle' => [ - 'default' => 1, - 'attr_code' => 'sku' - ], - 'gtins' => [ - 'EAN' => [ - 'default' => 0, - 'attr_code' => 'attr_ean', - 'method' => 'getDataFromConfig' - ], - 'UPC' => [ - 'default' => 0, - 'attr_code' => 'attr_upc', - 'method' => 'getDataFromConfig' - ], - 'ISBN' => [ - 'default' => 0, - 'attr_code' => 'attr_isbn', - 'method' => 'getDataFromConfig' - ] - ], - 'custom_properties' => [ - 'is_blocklisted' => [ - 'default' => 0, - 'attr_code' => 'attr_blocklist', - 'method' => 'getDataFromConfig' - ], - 'review_form_tag' => [ - 'default' => 0, - 'attr_code' => 'attr_crf', - 'method' => 'getDataFromConfig' - ] - ] - ]; + protected $magentoProductToYotpoProductAdapter; /** * @var StockRegistry @@ -175,6 +74,7 @@ class Data extends Main * @param CollectionFactory $collectionFactory * @param StockRegistry $stockRegistry * @param YotpoCoreCatalogLogger $yotpoCatalogLogger + * @param MagentoProductToYotpoProductAdapter $magentoProductToYotpoProductAdapter */ public function __construct( YotpoCoreConfig $yotpoCoreConfig, @@ -184,7 +84,8 @@ public function __construct( ResourceConnection $resourceConnection, CollectionFactory $collectionFactory, StockRegistry $stockRegistry, - YotpoCoreCatalogLogger $yotpoCatalogLogger + YotpoCoreCatalogLogger $yotpoCatalogLogger, + MagentoProductToYotpoProductAdapter $magentoProductToYotpoProductAdapter ) { $this->yotpoCoreConfig = $yotpoCoreConfig; $this->yotpoResource = $yotpoResource; @@ -192,8 +93,8 @@ public function __construct( $this->productRepository = $productRepository; $this->collectionFactory = $collectionFactory; $this->stockRegistry = $stockRegistry; - $this->mappingAttributes['row_id']['attr_code'] = $this->yotpoCoreConfig->getEavRowIdFieldName(); $this->logger = $yotpoCatalogLogger; + $this->magentoProductToYotpoProductAdapter = $magentoProductToYotpoProductAdapter; parent::__construct($resourceConnection); } @@ -214,7 +115,7 @@ public function getSyncItems($items, $isVariantsDataIncluded) $entityId = $item->getData('entity_id'); $productsId[] = $entityId; $productsObject[$entityId] = $item; - $syncItems[$entityId] = $this->attributeMapping($item); + $syncItems[$entityId] = $this->adaptMagentoProductToYotpoProduct($item); } $visibleVariantsData = []; $productIdsToParentIdsMap = []; @@ -360,188 +261,8 @@ protected function prepareOptions($syncItems, $configIds, $productObjects) return $syncItems; } - /** - * Mapping the yotpo data with magento data - product sync - * - * @param Product $item - * @return array - * @throws NoSuchEntityException - */ - public function attributeMapping(Product $item) - { - $itemArray = []; - $mapAttributes = $this->mappingAttributes; - - foreach ($mapAttributes as $key => $attr) { - try { - if ($key === 'gtins') { - $value = $this->prepareGtinsData($attr, $item); - } elseif ($key === 'custom_properties') { - $value = $this->prepareCustomProperties($attr, $item); - } elseif ($key === 'is_discontinued') { - $value = false; - } else { - if ($attr['default']) { - $data = $item->getData($attr['attr_code']); - - if (isset($attr['type']) && $attr['type'] === 'url') { - $data = $item->getProductUrl(); - } - - if (isset($attr['type']) && $attr['type'] === 'image') { - $baseUrl = $this->yotpoCoreConfig->getBaseUrl(UrlInterface::URL_TYPE_MEDIA); - $data = $data ? $baseUrl . 'catalog/product' . $data : null; - } - $value = $data; - } elseif (isset($attr['method']) && $attr['method']) { - - $configKey = isset($attr['attr_code']) && $attr['attr_code'] ? - $attr['attr_code'] : ''; - - $method = $attr['method']; - $itemValue = $this->$method($item, $configKey); - $value = $itemValue ?: ($method == 'getProductPrice' ? 0.00 : $itemValue); - } else { - $value = ''; - } - if ($key == 'group_name' && $value) { - $value = strtolower($value); - $value = str_replace(' ', '_', $value); - $value = preg_replace('/[^A-Za-z0-9_-]/', '-', $value); - $value = substr((string)$value, 0, 100); - } - } - $itemArray[$key] = $value; - if (($key == 'custom_properties' || $key == 'gtins') && !$value) { - unset($itemArray[$key]); - } - } catch (\Exception $e) { - $this->logger->infoLog( - __( - 'Exception raised within attributeMapping - $key: %1, $attr: %2 Exception Message: %3', - $key, - $attr, - $e->getMessage() - ) - ); - } - } - - return $itemArray; - } - - /** - * Get Current Currency - * - * @return string - * @throws NoSuchEntityException - */ - public function getCurrentCurrency() - { - return $this->yotpoCoreConfig->getCurrentCurrency(); - } - - /** - * Get product quantity - * @param Product $item - * @return float - * @throws NoSuchEntityException - */ - public function getProductQty($item) - { - return $this->stockRegistry->getStockItem($item->getId(), $this->yotpoCoreConfig->getWebsiteId())->getQty(); - } - - /** - * Get value from config table - dynamically - * @param Product $item - * @param string $configKey - * @return mixed|string|null - * @throws LocalizedException - * @throws NoSuchEntityException - */ - public function getDataFromConfig($item, $configKey = '') - { - $configValue = $this->yotpoCoreConfig->getConfig($configKey) ?: ''; - if ($configValue) { - $value = $item->getAttributeText($configValue) ?: ''; - if (!$value) { - $value = $item->getData($configValue) ?: ''; - } - } else { - return null; - } - return $value ?: ''; - } - - /** - * Get GTINs data from config table - * - * @param array $array - * @param Product $item - * @return array - */ - protected function prepareGtinsData($array, $item) - { - $resultArray = []; - foreach ($array as $key => $value) { - $configKey = isset($value['attr_code']) && $value['attr_code'] ? - $value['attr_code'] : ''; - $method = $value['method']; - $value = $this->$method($item, $configKey); - - if ($value && $value !== 'NULL') { - $resultArray[] = [ - 'declared_type' => $key, - 'value' => $value - ]; - } - } - return $resultArray; - } - - /** - * Prepare custom attributes for product sync - * - * @param array $array - * @param Product $item - * @return array - */ - protected function prepareCustomProperties($array, $item) - { - $resultArray = []; - foreach ($array as $key => $value) { - $configKey = isset($value['attr_code']) && $value['attr_code'] ? - $value['attr_code'] : ''; - - $method = $value['method']; - $itemValue = $this->$method($item, $configKey); - if ($key === 'is_blocklisted' || $key === 'review_form_tag') { - $configValue = $this->yotpoCoreConfig->getConfig($configKey) ?: ''; - if ($configValue) { - if ($key === 'is_blocklisted') { - $resultArray[$key] = $itemValue === 1 || $itemValue == 'Yes' || $itemValue === true; - } elseif ($key === 'review_form_tag') { - $itemValue = str_replace(',', '_', $itemValue); - $itemValue = substr($itemValue, 0, 255); - $resultArray[$key] = $itemValue ?: ''; - } - } - } else { - $resultArray[$key] = $itemValue; - } - } - return $resultArray; - } - - /** - * Get product price - * @param Product $item - * @return float - */ - public function getProductPrice($item) - { - return $item->getPrice() ?: 0.00; + public function adaptMagentoProductToYotpoProduct(Product $item) { + return $this->magentoProductToYotpoProductAdapter->adapt($item); } /** @@ -561,17 +282,6 @@ public function filterDataForCatSync($sqlData) return $result; } - /** - * Get product description - * @param Product $item - * @return string - */ - public function getProductDescription(Product $item) - { - return trim($item->getData('description')) ?: - trim($item->getData('short_description')); - } - /** * @param array $yotpoItemData * @return array diff --git a/Model/Sync/Catalog/MagentoProductToYotpoProductAdapter.php b/Model/Sync/Catalog/MagentoProductToYotpoProductAdapter.php new file mode 100644 index 00000000..b08c3405 --- /dev/null +++ b/Model/Sync/Catalog/MagentoProductToYotpoProductAdapter.php @@ -0,0 +1,315 @@ + 'getEan', + 'UPC' => 'getUpc', + 'ISBN' => 'getIsbn' + ]; + + const CUSTOM_ATTRIBUTE_TO_GETTER_METHOD_NAME_MAP = [ + 'is_blocklisted' => 'getIsBlocklisted', + 'review_form_tag' => 'getReviewFormTag' + ]; + + const CONFIGURABLE_PRODUCT_CODE = \Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE; + + const ENTITY_ID_ATTRIBUTE_NAME = 'entity_id'; + + const PRODUCT_NAME_ATTRIBUTE_NAME = 'name'; + + const SKU_ATTRIBUTE_NAME = 'sku'; + + const DESCRIPTION_ATTRIBUTE_NAME = 'description'; + + const SHORT_DESCRIPTION_ATTRIBUTE_NAME = 'short_description'; + + const BRAND_CONFIG_KEY = 'attr_brand'; + + const MPN_CONFIG_KEY = 'attr_mpn'; + + const UPC_CONFIG_KEY = 'attr_upc'; + + const ISBN_CONFIG_KEY = 'attr_isbn'; + + const EAN_CONFIG_KEY = 'attr_ean'; + + const GROUP_NAME_CONFIG_KEY = 'attr_product_group'; + + const BLOCKLISTED_CONFIG_KEY = 'attr_blocklist'; + + const CRF_CONFIG_KEY = 'attr_crf'; + + const GTIN_DECLARED_TYPE_KEY = 'declared_type'; + + const GTIN_VALUE_KEY = 'value'; + + /** + * @var StockRegistry + */ + protected $stockRegistry; + + /** + * @var ProductRepository + */ + protected $productRepository; + + /** + * @var YotpoCoreConfig + */ + protected $yotpoCoreConfig; + + /** + * @param StockRegistry $stockRegistry + * @param ProductRepository $productRepository + * @param YotpoCoreConfig $yotpoCoreConfig + */ + public function __construct( + StockRegistry $stockRegistry, + ProductRepository $productRepository, + YotpoCoreConfig $yotpoCoreConfig + ) { + $this->stockRegistry = $stockRegistry; + $this->productRepository = $productRepository; + $this->yotpoCoreConfig = $yotpoCoreConfig; + } + + /** + * @param Product $item + * @return array + */ + public function adapt(Product $item) { + $yotpoProduct = []; + + $yotpoProduct['row_id'] = $this->getRowId($item); + $yotpoProduct['external_id'] = $item->getData(self::ENTITY_ID_ATTRIBUTE_NAME); + $yotpoProduct['name'] = $item->getData(self::PRODUCT_NAME_ATTRIBUTE_NAME); + $yotpoProduct['description'] = $this->getDescription($item); + $yotpoProduct['url'] = $item->getProductUrl(); + $yotpoProduct['image_url'] = $this->getImageUrl($item); + $yotpoProduct['price'] = $this->getPrice($item); + $yotpoProduct['currency'] = $this->yotpoCoreConfig->getCurrentCurrency(); + $yotpoProduct['inventory_quantity'] = $this->getInventoryQuantity($item); + $yotpoProduct['is_discontinued'] = false; + $yotpoProduct['group_name'] = $this->getGroupName($item); + $yotpoProduct['brand'] = $this->getAttributeValueForItemByConfigKey($item, self::BRAND_CONFIG_KEY); + $yotpoProduct['sku'] = $item->getData(self::SKU_ATTRIBUTE_NAME); + $yotpoProduct['mpn'] = $this->getAttributeValueForItemByConfigKey($item, self::MPN_CONFIG_KEY); + $yotpoProduct['handle'] = $item->getData(self::SKU_ATTRIBUTE_NAME); + + $gtins = $this->getGtins($item); + if ($gtins) { + $yotpoProduct['gtins'] = $gtins; + } + + $customProperties = $this->getCustomProperties($item); + if ($customProperties) { + $yotpoProduct['custom_properties'] = $customProperties; + } + + return $yotpoProduct; + } + + /** + * @param Product $item + * @return mixed + */ + private function getRowId(Product $item) { + $rowIdFieldName = $this->yotpoCoreConfig->getEavRowIdFieldName(); + return $item->getData($rowIdFieldName); + } + + /** + * @param Product $item + * @return string + */ + private function getDescription(Product $item) { + $description = $item->getData(self::DESCRIPTION_ATTRIBUTE_NAME); + if ($description) { + return trim($description); + } + + $shortDescription = $item->getData(self::SHORT_DESCRIPTION_ATTRIBUTE_NAME); + if ($shortDescription) { + return trim($shortDescription); + } + + return null; + } + + /** + * @param Product $item + * @return string|null + */ + private function getImageUrl(Product $item) { + $image = $item->getData('image'); + $baseUrl = $this->yotpoCoreConfig->getBaseUrl(UrlInterface::URL_TYPE_MEDIA); + return $image ? $baseUrl . 'catalog/product' . $image : null; + } + + /** + * @param Product $item + * @return float + */ + private function getPrice(Product $item) { + if ($item->getTypeId() == self::CONFIGURABLE_PRODUCT_CODE) { + $itemVariantIdsObject = $item->getTypeInstance()->getChildrenIds($item->getId()); + if (isset($itemVariantIdsObject[0]) && count($itemVariantIdsObject[0]) > 0) { + $itemVariantIds = $itemVariantIdsObject[0]; + $firstVariantId = reset($itemVariantIds); + $variant = $this->productRepository->getById($firstVariantId); + return $variant->getPrice() ?: 0.00; + } + } + + return $item->getPrice() ?: 0.00; + } + + /** + * @param Product $item + * @return mixed + */ + private function getInventoryQuantity(Product $item) { + return $this->stockRegistry->getStockItem($item->getId(), $this->yotpoCoreConfig->getWebsiteId())->getQty(); + } + + /** + * @param Product $item + * @return false|mixed|string|null + */ + private function getGroupName(Product $item) { + $groupName = $this->getAttributeValueForItemByConfigKey($item, self::GROUP_NAME_CONFIG_KEY); + if ($groupName) { + $groupName = strtolower($groupName); + $groupName = str_replace(' ', '_', $groupName); + $groupName = preg_replace('/[^A-Za-z0-9_-]/', '-', $groupName); + $groupName = substr((string) $groupName, 0, 100); + } + + return $groupName; + } + + /** + * @param Product $item + * @return array + */ + private function getGtins(Product $item) { + $gtins = []; + + foreach (self::GTIN_TYPE_TO_GETTER_METHOD_NAME_MAP as $gtinType => $getterMethodName) { + $gtinValue = $this->$getterMethodName($item); + if ($gtinValue && $gtinValue !== 'NULL') { + $gtins[] = [ + self::GTIN_DECLARED_TYPE_KEY => $gtinType, + self::GTIN_VALUE_KEY => $gtinValue + ]; + } + } + + return $gtins; + } + + /** + * @param Product $item + * @return mixed|string|null + */ + private function getEan(Product $item) { + return $this->getAttributeValueForItemByConfigKey($item, self::EAN_CONFIG_KEY); + } + + /** + * @param Product $item + * @return mixed|string|null + */ + private function getUpc(Product $item) { + return $this->getAttributeValueForItemByConfigKey($item, self::UPC_CONFIG_KEY); + } + + /** + * @param Product $item + * @return mixed|string|null + */ + private function getIsbn(Product $item) { + return $this->getAttributeValueForItemByConfigKey($item, self::ISBN_CONFIG_KEY); + } + + /** + * @param Product $item + * @return array + */ + private function getCustomProperties(Product $item) { + $customProperties = []; + + foreach (self::CUSTOM_ATTRIBUTE_TO_GETTER_METHOD_NAME_MAP as $customPropertyType => $getterMethodName) { + $customPropertyValue = $this->$getterMethodName($item); + if ($customPropertyValue && $customPropertyValue !== 'NULL') { + $customProperties[$customPropertyType] = $customPropertyValue; + } + } + + return $customProperties; + } + + /** + * @param Product $item + * @return bool + */ + private function getIsBlocklisted(Product $item) { + $isBlocklisted = $this->getAttributeValueForItemByConfigKey($item, self::BLOCKLISTED_CONFIG_KEY); + return $isBlocklisted === 1 || $isBlocklisted == 'Yes' || $isBlocklisted === true; + } + + /** + * @param Product $item + * @return false|string + */ + private function getReviewFormTag(Product $item) { + $reviewFormTag = $this->getAttributeValueForItemByConfigKey($item, self::CRF_CONFIG_KEY); + if ($reviewFormTag === null) { + return ''; + } + + $reviewFormTag = str_replace(',', '_', $reviewFormTag); + $reviewFormTag = substr($reviewFormTag, 0, 255); + return $reviewFormTag ?: ''; + } + + /** + * @param Product $item + * @param string $configKey + * @return mixed|string|null + * @throws LocalizedException + * @throws NoSuchEntityException + */ + private function getAttributeValueForItemByConfigKey($item, $configKey) + { + $attributeKey = $this->getAttributeKeyByConfigKey($configKey); + if ($attributeKey) { + $attributeValue = $item->getAttributeText($attributeKey) ?: $item->getData($attributeKey); + } else { + return null; + } + return $attributeValue ?: ''; + } + + /** + * @param string $configKey + * @return string + */ + private function getAttributeKeyByConfigKey($configKey) { + return $this->yotpoCoreConfig->getConfig($configKey) ?: ''; + } +} diff --git a/Model/Sync/Catalog/Processor.php b/Model/Sync/Catalog/Processor.php index 78090327..972aa638 100644 --- a/Model/Sync/Catalog/Processor.php +++ b/Model/Sync/Catalog/Processor.php @@ -978,7 +978,7 @@ private function ensureEntityExistenceAsProductInYotpo( false ); /** @phpstan-ignore-next-line */ - $productData = $this->catalogData->attributeMapping(reset($parentProductData)); + $productData = $this->catalogData->adaptMagentoProductToYotpoProduct(reset($parentProductData)); if (isset($productData['row_id'])) { unset($productData['row_id']); } diff --git a/Observer/Category/SaveBefore.php b/Observer/Category/SaveBefore.php index 489e22a1..6388ef4f 100644 --- a/Observer/Category/SaveBefore.php +++ b/Observer/Category/SaveBefore.php @@ -87,15 +87,16 @@ public function execute(Observer $observer) $categoryId ); + $productIdToPositionInCategoryStringMapBeforeSave = $this->request->getParam('vm_category_products'); + if ($productIdToPositionInCategoryStringMapBeforeSave === null) { + return; + } + $productIdToPositionInCategoryMapBeforeSave = json_decode( - $this->request->getParam('vm_category_products'), + $productIdToPositionInCategoryStringMapBeforeSave, true ); - if ($productIdToPositionInCategoryMapBeforeSave === null) { - return; - } - $productIdsInCategoryBeforeSave = array_keys($productIdToPositionInCategoryMapBeforeSave); $productsAddedToCategory = array_diff($productIdsInCategoryBeforeSave, $currentProductIdsInCategory); $productsDeletedFromCategory = array_diff($currentProductIdsInCategory, $productIdsInCategoryBeforeSave); diff --git a/composer.json b/composer.json index 6c6969f6..c4710fc1 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "yotpo/module-yotpo-core", "description": "Yotpo Reviews core extension for Magento2", - "version": "4.0.31", + "version": "4.0.32", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/etc/module.xml b/etc/module.xml index ade8aaeb..d43d46e8 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -1,6 +1,6 @@ - +