39 class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
223 '_related_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_RELATED,
224 '_crosssell_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_CROSSSELL,
225 '_upsell_' => \Magento\Catalog\Model\Product\Link::LINK_TYPE_UPSELL,
239 'custom_design_from',
264 ValidatorInterface::ERROR_INVALID_SCOPE =>
'Invalid value in Scope column',
265 ValidatorInterface::ERROR_INVALID_WEBSITE =>
'Invalid value in Website column (website does not exist?)',
266 ValidatorInterface::ERROR_INVALID_STORE =>
'Invalid value in Store column (store doesn\'t exist?)',
267 ValidatorInterface::ERROR_INVALID_ATTR_SET =>
'Invalid value for Attribute Set column (set doesn\'t exist?)',
268 ValidatorInterface::ERROR_INVALID_TYPE =>
'Product Type is invalid or not supported',
269 ValidatorInterface::ERROR_INVALID_CATEGORY =>
'Category does not exist',
270 ValidatorInterface::ERROR_VALUE_IS_REQUIRED =>
'Please make sure attribute "%s" is not empty.',
271 ValidatorInterface::ERROR_TYPE_CHANGED =>
'Trying to change type of existing products',
272 ValidatorInterface::ERROR_SKU_IS_EMPTY =>
'SKU is empty',
273 ValidatorInterface::ERROR_NO_DEFAULT_ROW =>
'Default values row does not exist',
274 ValidatorInterface::ERROR_CHANGE_TYPE =>
'Product type change is not allowed',
275 ValidatorInterface::ERROR_DUPLICATE_SCOPE =>
'Duplicate scope',
276 ValidatorInterface::ERROR_DUPLICATE_SKU =>
'Duplicate SKU',
277 ValidatorInterface::ERROR_CHANGE_ATTR_SET =>
'Attribute set change is not allowed',
278 ValidatorInterface::ERROR_TYPE_UNSUPPORTED =>
'Product type is not supported',
279 ValidatorInterface::ERROR_ROW_IS_ORPHAN =>
'Orphan rows that will be skipped due default row errors',
280 ValidatorInterface::ERROR_INVALID_TIER_PRICE_QTY =>
'Tier Price data price or quantity value is invalid',
281 ValidatorInterface::ERROR_INVALID_TIER_PRICE_SITE =>
'Tier Price data website is invalid',
282 ValidatorInterface::ERROR_INVALID_TIER_PRICE_GROUP =>
'Tier Price customer group ID is invalid',
283 ValidatorInterface::ERROR_TIER_DATA_INCOMPLETE =>
'Tier Price data is incomplete',
284 ValidatorInterface::ERROR_SKU_NOT_FOUND_FOR_DELETE =>
'Product with specified SKU not found',
285 ValidatorInterface::ERROR_SUPER_PRODUCTS_SKU_NOT_FOUND =>
'Product with specified super products SKU not found',
286 ValidatorInterface::ERROR_MEDIA_DATA_INCOMPLETE =>
'Media data is incomplete',
287 ValidatorInterface::ERROR_EXCEEDED_MAX_LENGTH =>
'Attribute %s exceeded max length',
288 ValidatorInterface::ERROR_INVALID_ATTRIBUTE_TYPE =>
'Value for \'%s\' attribute contains incorrect value',
289 ValidatorInterface::ERROR_ABSENT_REQUIRED_ATTRIBUTE =>
'Attribute %s is required',
290 ValidatorInterface::ERROR_INVALID_ATTRIBUTE_OPTION =>
'Value for \'%s\' attribute contains incorrect value, see acceptable values on settings specified for Admin',
291 ValidatorInterface::ERROR_DUPLICATE_UNIQUE_ATTRIBUTE =>
'Duplicated unique attribute',
292 ValidatorInterface::ERROR_INVALID_VARIATIONS_CUSTOM_OPTIONS =>
'Value for \'%s\' sub attribute in \'%s\' attribute contains incorrect value, acceptable values are: \'dropdown\', \'checkbox\', \'radio\', \'text\'',
293 ValidatorInterface::ERROR_INVALID_MEDIA_URL_OR_PATH =>
'Wrong URL/path used for attribute %s',
294 ValidatorInterface::ERROR_MEDIA_PATH_NOT_ACCESSIBLE =>
'Imported resource (image) does not exist in the local media storage',
295 ValidatorInterface::ERROR_MEDIA_URL_NOT_ACCESSIBLE =>
'Imported resource (image) could not be downloaded from external resource due to timeout or access permissions',
296 ValidatorInterface::ERROR_INVALID_WEIGHT =>
'Product weight is invalid',
297 ValidatorInterface::ERROR_DUPLICATE_URL_KEY =>
'Url key: \'%s\' was already generated for an item with the SKU: \'%s\'. You need to specify the unique URL key manually',
298 ValidatorInterface::ERROR_DUPLICATE_MULTISELECT_VALUES =>
"Value for multiselect attribute %s contains duplicated values",
308 'image' =>
'base_image',
309 'image_label' =>
"base_image_label",
310 'thumbnail' =>
'thumbnail_image',
311 'thumbnail_label' =>
'thumbnail_image_label',
312 self::COL_MEDIA_IMAGE =>
'additional_images',
313 '_media_image_label' =>
'additional_image_labels',
314 '_media_is_disabled' =>
'hide_from_product_page',
319 'status' =>
'product_online',
320 'news_from_date' =>
'new_from_date',
321 'news_to_date' =>
'new_to_date',
322 'options_container' =>
'display_product_options_in',
323 'minimal_price' =>
'map_price',
324 'msrp' =>
'msrp_price',
325 'msrp_enabled' =>
'map_enabled',
326 'special_from_date' =>
'special_price_from_date',
327 'special_to_date' =>
'special_price_to_date',
328 'min_qty' =>
'out_of_stock_qty',
329 'backorders' =>
'allow_backorders',
330 'min_sale_qty' =>
'min_cart_qty',
331 'max_sale_qty' =>
'max_cart_qty',
332 'notify_stock_qty' =>
'notify_on_stock_below',
333 '_related_sku' =>
'related_skus',
334 '_related_position' =>
'related_position',
335 '_crosssell_sku' =>
'crosssell_skus',
336 '_crosssell_position' =>
'crosssell_position',
337 '_upsell_sku' =>
'upsell_skus',
338 '_upsell_position' =>
'upsell_position',
339 'meta_keyword' =>
'meta_keywords',
368 '_tier_price_website',
369 '_tier_price_customer_group',
375 '_crosssell_position',
378 '_custom_option_store',
379 '_custom_option_type',
380 '_custom_option_title',
381 '_custom_option_is_required',
382 '_custom_option_price',
383 '_custom_option_sku',
384 '_custom_option_max_characters',
385 '_custom_option_sort_order',
386 '_custom_option_file_extension',
387 '_custom_option_image_size_x',
388 '_custom_option_image_size_y',
389 '_custom_option_row_title',
390 '_custom_option_row_price',
391 '_custom_option_row_sku',
392 '_custom_option_row_sort',
393 '_media_attribute_id',
397 '_media_is_disabled',
405 'use_config_manage_stock' => 1,
408 'use_config_min_qty' => 1,
410 'use_config_min_sale_qty' => 1,
411 'max_sale_qty' => 10000,
412 'use_config_max_sale_qty' => 1,
413 'is_qty_decimal' => 0,
415 'use_config_backorders' => 1,
416 'notify_stock_qty' => 1,
417 'use_config_notify_stock_qty' => 1,
418 'enable_qty_increments' => 0,
419 'use_config_enable_qty_inc' => 1,
420 'qty_increments' => 0,
421 'use_config_qty_increments' => 1,
423 'low_stock_date' =>
null,
424 'stock_status_changed_auto' => 0,
425 'is_decimal_divided' => 0,
681 private $productEntityLinkField;
688 private $productEntityIdentifierField;
695 private $multiLineSeparatorForRegexp;
709 private $catalogConfig;
716 private $stockItemImporter;
721 private $imageTypeProcessor;
728 private $mediaProcessor;
733 private $dateTimeFactory;
790 \
Magento\Framework\Event\ManagerInterface $eventManager,
794 \
Magento\Catalog\Helper\Data $catalogData,
801 \
Magento\CatalogImportExport\Model\
Import\Proxy\ProductFactory $proxyProdFactory,
802 \
Magento\CatalogImportExport\Model\
Import\UploaderFactory $uploaderFactory,
826 $this->_eventManager = $eventManager;
830 $this->_catalogData = $catalogData;
831 $this->_importConfig = $importConfig;
832 $this->_resourceFactory = $resourceFactory;
833 $this->_setColFactory = $setColFactory;
834 $this->_productTypeFactory = $productTypeFactory;
836 $this->_proxyProdFactory = $proxyProdFactory;
837 $this->_uploaderFactory = $uploaderFactory;
840 $this->_stockResItemFac = $stockResItemFac;
841 $this->_localeDate = $localeDate;
854 $this->dateAttrCodes = array_merge($this->dateAttrCodes,
$dateAttrCodes);
859 ->get(StockItemImporterInterface::class);
870 $this->_optionEntity =
$data[
'option_entity'] ??
871 $optionFactory->create([
'data' => [
'product_entity' => $this]]);
875 ->initImagesArrayKeys();
876 $this->validator->init($this);
891 if (!$this->validator->isAttributeValid($attrCode, $attrParams, $rowData)) {
892 foreach ($this->validator->getMessages() as
$message) {
941 public function getMediaGalleryAttributeId()
943 if (!$this->_mediaGalleryAttributeId) {
945 $resource = $this->_resourceFactory->create();
946 $this->_mediaGalleryAttributeId =
$resource->getAttribute(self::MEDIA_GALLERY_ATTRIBUTE_CODE)->getId();
959 if (isset($this->_productTypeModels[
$name])) {
960 return $this->_productTypeModels[
$name];
973 parent::setParameters(
$params);
1003 $productEntityTable = $this->_resourceFactory->create()->getEntityTable();
1005 while ($bunch = $this->_dataSourceModel->getNextBunch()) {
1008 foreach ($bunch as $rowNum => $rowData) {
1010 $idsToDelete[] = $this->getExistingSku($rowData[self::COL_SKU])[
'entity_id'];
1014 $this->countItemsDeleted += count($idsToDelete);
1015 $this->transactionManager->start($this->_connection);
1017 $this->objectRelationProcessor->delete(
1018 $this->transactionManager,
1020 $productEntityTable,
1021 $this->_connection->quoteInto(
'entity_id IN (?)', $idsToDelete),
1022 [
'entity_id' => $idsToDelete]
1024 $this->_eventManager->dispatch(
1025 'catalog_product_import_bunch_delete_commit_before',
1029 'ids_to_delete' => $idsToDelete,
1032 $this->transactionManager->commit();
1033 }
catch (\Exception $e) {
1034 $this->transactionManager->rollBack();
1037 $this->_eventManager->dispatch(
1038 'catalog_product_import_bunch_delete_after',
1039 [
'adapter' => $this,
'bunch' => $bunch]
1055 $this->_validatedRows =
null;
1059 $this->_replaceFlag =
true;
1064 $this->_eventManager->dispatch(
'catalog_product_import_finish_before', [
'adapter' => $this]);
1076 $this->_oldSku = $this->skuProcessor->reloadOldSkus()->getOldSkus();
1077 $this->_validatedRows =
null;
1095 foreach ($this->_productTypeModels as $productTypeModel) {
1096 $productTypeModel->saveData();
1100 if ($this->_replaceFlag) {
1115 foreach ($this->_setColFactory->create()->setEntityTypeFilter($this->_entityTypeId) as
$attributeSet) {
1129 $this->skuProcessor->setTypeModels($this->_productTypeModels);
1130 $this->_oldSku = $this->skuProcessor->reloadOldSkus()->getOldSkus();
1139 private function initImagesArrayKeys()
1141 $this->_imagesArrayKeys = $this->imageTypeProcessor->getImageTypes();
1154 foreach (
$productTypes as $productTypeName => $productTypeConfig) {
1155 $params = [$this, $productTypeName];
1156 if (!(
$model = $this->_productTypeFactory->create($productTypeConfig[
'model'], [
'params' =>
$params]))
1159 __(
'Entity type model \'%1\' is not found', $productTypeConfig[
'model'])
1165 'Entity type model must be an instance of ' 1170 if (
$model->isSuitable()) {
1171 $this->_productTypeModels[$productTypeName] =
$model;
1173 $this->_fieldsMap = array_merge($this->_fieldsMap,
$model->getCustomFieldsMapping());
1174 $this->_specialAttributes = array_merge($this->_specialAttributes,
$model->getParticularAttributes());
1178 $this->_specialAttributes = array_unique($this->_specialAttributes);
1188 foreach ($this->_messageTemplates as $errorCode =>
$template) {
1204 $rowData = $this->_customFieldsMapping($rowData);
1206 $rowData = parent::_prepareRowForDb($rowData);
1208 static $lastSku =
null;
1216 if ($this->isSkuExist($lastSku)) {
1217 $newSku = $this->skuProcessor->getNewSku($lastSku);
1237 $resource = $this->_linkFactory->create();
1239 $positionAttrId = [];
1240 $nextLinkId = $this->_resourceHelper->getNextAutoincrement($mainTable);
1243 foreach ($this->_linkNameToId as $linkName => $linkId) {
1244 $select = $this->_connection->select()->from(
1245 $resource->getTable(
'catalog_product_link_attribute'),
1246 [
'id' =>
'product_link_attribute_id']
1248 'link_type_id = :link_id AND product_link_attribute_code = :position' 1250 $bind = [
':link_id' => $linkId,
':position' =>
'position'];
1251 $positionAttrId[$linkId] = $this->_connection->fetchOne(
$select, $bind);
1253 while ($bunch = $this->_dataSourceModel->getNextBunch()) {
1258 foreach ($bunch as $rowNum => $rowData) {
1265 $productId = $this->skuProcessor->getNewSku($sku)[$this->getProductEntityLinkField()];
1266 $productLinkKeys = [];
1267 $select = $this->_connection->select()->from(
1268 $resource->getTable(
'catalog_product_link'),
1269 [
'id' =>
'link_id',
'linked_id' =>
'linked_product_id',
'link_type_id' =>
'link_type_id']
1271 'product_id = :product_id' 1275 $linkKey =
"{$productId}-{$linkData['linked_id']}-{$linkData['link_type_id']}";
1276 $productLinkKeys[$linkKey] =
$linkData[
'id'];
1278 foreach ($this->_linkNameToId as $linkName => $linkId) {
1280 if (isset($rowData[$linkName .
'sku'])) {
1282 $linkPositions = !empty($rowData[$linkName .
'position'])
1285 foreach ($linkSkus as $linkedKey => $linkedSku) {
1286 $linkedSku = trim($linkedSku);
1287 if (($this->skuProcessor->getNewSku($linkedSku) !==
null || $this->isSkuExist($linkedSku))
1288 && strcasecmp($linkedSku, $sku) !== 0
1290 $newSku = $this->skuProcessor->getNewSku($linkedSku);
1291 if (!empty($newSku)) {
1292 $linkedId = $newSku[
'entity_id'];
1294 $linkedId = $this->getExistingSku($linkedSku)[
'entity_id'];
1297 if ($linkedId ==
null) {
1301 $this->_logger->critical(
1304 'WARNING: Orphaned link skipped: From SKU %s (ID %d) to SKU %s, ' .
1316 $linkKey =
"{$productId}-{$linkedId}-{$linkId}";
1317 if (empty($productLinkKeys[$linkKey])) {
1318 $productLinkKeys[$linkKey] = $nextLinkId;
1320 if (!isset($linkRows[$linkKey])) {
1321 $linkRows[$linkKey] = [
1322 'link_id' => $productLinkKeys[$linkKey],
1324 'linked_product_id' => $linkedId,
1325 'link_type_id' => $linkId,
1328 if (!empty($linkPositions[$linkedKey])) {
1330 'link_id' => $productLinkKeys[$linkKey],
1331 'product_link_attribute_id' => $positionAttrId[$linkId],
1332 'value' => $linkPositions[$linkedKey],
1342 $this->_connection->delete(
1344 $this->_connection->quoteInto(
'product_id IN (?)', array_unique(
$productIds))
1348 $this->_connection->insertOnDuplicate($mainTable, $linkRows, [
'link_id']);
1350 if ($positionRows) {
1352 $this->_connection->insertOnDuplicate(
1353 $resource->getAttributeTypeTable(
'int'),
1370 $linkField = $this->getProductEntityLinkField();
1374 $linkId = $this->_oldSku[strtolower($sku)][$linkField];
1375 foreach (
$attributes as $attributeId => $storeValues) {
1376 foreach ($storeValues as
$storeId => $storeValue) {
1378 $linkField => $linkId,
1379 'attribute_id' => $attributeId,
1381 'value' => $storeValue,
1386 $this->_connection->insertOnDuplicate(
$tableName, $tableData, [
'value']);
1403 $tableName = $this->_resourceFactory->create()->getProductCategoryTable();
1405 if ($categoriesData) {
1409 foreach ($categoriesData as $delSku =>
$categories) {
1410 $productId = $this->skuProcessor->getNewSku($delSku)[
'entity_id'];
1413 foreach (array_keys(
$categories) as $categoryId) {
1414 $categoriesIn[] = [
'product_id' =>
$productId,
'category_id' => $categoryId,
'position' => 1];
1418 $this->_connection->delete(
1420 $this->_connection->quoteInto(
'product_id IN (?)', $delProductId)
1423 if ($categoriesIn) {
1424 $this->_connection->insertOnDuplicate(
$tableName, $categoriesIn, [
'product_id',
'category_id']);
1441 $this->countItemsCreated += count($entityRowsIn);
1442 $this->countItemsUpdated += count($entityRowsUp);
1445 $entityTable = $this->_resourceFactory->create()->getEntityTable();
1447 if ($entityRowsUp) {
1448 $this->_connection->insertOnDuplicate(
$entityTable, $entityRowsUp, [
'updated_at',
'attribute_set_id']);
1450 if ($entityRowsIn) {
1451 $this->_connection->insertMultiple(
$entityTable, $entityRowsIn);
1453 $select = $this->_connection->select()->from(
1455 array_merge($this->getNewSkuFieldsForSelect(), $this->getOldSkuFieldsForSelect())
1457 $this->_connection->quoteInto(
'sku IN (?)', array_keys($entityRowsIn))
1459 $newProducts = $this->_connection->fetchAll(
$select);
1460 foreach ($newProducts as
$data) {
1461 $sku =
$data[
'sku'];
1462 unset(
$data[
'sku']);
1464 $this->skuProcessor->setNewSkuData($sku, $key,
$value);
1468 $this->updateOldSku($newProducts);
1479 private function getOldSkuFieldsForSelect()
1481 return [
'type_id',
'attribute_set_id'];
1490 private function updateOldSku(array $newProducts)
1493 foreach ($newProducts as
$info) {
1494 $typeId =
$info[
'type_id'];
1495 $sku = strtolower(
$info[
'sku']);
1497 'type_id' => $typeId,
1498 'attr_set_id' =>
$info[
'attribute_set_id'],
1499 $this->getProductIdentifierField() =>
$info[$this->getProductIdentifierField()],
1500 'supported_type' => isset($this->_productTypeModels[$typeId]),
1501 $this->getProductEntityLinkField() =>
$info[$this->getProductEntityLinkField()],
1505 $this->_oldSku = array_replace($this->_oldSku, $oldSkus);
1513 private function getNewSkuFieldsForSelect()
1515 $fields = [
'sku', $this->getProductEntityLinkField()];
1516 if ($this->getProductEntityLinkField() != $this->getProductIdentifierField()) {
1517 $fields[] = $this->getProductIdentifierField();
1531 if (
null == $this->mediaGalleryTableName) {
1532 $this->productEntityTableName = $this->
getResource()->getTable(
'catalog_product_entity');
1533 $this->mediaGalleryTableName = $this->
getResource()->getTable(
'catalog_product_entity_media_gallery');
1534 $this->mediaGalleryValueTableName = $this->
getResource()->getTable(
1535 'catalog_product_entity_media_gallery_value' 1537 $this->mediaGalleryEntityToValueTableName = $this->
getResource()->getTable(
1538 'catalog_product_entity_media_gallery_value_to_entity' 1551 return $this->mediaProcessor->getExistingImages($bunch);
1564 foreach ($this->_imagesArrayKeys as $column) {
1565 if (!empty($rowData[$column])) {
1566 $images[$column] = array_unique(
1573 if (!empty($rowData[$column .
'_label'])) {
1574 $labels[$column] = $this->parseMultipleValues($rowData[$column .
'_label']);
1576 if (count($labels[$column]) > count($images[$column])) {
1577 $labels[$column] = array_slice($labels[$column], 0, count($images[$column]));
1583 return [$images, $labels];
1598 $priceIsGlobal = $this->_catalogData->isPriceGlobal();
1599 $productLimit =
null;
1600 $productsQty =
null;
1601 $entityLinkField = $this->getProductEntityLinkField();
1603 while ($bunch = $this->_dataSourceModel->getNextBunch()) {
1607 $this->websitesCache = [];
1608 $this->categoriesCache = [];
1611 $labelsForUpdate = [];
1612 $imagesForChangeVisibility = [];
1613 $uploadedImages = [];
1614 $previousType =
null;
1615 $prevAttributeSet =
null;
1618 foreach ($bunch as $rowNum => $rowData) {
1620 $this->categoryProcessor->clearFailedCategories();
1635 if (
null === $rowSku) {
1638 }
elseif (self::SCOPE_STORE == $rowScope) {
1640 $rowData[
self::COL_TYPE] = $this->skuProcessor->getNewSku($rowSku)[
'type_id'];
1641 $rowData[
'attribute_set_id'] = $this->skuProcessor->getNewSku($rowSku)[
'attr_set_id'];
1642 $rowData[
self::COL_ATTR_SET] = $this->skuProcessor->getNewSku($rowSku)[
'attr_set_code'];
1646 if ($this->isSkuExist($rowSku)) {
1648 if (isset($rowData[
'attribute_set_code'])) {
1651 $rowData[
'attribute_set_code']
1658 'Wrong attribute set code "%1", please correct it and try again.',
1659 $rowData[
'attribute_set_code']
1664 $attributeSetId = $this->skuProcessor->getNewSku($rowSku)[
'attr_set_id'];
1670 $entityLinkField => $this->getExistingSku($rowSku)[$entityLinkField]
1673 if (!$productLimit || $productsQty < $productLimit) {
1674 $entityRowsIn[strtolower($rowSku)] = [
1675 'attribute_set_id' => $this->skuProcessor->getNewSku($rowSku)[
'attr_set_id'],
1676 'type_id' => $this->skuProcessor->getNewSku($rowSku)[
'type_id'],
1678 'has_options' => isset($rowData[
'has_options']) ? $rowData[
'has_options'] : 0,
1691 if (!array_key_exists($rowSku, $this->websitesCache)) {
1692 $this->websitesCache[$rowSku] = [];
1695 if (!empty($rowData[self::COL_PRODUCT_WEBSITES])) {
1699 $this->websitesCache[$rowSku][
$websiteId] =
true;
1704 if (!array_key_exists($rowSku, $this->categoriesCache)) {
1705 $this->categoriesCache[$rowSku] = [];
1707 $rowData[
'rowNum'] = $rowNum;
1709 foreach ($categoryIds as
$id) {
1710 $this->categoriesCache[$rowSku][
$id] =
true;
1712 unset($rowData[
'rowNum']);
1715 if (!empty($rowData[
'_tier_price_website'])) {
1717 'all_groups' => $rowData[
'_tier_price_customer_group'] ==
self::VALUE_ALL,
1718 'customer_group_id' => $rowData[
'_tier_price_customer_group'] ==
1719 self::VALUE_ALL ? 0 : $rowData[
'_tier_price_customer_group'],
1720 'qty' => $rowData[
'_tier_price_qty'],
1721 'value' => $rowData[
'_tier_price_price'],
1722 'website_id' => self::VALUE_ALL == $rowData[
'_tier_price_website'] ||
1723 $priceIsGlobal ? 0 : $this->storeResolver->getWebsiteCodeToId($rowData[
'_tier_price_website']),
1733 $storeId = !empty($rowData[self::COL_STORE])
1736 $imageHiddenStates = $this->getImagesHiddenStates($rowData);
1737 foreach (array_keys($imageHiddenStates) as
$image) {
1738 if (array_key_exists($rowSku, $existingImages)
1739 && array_key_exists(
$image, $existingImages[$rowSku])
1745 if (empty($rowImages)) {
1757 foreach ($rowImages as $column => $columnImages) {
1758 foreach ($columnImages as $columnImageKey => $columnImage) {
1759 if (!isset($uploadedImages[$columnImage])) {
1761 $uploadedFile = $uploadedFile ?: $this->getSystemFile($columnImage);
1762 if ($uploadedFile) {
1763 $uploadedImages[$columnImage] = $uploadedFile;
1766 ValidatorInterface::ERROR_MEDIA_URL_NOT_ACCESSIBLE,
1774 $uploadedFile = $uploadedImages[$columnImage];
1777 if ($uploadedFile && $column !== self::COL_MEDIA_IMAGE) {
1778 $rowData[$column] = $uploadedFile;
1781 if ($uploadedFile && !isset($mediaGallery[
$storeId][$rowSku][$uploadedFile])) {
1782 if (isset($existingImages[$rowSku][$uploadedFile])) {
1783 $currentFileData = $existingImages[$rowSku][$uploadedFile];
1784 if (isset($rowLabels[$column][$columnImageKey])
1785 && $rowLabels[$column][$columnImageKey] !=
1786 $currentFileData[
'label']
1788 $labelsForUpdate[] = [
1789 'label' => $rowLabels[$column][$columnImageKey],
1790 'imageData' => $currentFileData
1794 if (array_key_exists($uploadedFile, $imageHiddenStates)
1795 && $currentFileData[
'disabled'] != $imageHiddenStates[$uploadedFile]
1797 $imagesForChangeVisibility[] = [
1798 'disabled' => $imageHiddenStates[$uploadedFile],
1799 'imageData' => $currentFileData
1803 if ($column == self::COL_MEDIA_IMAGE) {
1804 $rowData[$column][] = $uploadedFile;
1806 $mediaGallery[
$storeId][$rowSku][$uploadedFile] = [
1807 'attribute_id' => $this->getMediaGalleryAttributeId(),
1808 'label' => isset($rowLabels[$column][$columnImageKey])
1809 ? $rowLabels[$column][$columnImageKey]
1811 'position' => ++$position,
1812 'disabled' => isset($imageHiddenStates[$columnImage])
1813 ? $imageHiddenStates[$columnImage] :
'0',
1814 'value' => $uploadedFile,
1822 $rowStore = (self::SCOPE_STORE == $rowScope)
1823 ? $this->storeResolver->getStoreCodeToId($rowData[self::COL_STORE])
1825 $productType = isset($rowData[self::COL_TYPE]) ? $rowData[
self::COL_TYPE] :
null;
1826 if ($productType !==
null) {
1827 $previousType = $productType;
1829 if (isset($rowData[self::COL_ATTR_SET])) {
1832 if (self::SCOPE_NULL == $rowScope) {
1834 if ($prevAttributeSet !==
null) {
1837 if ($productType ===
null && $previousType !==
null) {
1838 $productType = $previousType;
1840 if ($productType ===
null) {
1845 $productTypeModel = $this->_productTypeModels[$productType];
1846 if (!empty($rowData[
'tax_class_name'])) {
1847 $rowData[
'tax_class_id'] =
1848 $this->taxClassProcessor->upsertTaxClass($rowData[
'tax_class_name'], $productTypeModel);
1852 empty($rowData[self::COL_SKU])
1854 $rowData = $productTypeModel->clearEmptyData($rowData);
1857 $rowData = $productTypeModel->prepareAttributesWithDefaultValueForSave(
1859 !$this->isSkuExist($rowSku)
1861 $product = $this->_proxyProdFactory->create([
'data' => $rowData]);
1863 foreach ($rowData as $attrCode => $attrValue) {
1864 $attribute = $this->retrieveAttributeByCode($attrCode);
1866 if (
'multiselect' !=
$attribute->getFrontendInput() && self::SCOPE_NULL == $rowScope) {
1872 $attrTable =
$attribute->getBackend()->getTable();
1875 if (
'datetime' ==
$attribute->getBackendType()
1881 $attrValue = $this->dateTime->formatDate($attrValue,
false);
1882 }
elseif (
'datetime' ==
$attribute->getBackendType() && strtotime($attrValue)) {
1885 $this->_localeDate->date($attrValue)->getTimestamp()
1891 if (self::SCOPE_STORE == $rowScope) {
1892 if (self::SCOPE_WEBSITE ==
$attribute->getIsGlobal()) {
1894 if (!isset(
$attributes[$attrTable][$rowSku][$attrId][$rowStore])) {
1895 $storeIds = $this->storeResolver->getStoreIdToWebsiteStoreIds($rowStore);
1898 $storeIds = [$rowStore];
1900 if (!$this->isSkuExist($rowSku)) {
1914 foreach ($bunch as $rowNum => $rowData) {
1916 unset($bunch[$rowNum]);
1923 )->_saveProductWebsites(
1924 $this->websitesCache
1925 )->_saveProductCategories(
1926 $this->categoriesCache
1927 )->_saveProductTierPrices(
1929 )->_saveMediaGallery(
1931 )->_saveProductAttributes(
1933 )->updateMediaGalleryVisibility(
1934 $imagesForChangeVisibility
1935 )->updateMediaGalleryLabels(
1939 $this->_eventManager->dispatch(
1940 'catalog_product_import_bunch_save_after',
1941 [
'adapter' => $this,
'bunch' => $bunch]
1954 private function getImagesHiddenStates($rowData)
1958 '_media_is_disabled' =>
'1' 1961 foreach ($mappingArray as $key =>
$value) {
1962 if (isset($rowData[$key]) && strlen(trim($rowData[$key]))) {
1971 return $statesArray;
1982 $categoriesString = empty($rowData[self::COL_CATEGORY]) ?
'' : $rowData[
self::COL_CATEGORY];
1984 if (!empty($categoriesString)) {
1985 $categoryIds = $this->categoryProcessor->upsertCategories(
1989 foreach ($this->categoryProcessor->getFailedCategories() as $error) {
1990 $this->errorAggregator->addError(
1991 AbstractEntity::ERROR_CODE_CATEGORY_NOT_VALID,
1995 __(
'Category "%1" has not been created.', $error[
'category'])
1996 .
' ' . $error[
'exception']->getMessage()
2000 return $categoryIds;
2011 return array_keys($this->websitesCache[$productSku]);
2022 return array_keys($this->categoriesCache[$productSku]);
2036 return $this->storeResolver->getStoreCodeToId(
$storeCode);
2050 $tableName = $this->_resourceFactory->create()->getTable(
'catalog_product_entity_tier_price');
2057 $productId = $this->skuProcessor->getNewSku($delSku)[$this->getProductEntityLinkField()];
2060 foreach ($tierPriceRows as
$row) {
2062 $tierPriceIn[] =
$row;
2066 $this->_connection->delete(
2068 $this->_connection->quoteInto(
"{$this->getProductEntityLinkField()} IN (?)", $delProductId)
2072 $this->_connection->insertOnDuplicate(
$tableName, $tierPriceIn, [
'value']);
2086 if ($this->_fileUploader ===
null) {
2087 $this->_fileUploader = $this->_uploaderFactory->create();
2089 $this->_fileUploader->init();
2097 $tmpPath = $dirAddon .
'/' . $this->_mediaDirectory->getRelativePath(
'import');
2100 if (!$this->_fileUploader->setTmpDir($tmpPath)) {
2102 __(
'File directory \'%1\' is not readable.', $tmpPath)
2105 $destinationDir =
"catalog/product";
2106 $destinationPath = $dirAddon .
'/' . $this->_mediaDirectory->getRelativePath($destinationDir);
2108 $this->_mediaDirectory->create($destinationPath);
2109 if (!$this->_fileUploader->setDestDir($destinationPath)) {
2111 __(
'File directory \'%1\' is not writable.', $destinationPath)
2143 return $res[
'file'];
2144 }
catch (\Exception $e) {
2145 $this->_logger->critical($e);
2156 private function getSystemFile(
$fileName)
2158 $filePath =
'catalog' . DIRECTORY_SEPARATOR .
'product' . DIRECTORY_SEPARATOR .
$fileName;
2162 return $read->isExist($filePath) && $read->isReadable($filePath) ?
$fileName :
'';
2173 if (empty($mediaGalleryData)) {
2176 $this->mediaProcessor->saveMediaGallery($mediaGalleryData);
2192 $tableName = $this->_resourceFactory->create()->getProductWebsiteTable();
2198 foreach ($websiteData as $delSku =>
$websites) {
2199 $productId = $this->skuProcessor->getNewSku($delSku)[
'entity_id'];
2207 $this->_connection->delete(
2209 $this->_connection->quoteInto(
'product_id IN (?)', $delProductId)
2212 if ($websitesData) {
2213 $this->_connection->insertOnDuplicate(
$tableName, $websitesData);
2226 while ($bunch = $this->_dataSourceModel->getNextBunch()) {
2228 $productIdsToReindex = [];
2230 foreach ($bunch as $rowNum => $rowData) {
2237 if ($this->skuProcessor->getNewSku($sku) !==
null) {
2238 $row = $this->formatStockDataForRow($rowData);
2239 $productIdsToReindex[] =
$row[
'product_id'];
2249 $this->stockItemImporter->import(
$stockData);
2252 $this->reindexProducts($productIdsToReindex);
2263 private function reindexProducts($productIdsToReindex = [])
2265 $indexer = $this->indexerRegistry->get(
'catalog_product_category');
2266 if (is_array($productIdsToReindex) && count($productIdsToReindex) > 0 && !
$indexer->isScheduled()) {
2267 $indexer->reindexList($productIdsToReindex);
2277 public function retrieveAttributeByCode($attrCode)
2280 $attrCode = mb_strtolower($attrCode);
2282 if (!isset($this->_attributeCache[$attrCode])) {
2283 $this->_attributeCache[$attrCode] = $this->
getResource()->getAttribute($attrCode);
2286 return $this->_attributeCache[$attrCode];
2317 return 'catalog_product';
2331 return $this->skuProcessor->getNewSku($sku);
2341 return $this->_dataSourceModel->getNextBunch();
2375 if (empty($rowData[self::COL_STORE])) {
2391 public function validateRow(array $rowData, $rowNum)
2393 if (isset($this->_validatedRows[$rowNum])) {
2397 $this->_validatedRows[$rowNum] =
true;
2404 if (self::SCOPE_DEFAULT == $rowScope && !$this->isSkuExist($sku)) {
2405 $this->
addRowError(ValidatorInterface::ERROR_SKU_NOT_FOUND_FOR_DELETE, $rowNum);
2410 if (self::SCOPE_DEFAULT == $rowScope && !$this->isSkuExist($sku)) {
2411 $this->
addRowError(ValidatorInterface::ERROR_SKU_NOT_FOUND_FOR_DELETE, $rowNum);
2417 if (!$this->validator->isValid($rowData)) {
2418 foreach ($this->validator->getMessages() as
$message) {
2423 if (
null === $sku) {
2424 $this->
addRowError(ValidatorInterface::ERROR_SKU_IS_EMPTY, $rowNum);
2425 }
elseif (
false === $sku) {
2426 $this->
addRowError(ValidatorInterface::ERROR_ROW_IS_ORPHAN, $rowNum);
2427 }
elseif (self::SCOPE_STORE == $rowScope
2428 && !$this->storeResolver->getStoreCodeToId($rowData[self::COL_STORE])
2430 $this->
addRowError(ValidatorInterface::ERROR_INVALID_STORE, $rowNum);
2434 $this->_processedEntitiesCount++;
2439 if (isset($this->_productTypeModels[$this->getExistingSku($sku)[
'type_id']])) {
2440 $this->skuProcessor->addNewSku(
2442 $this->prepareNewSkuData($sku)
2445 $this->
addRowError(ValidatorInterface::ERROR_TYPE_UNSUPPORTED, $rowNum);
2449 if (!isset($rowData[self::COL_TYPE]) || !isset($this->_productTypeModels[$rowData[self::COL_TYPE]])) {
2450 $this->
addRowError(ValidatorInterface::ERROR_INVALID_TYPE, $rowNum);
2451 }
elseif (!isset($rowData[self::COL_ATTR_SET])
2452 || !isset($this->_attrSetNameToId[$rowData[self::COL_ATTR_SET]])
2454 $this->
addRowError(ValidatorInterface::ERROR_INVALID_ATTR_SET, $rowNum);
2455 }
elseif ($this->skuProcessor->getNewSku($sku) ===
null) {
2456 $this->skuProcessor->addNewSku(
2460 'entity_id' =>
null,
2461 'type_id' => $rowData[self::COL_TYPE],
2462 'attr_set_id' => $this->_attrSetNameToId[$rowData[self::COL_ATTR_SET]],
2463 'attr_set_code' => $rowData[self::COL_ATTR_SET],
2470 $newSku = $this->skuProcessor->getNewSku($sku);
2476 $productTypeValidator = $this->_productTypeModels[$newSku[
'type_id']];
2477 $productTypeValidator->isRowValid(
2486 if ($this->isNeedToValidateUrlKey($rowData)) {
2487 $urlKey = strtolower($this->
getUrlKey($rowData));
2488 $storeCodes = empty($rowData[self::COL_STORE_VIEW_CODE])
2489 ? array_flip($this->storeResolver->getStoreCodeToId())
2495 if (empty($this->urlKeys[
$storeId][$urlPath])
2496 || ($this->urlKeys[
$storeId][$urlPath] == $sku)
2498 $this->urlKeys[
$storeId][$urlPath] = $sku;
2499 $this->rowNumbers[
$storeId][$urlPath] = $rowNum;
2507 ValidatorInterface::ERROR_DUPLICATE_URL_KEY,
2509 $rowData[self::COL_NAME],
2524 private function isNeedToValidateUrlKey($rowData)
2526 return (!empty($rowData[self::URL_KEY]) || !empty($rowData[self::COL_NAME]))
2527 && (empty($rowData[self::COL_VISIBILITY])
2538 private function prepareNewSkuData($sku)
2541 foreach ($this->getExistingSku($sku) as $key =>
$value) {
2545 $data[
'attr_set_code'] = $this->_attrSetIdToName[$this->getExistingSku($sku)[
'attr_set_id']];
2557 private function _parseAdditionalAttributes($rowData)
2559 if (empty($rowData[
'additional_attributes'])) {
2562 $rowData = array_merge($rowData, $this->getAdditionalAttributes($rowData[
'additional_attributes']));
2578 private function getAdditionalAttributes($additionalAttributes)
2581 ? $this->parseAttributesWithoutWrappedValues($additionalAttributes)
2582 : $this->parseAttributesWithWrappedValues($additionalAttributes);
2601 $preparedAttributes = [];
2605 if (strpos(
$attributeData, self::PAIR_NAME_VALUE_SEPARATOR) ===
false) {
2616 return $preparedAttributes;
2641 '~((?:[a-zA-Z0-9_])+)="((?:[^"]|""|"' . $this->getMultiLineSeparatorForRegexp() .
'")+)"+~',
2648 ? str_replace(
'""',
'"', $matches[2][
$i])
2649 :
'"' . $matches[2][
$i] .
'"';
2666 return explode($delimiter,
$values);
2668 if (preg_match_all(
'~"((?:[^"]|"")*)"~',
$values, $matches)) {
2670 return str_replace(
'""',
'"',
$value);
2681 private function getMultiLineSeparatorForRegexp()
2683 if (!$this->multiLineSeparatorForRegexp) {
2684 $this->multiLineSeparatorForRegexp = in_array(self::PSEUDO_MULTI_LINE_SEPARATOR, str_split(
'[\^$.|?*+(){}'))
2688 return $this->multiLineSeparatorForRegexp;
2698 private function _setStockUseConfigFieldsValues($rowData)
2700 $useConfigFields = [];
2701 foreach ($rowData as $key =>
$value) {
2704 : self::INVENTORY_USE_CONFIG_PREFIX . $key;
2706 if (isset($this->defaultStockData[$key])
2707 && isset($this->defaultStockData[$useConfigName])
2709 && empty($rowData[$useConfigName])
2714 $rowData = array_merge($rowData, $useConfigFields);
2725 private function _customFieldsMapping($rowData)
2727 foreach ($this->_fieldsMap as $systemFieldName => $fileFieldName) {
2728 if (array_key_exists($fileFieldName, $rowData)) {
2729 $rowData[$systemFieldName] = $rowData[$fileFieldName];
2733 $rowData = $this->_parseAdditionalAttributes($rowData);
2735 $rowData = $this->_setStockUseConfigFieldsValues($rowData);
2736 if (array_key_exists(
'status', $rowData)
2737 && $rowData[
'status'] != \
Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED
2739 if ($rowData[
'status'] ==
'yes') {
2740 $rowData[
'status'] = \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED;
2741 }
elseif (!empty($rowData[
'status']) || $this->
getRowScope($rowData) == self::SCOPE_DEFAULT) {
2742 $rowData[
'status'] = \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED;
2760 $rowData =
$source->current();
2761 }
catch (\InvalidArgumentException $e) {
2763 $this->_processedRowsCount++;
2768 $rowData = $this->_customFieldsMapping($rowData);
2776 return parent::_saveValidatedBunches();
2789 $urlKeyDuplicates = $this->_connection->fetchAssoc(
2790 $this->_connection->select()->from(
2791 [
'url_rewrite' =>
$resource->getTable(
'url_rewrite')],
2792 [
'request_path',
'store_id']
2794 [
'cpe' =>
$resource->getTable(
'catalog_product_entity')],
2795 "cpe.entity_id = url_rewrite.entity_id" 2796 )->where(
'request_path IN (?)', array_keys(
$urlKeys))
2797 ->where(
'store_id IN (?)',
$storeId)
2798 ->where(
'cpe.sku not in (?)', array_values(
$urlKeys))
2800 foreach ($urlKeyDuplicates as $entityData) {
2801 $rowNum = $this->rowNumbers[$entityData[
'store_id']][$entityData[
'request_path']];
2804 $entityData[
'request_path'],
2807 $this->
addRowError(ValidatorInterface::ERROR_DUPLICATE_URL_KEY, $rowNum,
'url_key',
$message);
2821 if (!isset($this->productUrlSuffix[
$storeId])) {
2822 $this->productUrlSuffix[
$storeId] = $this->scopeConfig->getValue(
2823 \
Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator::XML_PATH_PRODUCT_URL_SUFFIX,
2828 return $this->productUrlSuffix[
$storeId];
2841 if (!empty($rowData[self::URL_KEY])) {
2842 return strtolower($rowData[self::URL_KEY]);
2845 if (!empty($rowData[self::COL_NAME])) {
2846 return $this->productUrl->formatUrlKey($rowData[self::COL_NAME]);
2861 if (!$this->_resource) {
2862 $this->_resource = $this->_resourceFactory->create();
2872 private function getProductEntityLinkField()
2874 if (!$this->productEntityLinkField) {
2876 ->getMetadata(\
Magento\Catalog\Api\Data\ProductInterface::class)
2879 return $this->productEntityLinkField;
2887 private function getProductIdentifierField()
2889 if (!$this->productEntityIdentifierField) {
2891 ->getMetadata(\
Magento\Catalog\Api\Data\ProductInterface::class)
2892 ->getIdentifierField();
2894 return $this->productEntityIdentifierField;
2903 private function updateMediaGalleryLabels(array $labels)
2905 if (!empty($labels)) {
2906 $this->mediaProcessor->updateMediaGalleryLabels($labels);
2916 private function updateMediaGalleryVisibility(array $images)
2918 if (!empty($images)) {
2919 $this->mediaProcessor->updateMediaGalleryVisibility($images);
2931 private function parseMultipleValues($labelRow)
2945 private function isSkuExist($sku)
2947 $sku = strtolower($sku);
2948 return isset($this->_oldSku[$sku]);
2957 private function getExistingSku($sku)
2959 return $this->_oldSku[strtolower($sku)];
2968 private function formatStockDataForRow(array $rowData): array
2971 $row[
'product_id'] = $this->skuProcessor->getNewSku($sku)[
'entity_id'];
2972 $row[
'website_id'] = $this->stockConfiguration->getDefaultScopeId();
2973 $row[
'stock_id'] = $this->stockRegistry->getStock(
$row[
'website_id'])->getStockId();
2975 $stockItemDo = $this->stockRegistry->getStockItem(
$row[
'product_id'],
$row[
'website_id']);
2976 $existStockData = $stockItemDo->getData();
2979 $this->defaultStockData,
2980 array_intersect_key($existStockData, $this->defaultStockData),
2981 array_intersect_key($rowData, $this->defaultStockData),
2985 if ($this->stockConfiguration->isQty($this->skuProcessor->getNewSku($sku)[
'type_id'])) {
2986 $stockItemDo->setData(
$row);
2987 $row[
'is_in_stock'] = isset(
$row[
'is_in_stock']) && $stockItemDo->getBackorders()
2988 ?
$row[
'is_in_stock']
2989 : $this->stockStateProvider->verifyStock($stockItemDo);
2990 if ($this->stockStateProvider->verifyNotification($stockItemDo)) {
2991 $date = $this->dateTimeFactory->create(
'now',
new \DateTimeZone(
'UTC'));
2994 $row[
'stock_status_changed_auto'] = (int)!$this->stockStateProvider->verifyStock($stockItemDo);
const DATETIME_PHP_FORMAT
const INVENTORY_USE_CONFIG_PREFIX
getExistingImages($bunch)
if($msrpShowOnGesture && $price['price']->getValue()< $product->getMsrp()) if($isSaleable) $tierPriceData
const ATTRIBUTE_DELETE_BUNCH
const ERROR_LEVEL_NOT_CRITICAL
setParameters(array $params)
elseif(isset( $params[ 'redirect_parent']))
addMessageTemplate($errorCode, $message)
uploadMediaFiles($fileName, $renameFileOff=false)
const MEDIA_GALLERY_ATTRIBUTE_CODE
const USE_CONFIG_ENABLE_QTY_INC
retrieveMessageTemplate($errorCode)
retrieveProductTypeByName($name)
validateRow(array $rowData, $rowNum)
const DEFAULT_EMPTY_ATTRIBUTE_VALUE_CONSTANT
$mediaGalleryValueTableName
const FIELD_NAME_IMG_FILE_DIR
static getDefaultConfig()
_saveProductCategories(array $categoriesData)
getProductWebsites($productSku)
_saveProductWebsites(array $websiteData)
const DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR
const FIELD_FIELD_MULTIPLE_VALUE_SEPARATOR
const PSEUDO_MULTI_LINE_SEPARATOR
_prepareRowForDb(array $rowData)
const CONFIG_KEY_PRODUCT_TYPES
const PAIR_NAME_VALUE_SEPARATOR
getStoreIdByCode($storeCode)
_saveProductTierPrices(array $tierPriceData)
processRowCategories($rowData)
getMultipleValueSeparator()
getRowScope(array $rowData)
addRowError( $errorCode, $errorRowNum, $colName=null, $errorMessage=null, $errorLevel=ProcessingError::ERROR_LEVEL_CRITICAL, $errorDescription=null)
if($product->getId()) catch(\Magento\Framework\Exception\NoSuchEntityException $e) $storeCodes
_saveProductAttributes(array $attributesData)
getProductCategories($productSku)
$mediaGalleryEntityToValueTableName
getEmptyAttributeValueConstant()
getImagesFromRow(array $rowData)
const COL_PRODUCT_WEBSITES
const FIELD_EMPTY_ATTRIBUTE_VALUE_CONSTANT
getProductUrlSuffix($storeId=null)
isRowAllowedToImport(array $rowData, $rowNum)
__construct(\Magento\Framework\Json\Helper\Data $jsonHelper, \Magento\ImportExport\Helper\Data $importExportData, \Magento\ImportExport\Model\ResourceModel\Import\Data $importData, \Magento\Eav\Model\Config $config, \Magento\Framework\App\ResourceConnection $resource, \Magento\ImportExport\Model\ResourceModel\Helper $resourceHelper, \Magento\Framework\Stdlib\StringUtils $string, ProcessingErrorAggregatorInterface $errorAggregator, \Magento\Framework\Event\ManagerInterface $eventManager, \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry, \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration, \Magento\CatalogInventory\Model\Spi\StockStateProviderInterface $stockStateProvider, \Magento\Catalog\Helper\Data $catalogData, \Magento\ImportExport\Model\Import\Config $importConfig, \Magento\CatalogImportExport\Model\Import\Proxy\Product\ResourceModelFactory $resourceFactory, \Magento\CatalogImportExport\Model\Import\Product\OptionFactory $optionFactory, \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory $setColFactory, \Magento\CatalogImportExport\Model\Import\Product\Type\Factory $productTypeFactory, \Magento\Catalog\Model\ResourceModel\Product\LinkFactory $linkFactory, \Magento\CatalogImportExport\Model\Import\Proxy\ProductFactory $proxyProdFactory, \Magento\CatalogImportExport\Model\Import\UploaderFactory $uploaderFactory, \Magento\Framework\Filesystem $filesystem, \Magento\CatalogInventory\Model\ResourceModel\Stock\ItemFactory $stockResItemFac, \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, DateTime $dateTime, \Psr\Log\LoggerInterface $logger, \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry, Product\StoreResolver $storeResolver, Product\SkuProcessor $skuProcessor, Product\CategoryProcessor $categoryProcessor, Product\Validator $validator, ObjectRelationProcessor $objectRelationProcessor, TransactionManagerInterface $transactionManager, Product\TaxClassProcessor $taxClassProcessor, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Catalog\Model\Product\Url $productUrl, array $data=[], array $dateAttrCodes=[], CatalogConfig $catalogConfig=null, ImageTypeProcessor $imageTypeProcessor=null, MediaGalleryProcessor $mediaProcessor=null, StockItemImporterInterface $stockItemImporter=null, DateTimeFactory $dateTimeFactory=null)
saveProductEntity(array $entityRowsIn, array $entityRowsUp)
const ENABLE_QTY_INCREMENTS
deleteProductsForReplacement()
foreach( $_productCollection as $_product)() ?>" class $info
const VISIBILITY_NOT_VISIBLE
$_mediaGalleryAttributeId
const COL_STORE_VIEW_CODE
if(!isset($_GET['website_code'])) $websiteCode
$params[\Magento\Store\Model\StoreManager::PARAM_RUN_CODE]
const INVENTORY_USE_CONFIG
parseMultiselectValues($values, $delimiter=self::PSEUDO_MULTI_LINE_SEPARATOR)
initMediaGalleryResources()
_saveMediaGallery(array $mediaGalleryData)
if(!isset($_GET['name'])) $name
isAttributeValid($attrCode, array $attrParams, array $rowData, $rowNum)