![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/old/vendor/magento/module-bundle-import-export/Model/Export/ |
<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\BundleImportExport\Model\Export; use Magento\Catalog\Model\ResourceModel\Product\Collection; use Magento\CatalogImportExport\Model\Export\RowCustomizerInterface; use Magento\CatalogImportExport\Model\Import\Product as ImportProductModel; use Magento\Bundle\Model\ResourceModel\Selection\Collection as SelectionCollection; use Magento\ImportExport\Model\Import as ImportModel; use Magento\Catalog\Model\Product\Type\AbstractType; use Magento\Store\Model\StoreManagerInterface; /** * Class RowCustomizer */ class RowCustomizer implements RowCustomizerInterface { const BUNDLE_PRICE_TYPE_COL = 'bundle_price_type'; const BUNDLE_SKU_TYPE_COL = 'bundle_sku_type'; const BUNDLE_PRICE_VIEW_COL = 'bundle_price_view'; const BUNDLE_WEIGHT_TYPE_COL = 'bundle_weight_type'; const BUNDLE_VALUES_COL = 'bundle_values'; const VALUE_FIXED = 'fixed'; const VALUE_DYNAMIC = 'dynamic'; const VALUE_PERCENT = 'percent'; const VALUE_PRICE_RANGE = 'Price range'; const VALUE_AS_LOW_AS = 'As low as'; /** * Mapping for bundle types * * @var array */ protected $typeMapping = [ '0' => self::VALUE_DYNAMIC, '1' => self::VALUE_FIXED ]; /** * Mapping for price views * * @var array */ protected $priceViewMapping = [ '0' => self::VALUE_PRICE_RANGE, '1' => self::VALUE_AS_LOW_AS ]; /** * Mapping for price types * * @var array */ protected $priceTypeMapping = [ '0' => self::VALUE_FIXED, '1' => self::VALUE_PERCENT ]; /** * Bundle product columns * * @var array */ protected $bundleColumns = [ self::BUNDLE_PRICE_TYPE_COL, self::BUNDLE_SKU_TYPE_COL, self::BUNDLE_PRICE_VIEW_COL, self::BUNDLE_WEIGHT_TYPE_COL, self::BUNDLE_VALUES_COL ]; /** * Product's bundle data * * @var array */ protected $bundleData = []; /** * Column name for shipment_type attribute * * @var string */ private $shipmentTypeColumn = 'bundle_shipment_type'; /** * Mapping for shipment type * * @var array */ private $shipmentTypeMapping = [ AbstractType::SHIPMENT_TOGETHER => 'together', AbstractType::SHIPMENT_SEPARATELY => 'separately', ]; /** * @var \Magento\Bundle\Model\ResourceModel\Option\Collection[] */ private $optionCollections = []; /** * @var array */ private $storeIdToCode = []; /** * @var string */ private $optionCollectionCacheKey = '_cache_instance_options_collection'; /** * @var StoreManagerInterface */ private $storeManager; /** * @param StoreManagerInterface $storeManager */ public function __construct(StoreManagerInterface $storeManager) { $this->storeManager = $storeManager; } /** * Retrieve list of bundle specific columns * @return array */ private function getBundleColumns() { return array_merge($this->bundleColumns, [$this->shipmentTypeColumn]); } /** * Prepare data for export * * @param \Magento\Catalog\Model\ResourceModel\Product\Collection $collection * @param int[] $productIds * @return $this */ public function prepareData($collection, $productIds) { $productCollection = clone $collection; $productCollection->addAttributeToFilter( 'entity_id', ['in' => $productIds] )->addAttributeToFilter( 'type_id', ['eq' => \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE] ); return $this->populateBundleData($productCollection); } /** * Set headers columns * * @param array $columns * @return array */ public function addHeaderColumns($columns) { $columns = array_merge($columns, $this->getBundleColumns()); return $columns; } /** * Add data for export * * @param array $dataRow * @param int $productId * @return array */ public function addData($dataRow, $productId) { if (!empty($this->bundleData[$productId])) { $dataRow = array_merge($this->cleanNotBundleAdditionalAttributes($dataRow), $this->bundleData[$productId]); } return $dataRow; } /** * Calculate the largest links block * * @param array $additionalRowsCount * @param int $productId * @return array * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getAdditionalRowsCount($additionalRowsCount, $productId) { return $additionalRowsCount; } /** * Populate bundle product data * * @param \Magento\Catalog\Model\ResourceModel\Product\Collection $collection * @return $this */ protected function populateBundleData($collection) { foreach ($collection as $product) { $id = $product->getEntityId(); $this->bundleData[$id][self::BUNDLE_PRICE_TYPE_COL] = $this->getTypeValue($product->getPriceType()); $this->bundleData[$id][$this->shipmentTypeColumn] = $this->getShipmentTypeValue( $product->getShipmentType() ); $this->bundleData[$id][self::BUNDLE_SKU_TYPE_COL] = $this->getTypeValue($product->getSkuType()); $this->bundleData[$id][self::BUNDLE_PRICE_VIEW_COL] = $this->getPriceViewValue($product->getPriceView()); $this->bundleData[$id][self::BUNDLE_WEIGHT_TYPE_COL] = $this->getTypeValue($product->getWeightType()); $this->bundleData[$id][self::BUNDLE_VALUES_COL] = $this->getFormattedBundleOptionValues($product); } return $this; } /** * Retrieve formatted bundle options * * @param \Magento\Catalog\Model\Product $product * @return string */ protected function getFormattedBundleOptionValues(\Magento\Catalog\Model\Product $product): string { $optionCollections = $this->getProductOptionCollection($product); $bundleData = ''; $optionTitles = $this->getBundleOptionTitles($product); foreach ($optionCollections->getItems() as $option) { $optionValues = $this->getFormattedOptionValues($option, $optionTitles); $bundleData .= $this->getFormattedBundleSelections( $optionValues, $product->getTypeInstance() ->getSelectionsCollection([$option->getId()], $product) ->setOrder('position', Collection::SORT_ORDER_ASC) ); } return rtrim($bundleData, ImportProductModel::PSEUDO_MULTI_LINE_SEPARATOR); } /** * Retrieve formatted bundle selections * * @param string $optionValues * @param SelectionCollection $selections * @return string */ protected function getFormattedBundleSelections($optionValues, SelectionCollection $selections) { $bundleData = ''; $selections->addAttributeToSort('position'); foreach ($selections as $selection) { $selectionData = [ 'sku' => $selection->getSku(), 'price' => $selection->getSelectionPriceValue(), 'default' => $selection->getIsDefault(), 'default_qty' => $selection->getSelectionQty(), 'price_type' => $this->getPriceTypeValue($selection->getSelectionPriceType()), 'can_change_qty' => $selection->getSelectionCanChangeQty(), ]; $bundleData .= $optionValues . ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR . implode( ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, array_map( function ($value, $key) { return $key . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR . $value; }, $selectionData, array_keys($selectionData) ) ) . ImportProductModel::PSEUDO_MULTI_LINE_SEPARATOR; } return $bundleData; } /** * Retrieve option value of bundle product * * @param \Magento\Bundle\Model\Option $option * @param string[] $optionTitles * @return string */ protected function getFormattedOptionValues( \Magento\Bundle\Model\Option $option, array $optionTitles = [] ): string { $names = implode(ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, array_map( function ($title, $storeName) { return $storeName . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR . $title; }, $optionTitles[$option->getOptionId()], array_keys($optionTitles[$option->getOptionId()]) )); return $names . ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR . 'type' . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR . $option->getType() . ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR . 'required' . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR . $option->getRequired(); } /** * Retrieve bundle type value by code * * @param string $type * @return string */ protected function getTypeValue($type) { return $this->typeMapping[$type] ?? self::VALUE_DYNAMIC; } /** * Retrieve bundle price view value by code * * @param string $type * @return string */ protected function getPriceViewValue($type) { return $this->priceViewMapping[$type] ?? self::VALUE_PRICE_RANGE; } /** * Retrieve bundle price type value by code * * @param string $type * @return string */ protected function getPriceTypeValue($type) { return $this->priceTypeMapping[$type] ?? null; } /** * Retrieve bundle shipment type value by code * * @param string $type * @return string */ private function getShipmentTypeValue($type) { return $this->shipmentTypeMapping[$type] ?? null; } /** * Remove bundle specified additional attributes as now they are stored in specified columns * * @param array $dataRow * @return array */ protected function cleanNotBundleAdditionalAttributes($dataRow) { if (!empty($dataRow['additional_attributes'])) { $additionalAttributes = $this->parseAdditionalAttributes($dataRow['additional_attributes']); $dataRow['additional_attributes'] = $this->getNotBundleAttributes($additionalAttributes); } return $dataRow; } /** * Retrieve not bundle additional attributes * * @param array $additionalAttributes * @return string */ protected function getNotBundleAttributes($additionalAttributes) { $filteredAttributes = []; foreach ($additionalAttributes as $code => $value) { if (!in_array('bundle_' . $code, $this->getBundleColumns())) { $filteredAttributes[] = $code . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR . $value; } } return implode(ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $filteredAttributes); } /** * Retrieves additional attributes as array code=>value. * * @param string $additionalAttributes * @return array */ private function parseAdditionalAttributes($additionalAttributes) { $attributeNameValuePairs = explode(ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $additionalAttributes); $preparedAttributes = []; $code = ''; foreach ($attributeNameValuePairs as $attributeData) { //process case when attribute has ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR inside its value if (strpos($attributeData, ImportProductModel::PAIR_NAME_VALUE_SEPARATOR) === false) { if (!$code) { continue; } $preparedAttributes[$code] .= ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR . $attributeData; continue; } list($code, $value) = explode(ImportProductModel::PAIR_NAME_VALUE_SEPARATOR, $attributeData, 2); $preparedAttributes[$code] = $value; } return $preparedAttributes; } /** * Get product options titles. * * Values for all store views (default) should be specified with 'name' key. * If user want to specify value or change existing for non default store views it should be specified with * 'name_' prefix and needed store view suffix. * * For example: * - 'name=All store views name' for all store views * - 'name_specific_store=Specific store name' for store view with 'specific_store' store code * * @param \Magento\Catalog\Model\Product $product * @return array */ private function getBundleOptionTitles(\Magento\Catalog\Model\Product $product): array { $optionCollections = $this->getProductOptionCollection($product); $optionsTitles = []; /** @var \Magento\Bundle\Model\Option $option */ foreach ($optionCollections->getItems() as $option) { $optionsTitles[$option->getId()]['name'] = $option->getTitle(); } $storeIds = $product->getStoreIds(); if (count($storeIds) > 1) { foreach ($storeIds as $storeId) { $optionCollections = $this->getProductOptionCollection($product, (int)$storeId); /** @var \Magento\Bundle\Model\Option $option */ foreach ($optionCollections->getItems() as $option) { $optionTitle = $option->getTitle(); if ($optionsTitles[$option->getId()]['name'] != $optionTitle) { $optionsTitles[$option->getId()]['name_' . $this->getStoreCodeById((int)$storeId)] = $optionTitle; } } } } return $optionsTitles; } /** * Get product options collection by provided product model. * * Set given store id to the product if it was defined (default store id will be set if was not). * * @param \Magento\Catalog\Model\Product $product $product * @param int $storeId * @return \Magento\Bundle\Model\ResourceModel\Option\Collection */ private function getProductOptionCollection( \Magento\Catalog\Model\Product $product, int $storeId = \Magento\Store\Model\Store::DEFAULT_STORE_ID ): \Magento\Bundle\Model\ResourceModel\Option\Collection { $productSku = $product->getSku(); if (!isset($this->optionCollections[$productSku][$storeId])) { $product->unsetData($this->optionCollectionCacheKey); $product->setStoreId($storeId); $this->optionCollections[$productSku][$storeId] = $product->getTypeInstance() ->getOptionsCollection($product) ->setOrder('position', Collection::SORT_ORDER_ASC); } return $this->optionCollections[$productSku][$storeId]; } /** * Retrieve store code by it's ID. * * Collect store id in $storeIdToCode[] private variable if it was not initialized earlier. * * @param int $storeId * @return string */ private function getStoreCodeById(int $storeId): string { if (!isset($this->storeIdToCode[$storeId])) { $this->storeIdToCode[$storeId] = $this->storeManager->getStore($storeId)->getCode(); } return $this->storeIdToCode[$storeId]; } }