11 use \Magento\Store\Model\Store;
12 use \Magento\CatalogImportExport\Model\Import\Product as
ImportProduct;
25 class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity
140 private $userDefinedAttributes = [];
239 'image' =>
'base_image',
240 'image_label' =>
"base_image_label",
241 'thumbnail' =>
'thumbnail_image',
242 'thumbnail_label' =>
'thumbnail_image_label',
243 self::COL_MEDIA_IMAGE =>
'additional_images',
244 '_media_image_label' =>
'additional_image_labels',
245 self::COL_STORE =>
'store_view_code',
246 self::COL_ATTR_SET =>
'attribute_set_code',
247 self::COL_TYPE =>
'product_type',
248 self::COL_CATEGORY =>
'categories',
249 self::COL_PRODUCT_WEBSITES =>
'product_websites',
250 'status' =>
'product_online',
251 'news_from_date' =>
'new_from_date',
252 'news_to_date' =>
'new_to_date',
253 'options_container' =>
'display_product_options_in',
254 'minimal_price' =>
'map_price',
255 'msrp' =>
'msrp_price',
256 'msrp_enabled' =>
'map_enabled',
257 'special_from_date' =>
'special_price_from_date',
258 'special_to_date' =>
'special_price_to_date',
259 'min_qty' =>
'out_of_stock_qty',
260 'backorders' =>
'allow_backorders',
261 'min_sale_qty' =>
'min_cart_qty',
262 'max_sale_qty' =>
'max_cart_qty',
263 'notify_stock_qty' =>
'notify_on_stock_below',
264 'meta_keyword' =>
'meta_keywords',
265 'tax_class_id' =>
'tax_class_name',
279 'custom_design_from',
299 'special_price_from_date',
300 'special_price_to_date',
310 'thumbnail_image_label',
312 'swatch_image_label',
317 'display_product_options_in',
321 'special_price_from_date',
322 'special_price_to_date',
323 'gift_message_available',
325 'custom_design_from',
327 'custom_layout_update',
329 'product_options_container',
331 'msrp_display_actual_price_type',
333 'country_of_manufacture',
335 'display_product_options_in',
349 private $productEntityLinkField;
372 \
Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate,
386 \
Magento\Catalog\Model\
Product\LinkTypeProvider $linkTypeProvider,
390 $this->_entityCollectionFactory = $collectionFactory;
391 $this->_exportConfig = $exportConfig;
394 $this->_attrSetColFactory = $attrSetColFactory;
395 $this->_categoryColFactory = $categoryColFactory;
398 $this->_optionColFactory = $optionColFactory;
399 $this->_attributeColFactory = $attributeColFactory;
401 $this->_linkTypeProvider = $linkTypeProvider;
403 $this->dateAttrCodes = array_merge($this->dateAttrCodes,
$dateAttrCodes);
410 ->initAttributeSets()
422 $productTypeId = $this->_productFactory->create()->getTypeId();
423 foreach ($this->_attrSetColFactory->create()->setEntityTypeFilter($productTypeId) as
$attributeSet) {
436 $collection = $this->_categoryColFactory->create()->addNameToResult();
439 $structure = preg_split(
'#/+#',
$category->getPath());
440 $pathSize = count($structure);
443 for (
$i = 1;
$i < $pathSize;
$i++) {
445 $path[] = $this->quoteCategoryDelimiter(
$name);
465 foreach (
$productTypes as $productTypeName => $productTypeConfig) {
466 if (!(
$model = $this->_typeFactory->create($productTypeConfig[
'model']))) {
467 throw new \Magento\Framework\Exception\LocalizedException(
468 __(
'Entity type model \'%1\' is not found', $productTypeConfig[
'model'])
472 throw new \Magento\Framework\Exception\LocalizedException(
474 'Entity type model must be an instance of' 475 .
' \Magento\CatalogImportExport\Model\Export\Product\Type\AbstractType' 479 if (
$model->isSuitable()) {
480 $this->_productTypeModels[$productTypeName] =
$model;
481 $this->_disabledAttrs = array_merge($this->_disabledAttrs,
$model->getDisabledAttrs());
482 $this->_indexValueAttributes = array_merge(
483 $this->_indexValueAttributes,
484 $model->getIndexValueAttributes()
488 if (!$this->_productTypeModels) {
489 throw new \Magento\Framework\Exception\LocalizedException(
490 __(
'There are no product types available for export.')
493 $this->_disabledAttrs = array_unique($this->_disabledAttrs);
503 protected function initWebsites()
506 foreach ($this->_storeManager->getWebsites() as
$website) {
523 $select = $this->_connection->select()->from(
524 [
'mgvte' => $this->_resourceModel->getTableName(
'catalog_product_entity_media_gallery_value_to_entity')],
526 "mgvte.{$this->getProductEntityLinkField()}",
530 [
'mg' => $this->_resourceModel->getTableName(
'catalog_product_entity_media_gallery')],
531 '(mg.value_id = mgvte.value_id)',
534 'filename' =>
'mg.value',
537 [
'mgv' => $this->_resourceModel->getTableName(
'catalog_product_entity_media_gallery_value')],
538 '(mg.value_id = mgv.value_id)',
546 "mgvte.{$this->getProductEntityLinkField()} IN (?)",
550 $rowMediaGallery = [];
551 $stmt = $this->_connection->query(
$select);
552 while ($mediaRow = $stmt->fetch()) {
554 '_media_attribute_id' => $mediaRow[
'attribute_id'],
555 '_media_image' => $mediaRow[
'filename'],
556 '_media_label' => $mediaRow[
'label'],
557 '_media_position' => $mediaRow[
'position'],
558 '_media_is_disabled' => $mediaRow[
'disabled'],
559 '_media_store_id' => $mediaRow[
'store_id'],
563 return $rowMediaGallery;
577 $select = $this->_connection->select()->from(
578 $this->_itemFactory->create()->getMainTable()
584 $stmt = $this->_connection->query(
$select);
586 while ($stockItemRow = $stmt->fetch()) {
589 $stockItemRow[
'item_id'],
590 $stockItemRow[
'product_id'],
591 $stockItemRow[
'low_stock_date'],
592 $stockItemRow[
'stock_id'],
593 $stockItemRow[
'stock_status_changed_auto']
597 return $stockItemRows;
611 $select = $this->_connection->select()->from(
612 [
'cpl' => $this->_resourceModel->getTableName(
'catalog_product_link')],
617 'position' =>
'cplai.value',
618 'default_qty' =>
'cplad.value' 621 [
'cpe' => $this->_resourceModel->getTableName(
'catalog_product_entity')],
622 '(cpe.entity_id = cpl.linked_product_id)',
625 [
'cpla' => $this->_resourceModel->getTableName(
'catalog_product_link_attribute')],
626 $this->_connection->quoteInto(
627 '(cpla.link_type_id = cpl.link_type_id AND cpla.product_link_attribute_code = ?)',
632 [
'cplaq' => $this->_resourceModel->getTableName(
'catalog_product_link_attribute')],
633 $this->_connection->quoteInto(
634 '(cplaq.link_type_id = cpl.link_type_id AND cplaq.product_link_attribute_code = ?)',
639 [
'cplai' => $this->_resourceModel->getTableName(
'catalog_product_link_attribute_int')],
640 '(cplai.link_id = cpl.link_id AND cplai.product_link_attribute_id = cpla.product_link_attribute_id)',
643 [
'cplad' => $this->_resourceModel->getTableName(
'catalog_product_link_attribute_decimal')],
644 '(cplad.link_id = cpl.link_id AND cplad.product_link_attribute_id = cplaq.product_link_attribute_id)',
647 'cpl.link_type_id IN (?)',
648 array_values($this->_linkTypeProvider->getLinkTypes())
650 'cpl.product_id IN (?)',
654 $stmt = $this->_connection->query(
$select);
656 while ($linksRow = $stmt->fetch()) {
657 $linksRows[$linksRow[
'product_id']][$linksRow[
'link_type_id']][] = [
658 'sku' => $linksRow[
'sku'],
659 'position' => $linksRow[
'position'],
660 'default_qty' => $linksRow[
'default_qty'],
681 foreach ($rowCategories[
$productId] as $categoryId) {
682 $categoryPath = $this->_rootCategories[$categoryId];
683 if (isset($this->_categories[$categoryId])) {
684 $categoryPath .=
'/' . $this->_categories[$categoryId];
714 if (!$this->_headerColumns) {
715 $this->_headerColumns = array_merge(
722 self::COL_PRODUCT_WEBSITES,
725 [self::COL_ADDITIONAL_ATTRIBUTES],
726 reset($stockItemRows) ? array_keys(end($stockItemRows)) : [],
731 'crosssell_position',
735 'additional_image_labels',
736 'hide_from_product_page',
758 if ($resetCollection || empty($this->_entityCollection)) {
759 $this->_entityCollection = $this->_entityCollectionFactory->create();
771 if ($this->_itemsPerPage ===
null) {
772 $memoryLimitConfigValue = trim(
ini_get(
'memory_limit'));
773 $lastMemoryLimitLetter = strtolower($memoryLimitConfigValue[strlen($memoryLimitConfigValue) - 1]);
774 $memoryLimit = (int) $memoryLimitConfigValue;
775 switch ($lastMemoryLimitLetter) {
777 $memoryLimit *= 1024;
780 $memoryLimit *= 1024;
783 $memoryLimit *= 1024;
787 $memoryLimit = 250000000;
791 $memoryPerProduct = 500000;
793 $memoryUsagePercent = 0.8;
795 $minProductsLimit = 500;
797 $maxProductsLimit = 5000;
799 $this->_itemsPerPage = intval(
800 ($memoryLimit * $memoryUsagePercent -
memory_get_usage(
true)) / $memoryPerProduct
802 if ($this->_itemsPerPage < $minProductsLimit) {
803 $this->_itemsPerPage = $minProductsLimit;
805 if ($this->_itemsPerPage > $maxProductsLimit) {
806 $this->_itemsPerPage = $maxProductsLimit;
839 $entityCollection->setOrder(
'entity_id',
'asc');
843 if ($entityCollection->count() == 0) {
850 foreach ($exportData as $dataRow) {
853 if ($entityCollection->getCurPage() >= $entityCollection->getLastPageNumber()) {
857 return $writer->getContents();
866 $exportFilter = !empty($this->_parameters[\
Magento\
ImportExport\Model\Export::FILTER_ELEMENT_GROUP]) ?
869 if (isset($exportFilter[
'category_ids'])
870 && trim($exportFilter[
'category_ids'])
873 $collection->addCategoriesFilter([
'in' => explode(
',', $exportFilter[
'category_ids'])]);
876 return parent::_prepareEntityCollection(
$collection);
892 $rawData = $this->collectRawData();
893 $multirawData = $this->collectMultirawData();
898 $this->rowCustomizer->prepareData(
903 $this->
setHeaderColumns($multirawData[
'customOptionsData'], $stockItemRows);
908 $dataRow = array_merge($dataRow, $stockItemRows[
$productId]);
910 $this->appendMultirowData($dataRow, $multirawData);
912 $exportData[] = $dataRow;
916 }
catch (\Exception $e) {
917 $this->_logger->critical($e);
935 foreach (array_keys($this->_storeIdToCode) as
$storeId) {
953 protected function collectRawData()
962 foreach (
$items as $itemId => $itemByStore) {
965 $additionalAttributes = [];
973 if (isset($this->_attributeValues[
$code][$attrValue]) && !empty($this->_attributeValues[
$code])) {
974 $attrValue = $this->_attributeValues[
$code][$attrValue];
976 $fieldName = isset($this->_fieldsMap[
$code]) ? $this->_fieldsMap[
$code] :
$code;
978 if ($this->_attributeTypes[
$code] ==
'datetime') {
979 if (in_array(
$code, $this->dateAttrCodes)
980 || in_array(
$code, $this->userDefinedAttributes)
982 $attrValue = $this->_localeDate->formatDateTime(
983 new \DateTime($attrValue),
984 \IntlDateFormatter::SHORT,
985 \IntlDateFormatter::NONE,
987 date_default_timezone_get()
990 $attrValue = $this->_localeDate->formatDateTime(
991 new \DateTime($attrValue),
992 \IntlDateFormatter::SHORT,
993 \IntlDateFormatter::SHORT
1005 if ($this->_attributeTypes[
$code] !==
'multiselect') {
1006 if (is_scalar($attrValue)) {
1008 $additionalAttributes[$fieldName] = $fieldName .
1009 ImportProduct::PAIR_NAME_VALUE_SEPARATOR . $this->wrapValue($attrValue);
1011 $data[$itemId][
$storeId][$fieldName] = htmlspecialchars_decode($attrValue);
1015 if (!empty($this->collectedMultiselectsData[
$storeId][$productLinkId][
$code])) {
1016 $additionalAttributes[
$code] = $fieldName .
1017 ImportProduct::PAIR_NAME_VALUE_SEPARATOR . implode(
1018 ImportProduct::PSEUDO_MULTI_LINE_SEPARATOR,
1019 $this->wrapValue($this->collectedMultiselectsData[
$storeId][$productLinkId][
$code])
1025 if (!empty($additionalAttributes)) {
1026 $additionalAttributes = array_map(
'htmlspecialchars_decode', $additionalAttributes);
1030 unset(
$data[$itemId][
$storeId][self::COL_ADDITIONAL_ATTRIBUTES]);
1033 $attrSetId =
$item->getAttributeSetId();
1040 $data[$itemId][
$storeId][
'product_link_id'] = $productLinkId;
1053 private function wrapValue(
$value)
1056 $wrap =
function (
$value) {
1057 return sprintf(
'"%s"', str_replace(
'"',
'""',
$value));
1069 protected function collectMultirawData()
1074 $rowCategories = [];
1078 $collection->addCategoryIds()->addWebsiteNamesToResult();
1083 $rowWebsites[
$item->getId()] = array_intersect(
1084 array_keys($this->_websiteIdToCode),
1085 $item->getWebsites()
1087 $rowCategories[
$item->getId()] = array_combine(
$item->getCategoryIds(),
$item->getCategoryIds());
1091 $allCategoriesIds = array_merge(array_keys($this->_categories), array_keys($this->_rootCategories));
1092 $allCategoriesIds = array_combine($allCategoriesIds, $allCategoriesIds);
1097 $data[
'rowWebsites'] = $rowWebsites;
1098 $data[
'rowCategories'] = $rowCategories;
1116 return !empty($this->collectedMultiselectsData[
$storeId][$linkId]);
1127 $attrValue =
$item->getData($attrCode);
1130 $this->_attributeValues[$attrCode],
1155 if (!isset($this->_attributeValues[
$code])) {
1170 private function appendMultirowData(&$dataRow, $multiRawData)
1173 $productLinkId = $dataRow[
'product_link_id'];
1179 unset($dataRow[
'product_id']);
1180 unset($dataRow[
'product_link_id']);
1181 unset($dataRow[
'store_id']);
1182 unset($dataRow[self::COL_SKU]);
1183 unset($dataRow[self::COL_STORE]);
1184 unset($dataRow[self::COL_ATTR_SET]);
1185 unset($dataRow[self::COL_TYPE]);
1189 if (!empty($multiRawData[
'rowWebsites'][
$productId])) {
1191 foreach ($multiRawData[
'rowWebsites'][
$productId] as $productWebsite) {
1196 $multiRawData[
'rowWebsites'][
$productId] = [];
1198 if (!empty($multiRawData[
'mediaGalery'][$productLinkId])) {
1199 $additionalImages = [];
1200 $additionalImageLabels = [];
1201 $additionalImageIsDisabled = [];
1202 foreach ($multiRawData[
'mediaGalery'][$productLinkId] as $mediaItem) {
1204 $additionalImages[] = $mediaItem[
'_media_image'];
1205 $additionalImageLabels[] = $mediaItem[
'_media_label'];
1207 if ($mediaItem[
'_media_is_disabled'] ==
true) {
1208 $additionalImageIsDisabled[] = $mediaItem[
'_media_image'];
1212 $dataRow[
'additional_images'] =
1214 $dataRow[
'additional_image_labels'] =
1216 $dataRow[
'hide_from_product_page'] =
1218 $multiRawData[
'mediaGalery'][$productLinkId] = [];
1220 foreach ($this->_linkTypeProvider->getLinkTypes() as $linkTypeName => $linkId) {
1221 if (!empty($multiRawData[
'linksRows'][$productLinkId][$linkId])) {
1222 $colPrefix = $linkTypeName .
'_';
1225 foreach ($multiRawData[
'linksRows'][$productLinkId][$linkId] as
$linkData) {
1226 if (
$linkData[
'default_qty'] !==
null) {
1227 $skuItem =
$linkData[
'sku'] . ImportProduct::PAIR_NAME_VALUE_SEPARATOR .
1232 $associations[$skuItem] =
$linkData[
'position'];
1234 $multiRawData[
'linksRows'][$productLinkId][$linkId] = [];
1235 asort($associations);
1236 $dataRow[$colPrefix .
'skus'] =
1238 $dataRow[$colPrefix .
'position'] =
1242 $dataRow = $this->rowCustomizer->addData($dataRow,
$productId);
1244 $additionalImageIsDisabled = [];
1245 if (!empty($multiRawData[
'mediaGalery'][$productLinkId])) {
1246 foreach ($multiRawData[
'mediaGalery'][$productLinkId] as $mediaItem) {
1247 if ((
int)$mediaItem[
'_media_store_id'] ===
$storeId) {
1248 if ($mediaItem[
'_media_is_disabled'] ==
true) {
1249 $additionalImageIsDisabled[] = $mediaItem[
'_media_image'];
1254 if ($additionalImageIsDisabled) {
1255 $dataRow[
'hide_from_product_page'] =
1261 foreach (array_keys($this->collectedMultiselectsData[
$storeId][
$productId]) as $attrKey) {
1263 $dataRow[$attrKey] = implode(
1271 if (!empty($multiRawData[
'customOptionsData'][$productLinkId][
$storeId])) {
1272 $customOptionsRows = $multiRawData[
'customOptionsData'][$productLinkId][
$storeId];
1273 $multiRawData[
'customOptionsData'][$productLinkId][
$storeId] = [];
1274 $customOptions = implode(ImportProduct::PSEUDO_MULTI_LINE_SEPARATOR, $customOptionsRows);
1276 $dataRow = array_merge($dataRow, [
'custom_options' =>
$customOptions]);
1279 if (empty($dataRow)) {
1299 $data = $this->appendMultirowData($dataRow, $multiRawData);
1312 foreach ($this->_fieldsMap as $systemFieldName => $fileFieldName) {
1313 if (isset($rowData[$systemFieldName])) {
1314 $rowData[$fileFieldName] = $rowData[$systemFieldName];
1315 unset($rowData[$systemFieldName]);
1330 foreach ($rowData as $key => $fieldName) {
1331 if (isset($this->_fieldsMap[$fieldName])) {
1332 $rowData[$key] = $this->_fieldsMap[$fieldName];
1347 $result[] = $key . ImportProduct::PAIR_NAME_VALUE_SEPARATOR .
$value;
1367 $customOptionsData = [];
1369 foreach (array_keys($this->_storeIdToCode) as
$storeId) {
1370 $options = $this->_optionColFactory->create();
1387 $row[
'price_type'] = (
$option[
'price_type'] ===
'percent') ?
'percent' :
'fixed';
1389 if (
$option[
'max_characters']) {
1390 $row[
'max_characters'] =
$option[
'max_characters'];
1393 foreach ([
'file_extension',
'image_size_x',
'image_size_y'] as $fileOptionKey) {
1394 if (!isset(
$option[$fileOptionKey])) {
1409 $row[
'price_type'] = (
$value[
'price_type'] ===
'percent') ?
'percent' :
'fixed';
1422 return $customOptionsData;
1433 $validTypes = array_keys($this->_productTypeModels);
1434 $validTypes = array_combine($validTypes, $validTypes);
1442 $attrApplyTo = array_combine($attrApplyTo, $attrApplyTo);
1443 $attrApplyTo = $attrApplyTo ? array_intersect_key($attrApplyTo, $validTypes) : $validTypes;
1446 foreach ($attrApplyTo as $productType) {
1448 if ($this->_productTypeModels[$productType]->overrideAttribute(
$attribute)) {
1467 return $this->_attributeColFactory->create();
1477 return 'catalog_product';
1489 $this->_attributeTypes[
$attribute->getAttributeCode()] =
1492 $this->userDefinedAttributes[] =
$attribute->getAttributeCode();
1503 private function getMetadataPool()
1505 if (!$this->metadataPool) {
1507 ->get(\
Magento\Framework\EntityManager\MetadataPool::class);
1520 if (!$this->productEntityLinkField) {
1521 $this->productEntityLinkField = $this->getMetadataPool()
1522 ->getMetadata(\
Magento\Catalog\Api\Data\ProductInterface::class)
1525 return $this->productEntityLinkField;
1534 private function quoteCategoryDelimiter($string)
updateDataWithCategoryColumns(&$dataRow, &$rowCategories, $productId)
optionRowToCellString($option)
prepareCatalogInventory(array $productIds)
_customFieldsMapping($rowData)
static getAttributeType(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute)
_prepareEntityCollection(\Magento\Eav\Model\Entity\Collection\AbstractCollection $collection)
elseif(isset( $params[ 'redirect_parent']))
_getExportMainAttrCodes()
getAttributeOptions(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute)
addMultirowData($dataRow, $multiRawData)
filterAttributeCollection(\Magento\Eav\Model\ResourceModel\Entity\Attribute\Collection $collection)
const COL_ADDITIONAL_ATTRIBUTES
paginateCollection($page, $pageSize)
getProductEntityLinkField()
const DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR
$_entityCollectionFactory
_getEntityCollection($resetCollection=false)
_customHeadersMapping($rowData)
setHeaderColumns($customOptionsData, $stockItemRows)
prepareLinks(array $productIds)
collectMultiselectValues($item, $attrCode, $storeId)
isValidAttributeValue($code, $value)
const FILTER_ELEMENT_GROUP
__construct(\Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, \Magento\Eav\Model\Config $config, \Magento\Framework\App\ResourceConnection $resource, \Magento\Store\Model\StoreManagerInterface $storeManager, \Psr\Log\LoggerInterface $logger, \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $collectionFactory, \Magento\ImportExport\Model\Export\ConfigInterface $exportConfig, \Magento\Catalog\Model\ResourceModel\ProductFactory $productFactory, \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory $attrSetColFactory, \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory $categoryColFactory, \Magento\CatalogInventory\Model\ResourceModel\Stock\ItemFactory $itemFactory, \Magento\Catalog\Model\ResourceModel\Product\Option\CollectionFactory $optionColFactory, \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory $attributeColFactory, \Magento\CatalogImportExport\Model\Export\Product\Type\Factory $_typeFactory, \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider, \Magento\CatalogImportExport\Model\Export\RowCustomizerInterface $rowCustomizer, array $dateAttrCodes=[])
getMediaGallery(array $productIds)
const COL_PRODUCT_WEBSITES
hasMultiselectData($item, $storeId)
getCustomOptionsData($productIds)
$collectedMultiselectsData
if(!isset($_GET['name'])) $name