mirror of
https://github.com/bdnugget/magento2.git
synced 2026-11-07 09:17:35 +00:00
957 lines
29 KiB
PHP
957 lines
29 KiB
PHP
<?php
|
|
/**
|
|
* Copyright © Magento, Inc. All rights reserved.
|
|
* See COPYING.txt for license details.
|
|
*/
|
|
|
|
namespace Magento\ImportExport\Model;
|
|
|
|
use Magento\Eav\Model\Entity\Attribute;
|
|
use Magento\Eav\Model\Entity\Attribute\AbstractAttribute;
|
|
use Magento\Framework\App\Config\ScopeConfigInterface;
|
|
use Magento\Framework\App\Filesystem\DirectoryList;
|
|
use Magento\Framework\App\ObjectManager;
|
|
use Magento\Framework\Exception\FileSystemException;
|
|
use Magento\Framework\Exception\LocalizedException;
|
|
use Magento\Framework\Exception\ValidatorException;
|
|
use Magento\Framework\Filesystem;
|
|
use Magento\Framework\HTTP\Adapter\FileTransferFactory;
|
|
use Magento\Framework\Indexer\IndexerRegistry;
|
|
use Magento\Framework\Math\Random;
|
|
use Magento\Framework\Message\ManagerInterface;
|
|
use Magento\Framework\Stdlib\DateTime\DateTime;
|
|
use Magento\ImportExport\Helper\Data as DataHelper;
|
|
use Magento\ImportExport\Model\Export\Adapter\CsvFactory;
|
|
use Magento\ImportExport\Model\Import\AbstractEntity as ImportAbstractEntity;
|
|
use Magento\ImportExport\Model\Import\AbstractSource;
|
|
use Magento\ImportExport\Model\Import\Adapter;
|
|
use Magento\ImportExport\Model\Import\ConfigInterface;
|
|
use Magento\ImportExport\Model\Import\Entity\AbstractEntity;
|
|
use Magento\ImportExport\Model\Import\Entity\Factory;
|
|
use Magento\ImportExport\Model\Import\EntityInterface;
|
|
use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingError;
|
|
use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface;
|
|
use Magento\ImportExport\Model\ResourceModel\Import\Data;
|
|
use Magento\ImportExport\Model\Source\Import\AbstractBehavior;
|
|
use Magento\ImportExport\Model\Source\Import\Behavior\Factory as BehaviorFactory;
|
|
use Magento\ImportExport\Model\Source\Upload;
|
|
use Magento\MediaStorage\Model\File\UploaderFactory;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
/**
|
|
* Import model
|
|
*
|
|
* @api
|
|
*
|
|
* @method string getBehavior() getBehavior()
|
|
* @method self setEntity() setEntity(string $value)
|
|
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
|
|
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
|
|
* @SuppressWarnings(PHPMD.TooManyFields)
|
|
* @since 100.0.2
|
|
*/
|
|
class Import extends AbstractModel
|
|
{
|
|
public const BEHAVIOR_APPEND = 'append';
|
|
public const BEHAVIOR_ADD_UPDATE = 'add_update';
|
|
public const BEHAVIOR_REPLACE = 'replace';
|
|
public const BEHAVIOR_DELETE = 'delete';
|
|
public const BEHAVIOR_CUSTOM = 'custom';
|
|
|
|
/**
|
|
* Import source file.
|
|
*/
|
|
public const FIELD_NAME_SOURCE_FILE = 'import_file';
|
|
|
|
/**
|
|
* Import image archive.
|
|
*/
|
|
public const FIELD_NAME_IMG_ARCHIVE_FILE = 'import_image_archive';
|
|
|
|
/**
|
|
* Import images file directory.
|
|
*/
|
|
public const FIELD_NAME_IMG_FILE_DIR = 'import_images_file_dir';
|
|
|
|
/**
|
|
* Allowed errors count field name
|
|
*/
|
|
public const FIELD_NAME_ALLOWED_ERROR_COUNT = 'allowed_error_count';
|
|
|
|
/**
|
|
* Validation startegt field name
|
|
*/
|
|
public const FIELD_NAME_VALIDATION_STRATEGY = 'validation_strategy';
|
|
|
|
/**
|
|
* Import field separator.
|
|
*/
|
|
public const FIELD_FIELD_SEPARATOR = '_import_field_separator';
|
|
|
|
/**
|
|
* Import multiple value separator.
|
|
*/
|
|
public const FIELD_FIELD_MULTIPLE_VALUE_SEPARATOR = '_import_multiple_value_separator';
|
|
|
|
/**
|
|
* Import empty attribute value constant.
|
|
*/
|
|
public const FIELD_EMPTY_ATTRIBUTE_VALUE_CONSTANT = '_import_empty_attribute_value_constant';
|
|
|
|
/**
|
|
* Id of the `importexport_importdata` row after validation.
|
|
*/
|
|
public const FIELD_IMPORT_IDS = '_import_ids';
|
|
|
|
/**
|
|
* Allow multiple values wrapping in double quotes for additional attributes.
|
|
*/
|
|
public const FIELDS_ENCLOSURE = 'fields_enclosure';
|
|
|
|
/**
|
|
* default delimiter for several values in one cell as default for FIELD_FIELD_MULTIPLE_VALUE_SEPARATOR
|
|
*/
|
|
public const DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR = ',';
|
|
|
|
/**
|
|
* Import empty attribute default value
|
|
*/
|
|
public const DEFAULT_EMPTY_ATTRIBUTE_VALUE_CONSTANT = '__EMPTY__VALUE__';
|
|
public const DEFAULT_SIZE = 50;
|
|
public const MAX_IMPORT_CHUNKS = 4;
|
|
public const IMPORT_HISTORY_DIR = 'import_history/';
|
|
public const IMPORT_DIR = 'import/';
|
|
|
|
/**
|
|
* @var EntityInterface
|
|
*/
|
|
protected $_entityAdapter;
|
|
|
|
/**
|
|
* @Deprecated Property isn't used
|
|
* @var DataHelper
|
|
*/
|
|
protected $_importExportData = null;
|
|
|
|
/**
|
|
* @var \Magento\Framework\App\Config\ScopeConfigInterface
|
|
*/
|
|
private $_coreConfig;
|
|
|
|
/**
|
|
* @var ConfigInterface
|
|
*/
|
|
protected $_importConfig;
|
|
|
|
/**
|
|
* @var Factory
|
|
*/
|
|
protected $_entityFactory;
|
|
|
|
/**
|
|
* @var Data
|
|
*/
|
|
protected $_importData;
|
|
|
|
/**
|
|
* @var CsvFactory
|
|
*/
|
|
protected $_csvFactory;
|
|
|
|
/**
|
|
* @Deprecated Property isn't used
|
|
* @var FileTransferFactory
|
|
*/
|
|
protected $_httpFactory;
|
|
|
|
/**
|
|
* @var UploaderFactory
|
|
*/
|
|
protected $_uploaderFactory;
|
|
|
|
/**
|
|
* @var IndexerRegistry
|
|
*/
|
|
protected $indexerRegistry;
|
|
|
|
/**
|
|
* @var BehaviorFactory
|
|
*/
|
|
protected $_behaviorFactory;
|
|
|
|
/**
|
|
* @var Filesystem
|
|
*/
|
|
protected $_filesystem;
|
|
|
|
/**
|
|
* @var History
|
|
*/
|
|
private $importHistoryModel;
|
|
|
|
/**
|
|
* @var DateTime
|
|
*/
|
|
private $localeDate;
|
|
|
|
/**
|
|
* @var ManagerInterface
|
|
*/
|
|
private $messageManager;
|
|
|
|
/**
|
|
* @Deprecated Property isn't used
|
|
* @var Random
|
|
*/
|
|
private $random;
|
|
|
|
/**
|
|
* @var Upload
|
|
*/
|
|
private $upload;
|
|
|
|
/**
|
|
* @var LocaleEmulatorInterface
|
|
*/
|
|
private $localeEmulator;
|
|
|
|
/**
|
|
* @param LoggerInterface $logger
|
|
* @param Filesystem $filesystem
|
|
* @param DataHelper $importExportData
|
|
* @param ScopeConfigInterface $coreConfig
|
|
* @param ConfigInterface $importConfig
|
|
* @param Factory $entityFactory
|
|
* @param Data $importData
|
|
* @param CsvFactory $csvFactory
|
|
* @param FileTransferFactory $httpFactory
|
|
* @param UploaderFactory $uploaderFactory
|
|
* @param Factory $behaviorFactory
|
|
* @param IndexerRegistry $indexerRegistry
|
|
* @param History $importHistoryModel
|
|
* @param DateTime $localeDate
|
|
* @param array $data
|
|
* @param ManagerInterface|null $messageManager
|
|
* @param Random|null $random
|
|
* @param Upload|null $upload
|
|
* @param LocaleEmulatorInterface|null $localeEmulator
|
|
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
|
|
*/
|
|
public function __construct(
|
|
LoggerInterface $logger,
|
|
Filesystem $filesystem,
|
|
DataHelper $importExportData,
|
|
ScopeConfigInterface $coreConfig,
|
|
ConfigInterface $importConfig,
|
|
Factory $entityFactory,
|
|
Data $importData,
|
|
CsvFactory $csvFactory,
|
|
FileTransferFactory $httpFactory,
|
|
UploaderFactory $uploaderFactory,
|
|
BehaviorFactory $behaviorFactory,
|
|
IndexerRegistry $indexerRegistry,
|
|
History $importHistoryModel,
|
|
DateTime $localeDate,
|
|
array $data = [],
|
|
ManagerInterface $messageManager = null,
|
|
Random $random = null,
|
|
Upload $upload = null,
|
|
LocaleEmulatorInterface $localeEmulator = null
|
|
) {
|
|
$this->_importExportData = $importExportData;
|
|
$this->_coreConfig = $coreConfig;
|
|
$this->_importConfig = $importConfig;
|
|
$this->_entityFactory = $entityFactory;
|
|
$this->_importData = $importData;
|
|
$this->_csvFactory = $csvFactory;
|
|
$this->_httpFactory = $httpFactory;
|
|
$this->_uploaderFactory = $uploaderFactory;
|
|
$this->indexerRegistry = $indexerRegistry;
|
|
$this->_behaviorFactory = $behaviorFactory;
|
|
$this->_filesystem = $filesystem;
|
|
$this->importHistoryModel = $importHistoryModel;
|
|
$this->localeDate = $localeDate;
|
|
$this->messageManager = $messageManager ?: ObjectManager::getInstance()
|
|
->get(ManagerInterface::class);
|
|
$this->random = $random ?: ObjectManager::getInstance()
|
|
->get(Random::class);
|
|
$this->upload = $upload ?: ObjectManager::getInstance()
|
|
->get(Upload::class);
|
|
$this->localeEmulator = $localeEmulator ?: ObjectManager::getInstance()
|
|
->get(LocaleEmulatorInterface::class);
|
|
parent::__construct($logger, $filesystem, $data);
|
|
}
|
|
|
|
/**
|
|
* Returns or create existing instance of entity adapter
|
|
*
|
|
* @throws LocalizedException
|
|
* @return EntityInterface
|
|
*/
|
|
protected function _getEntityAdapter()
|
|
{
|
|
if (!$this->_entityAdapter) {
|
|
$this->_entityAdapter = $this->localeEmulator->emulate(
|
|
$this->createEntityAdapter(...),
|
|
$this->getData('locale') ?: null
|
|
);
|
|
}
|
|
|
|
return $this->_entityAdapter;
|
|
}
|
|
|
|
/**
|
|
* Create instance of entity adapter and return it
|
|
*
|
|
* @throws LocalizedException
|
|
* @return EntityInterface
|
|
*/
|
|
private function createEntityAdapter()
|
|
{
|
|
if (!$this->_entityAdapter) {
|
|
$entities = $this->_importConfig->getEntities();
|
|
if (isset($entities[$this->getEntity()])) {
|
|
try {
|
|
$this->_entityAdapter = $this->_entityFactory->create($entities[$this->getEntity()]['model']);
|
|
} catch (\Exception $e) {
|
|
$this->_logger->critical($e);
|
|
throw new LocalizedException(
|
|
__('Please enter a correct entity model.')
|
|
);
|
|
}
|
|
if (!$this->_entityAdapter instanceof AbstractEntity &&
|
|
!$this->_entityAdapter instanceof ImportAbstractEntity
|
|
) {
|
|
throw new LocalizedException(
|
|
__(
|
|
'The entity adapter object must be an instance of %1 or %2.',
|
|
AbstractEntity::class,
|
|
ImportAbstractEntity::class
|
|
)
|
|
);
|
|
}
|
|
|
|
// check for entity codes integrity
|
|
if ($this->getEntity() != $this->_entityAdapter->getEntityTypeCode()) {
|
|
throw new LocalizedException(
|
|
__('The input entity code is not equal to entity adapter code.')
|
|
);
|
|
}
|
|
} else {
|
|
throw new LocalizedException(__('Please enter a correct entity.'));
|
|
}
|
|
$this->_entityAdapter->setParameters($this->getData());
|
|
}
|
|
return $this->_entityAdapter;
|
|
}
|
|
|
|
/**
|
|
* Returns source adapter object.
|
|
*
|
|
* @Deprecated
|
|
* @see \Magento\ImportExport\Model\Import\Source\Factory::create()
|
|
* @param string $sourceFile Full path to source file
|
|
* @return AbstractSource
|
|
* @throws FileSystemException
|
|
*/
|
|
protected function _getSourceAdapter($sourceFile)
|
|
{
|
|
return Adapter::findAdapterFor(
|
|
$sourceFile,
|
|
$this->_filesystem->getDirectoryWrite(DirectoryList::ROOT),
|
|
$this->getData(self::FIELD_FIELD_SEPARATOR)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Return operation result messages
|
|
*
|
|
* @param ProcessingErrorAggregatorInterface $validationResult
|
|
* @return string[]
|
|
* @throws LocalizedException
|
|
*/
|
|
public function getOperationResultMessages(ProcessingErrorAggregatorInterface $validationResult)
|
|
{
|
|
$messages = [];
|
|
if ($this->getProcessedRowsCount()) {
|
|
if ($validationResult->isErrorLimitExceeded()) {
|
|
$messages[] = __('Data validation failed. Please fix the following errors and upload the file again.');
|
|
|
|
// errors info
|
|
foreach ($validationResult->getRowsGroupedByErrorCode() as $errorMessage => $rows) {
|
|
$error = $errorMessage . ' ' . __('in row(s)') . ': ' . implode(', ', $rows);
|
|
$messages[] = $error;
|
|
}
|
|
} else {
|
|
if ($this->isImportAllowed()) {
|
|
$messages[] = __('The validation is complete.');
|
|
} else {
|
|
$messages[] = __('The file is valid, but we can\'t import it for some reason.');
|
|
}
|
|
}
|
|
|
|
$messages[] = __(
|
|
'Checked rows: %1, checked entities: %2, invalid rows: %3, total errors: %4',
|
|
$this->getProcessedRowsCount(),
|
|
$this->getProcessedEntitiesCount(),
|
|
$validationResult->getInvalidRowsCount(),
|
|
$validationResult->getErrorsCount(
|
|
[
|
|
ProcessingError::ERROR_LEVEL_CRITICAL,
|
|
ProcessingError::ERROR_LEVEL_NOT_CRITICAL
|
|
]
|
|
)
|
|
);
|
|
} else {
|
|
$messages[] = __('This file does not contain any data.');
|
|
}
|
|
return $messages;
|
|
}
|
|
|
|
/**
|
|
* Get attribute type for upcoming validation.
|
|
*
|
|
* @param AbstractAttribute|Attribute $attribute
|
|
* @return string
|
|
* phpcs:disable Magento2.Functions.StaticFunction
|
|
*/
|
|
public static function getAttributeType(AbstractAttribute $attribute)
|
|
{
|
|
$frontendInput = $attribute->getFrontendInput();
|
|
if ($attribute->usesSource() && in_array($frontendInput, ['select', 'multiselect', 'boolean'])) {
|
|
return $frontendInput;
|
|
} elseif ($attribute->isStatic()) {
|
|
return $frontendInput == 'date' ? 'datetime' : 'varchar';
|
|
} else {
|
|
return $attribute->getBackendType();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* DB data source model getter.
|
|
*
|
|
* @return Data
|
|
*/
|
|
public function getDataSourceModel()
|
|
{
|
|
return $this->_importData;
|
|
}
|
|
|
|
/**
|
|
* Default import behavior getter.
|
|
*
|
|
* @static
|
|
* @return string
|
|
*/
|
|
public static function getDefaultBehavior()
|
|
{
|
|
return self::BEHAVIOR_APPEND;
|
|
}
|
|
|
|
/**
|
|
* Override standard entity getter.
|
|
*
|
|
* @throws LocalizedException
|
|
* @return string
|
|
*/
|
|
public function getEntity()
|
|
{
|
|
$entities = $this->_importConfig->getEntities();
|
|
|
|
if (empty($this->_data['entity'])
|
|
|| !empty($this->_data['entity']) && !isset($entities[$this->_data['entity']])
|
|
) {
|
|
throw new LocalizedException(__('Entity is unknown'));
|
|
}
|
|
|
|
return $this->_data['entity'];
|
|
}
|
|
|
|
/**
|
|
* Returns number of checked entities.
|
|
*
|
|
* @return int
|
|
* @throws LocalizedException
|
|
*/
|
|
public function getProcessedEntitiesCount()
|
|
{
|
|
return $this->_getEntityAdapter()->getProcessedEntitiesCount();
|
|
}
|
|
|
|
/**
|
|
* Returns number of checked rows.
|
|
*
|
|
* @return int
|
|
* @throws LocalizedException
|
|
*/
|
|
public function getProcessedRowsCount()
|
|
{
|
|
return $this->_getEntityAdapter()->getProcessedRowsCount();
|
|
}
|
|
|
|
/**
|
|
* Import/Export working directory (source files, result files, lock files etc.).
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getWorkingDir()
|
|
{
|
|
return $this->_varDirectory->getAbsolutePath('importexport/');
|
|
}
|
|
|
|
/**
|
|
* Import source file structure to DB.
|
|
*
|
|
* @return bool
|
|
* @throws LocalizedException
|
|
*/
|
|
public function importSource()
|
|
{
|
|
return $this->localeEmulator->emulate(
|
|
$this->importSourceCallback(...),
|
|
$this->getData('locale') ?: null
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Import source file structure to DB.
|
|
*
|
|
* @return bool
|
|
* @throws LocalizedException
|
|
*/
|
|
private function importSourceCallback()
|
|
{
|
|
$ids = $this->getImportIds();
|
|
$this->_getEntityAdapter()->setIds($ids);
|
|
$this->setData('entity', $this->getDataSourceModel()->getEntityTypeCode($ids));
|
|
$this->setData('behavior', $this->getDataSourceModel()->getBehavior($ids));
|
|
|
|
//Validating images temporary directory path if the constraint has been provided
|
|
if ($this->hasData('images_base_directory')
|
|
&& $this->getData('images_base_directory') instanceof Filesystem\Directory\ReadInterface
|
|
) {
|
|
/** @var Filesystem\Directory\ReadInterface $imagesDirectory */
|
|
$imagesDirectory = $this->getData('images_base_directory');
|
|
if (!$imagesDirectory->isReadable()) {
|
|
$rootWrite = $this->_filesystem->getDirectoryWrite(DirectoryList::ROOT);
|
|
$rootWrite->create($imagesDirectory->getAbsolutePath());
|
|
}
|
|
try {
|
|
$this->setData(
|
|
self::FIELD_NAME_IMG_FILE_DIR,
|
|
$imagesDirectory->getAbsolutePath($this->getData(self::FIELD_NAME_IMG_FILE_DIR))
|
|
);
|
|
$this->_getEntityAdapter()->setParameters($this->getData());
|
|
} catch (ValidatorException $exception) {
|
|
throw new LocalizedException(__('Images file directory is outside required directory'), $exception);
|
|
}
|
|
}
|
|
|
|
$this->importHistoryModel->updateReport($this);
|
|
$this->addLogComment(__('Begin import of "%1" with "%2" behavior', $this->getEntity(), $this->getBehavior()));
|
|
|
|
$result = $this->processImport();
|
|
$this->getDataSourceModel()->markProcessedBunches($ids);
|
|
|
|
if ($result) {
|
|
$logComments = [
|
|
__(
|
|
'Checked rows: %1, checked entities: %2, invalid rows: %3, total errors: %4',
|
|
$this->getProcessedRowsCount(),
|
|
$this->getProcessedEntitiesCount(),
|
|
$this->getErrorAggregator()->getInvalidRowsCount(),
|
|
$this->getErrorAggregator()->getErrorsCount()
|
|
)
|
|
];
|
|
foreach ($this->getErrorAggregator()->getAllErrors() as $error) {
|
|
$logComments[] = $error->getErrorMessage();
|
|
}
|
|
$logComments[] = $this->getForceImport() == '0' && $this->getErrorAggregator()->getErrorsCount() > 0 ?
|
|
__('The import was not successful.') : __('The import was successful.');
|
|
$this->addLogComment($logComments);
|
|
$this->importHistoryModel->updateReport($this, true);
|
|
} else {
|
|
$this->importHistoryModel->invalidateReport($this);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Get entity import ids
|
|
*
|
|
* @return array
|
|
* @throws LocalizedException
|
|
*/
|
|
private function getImportIds(): array
|
|
{
|
|
$ids = $this->_getEntityAdapter()->getIds();
|
|
if (empty($ids)) {
|
|
$idsFromPostData = $this->getData(self::FIELD_IMPORT_IDS);
|
|
if (null !== $idsFromPostData && '' !== $idsFromPostData) {
|
|
$ids = explode(",", $idsFromPostData);
|
|
}
|
|
}
|
|
|
|
return $ids;
|
|
}
|
|
|
|
/**
|
|
* Process import.
|
|
*
|
|
* @return bool
|
|
* @throws LocalizedException
|
|
*/
|
|
protected function processImport()
|
|
{
|
|
return $this->_getEntityAdapter()->importData();
|
|
}
|
|
|
|
/**
|
|
* Import possibility getter.
|
|
*
|
|
* @return bool
|
|
* @throws LocalizedException
|
|
*/
|
|
public function isImportAllowed()
|
|
{
|
|
return $this->_getEntityAdapter()->isImportAllowed();
|
|
}
|
|
|
|
/**
|
|
* Get error aggregator instance.
|
|
*
|
|
* @return ProcessingErrorAggregatorInterface
|
|
* @throws LocalizedException
|
|
*/
|
|
public function getErrorAggregator()
|
|
{
|
|
return $this->_getEntityAdapter()->getErrorAggregator();
|
|
}
|
|
|
|
/**
|
|
* Move uploaded file.
|
|
*
|
|
* @throws LocalizedException
|
|
* @return string Source file path
|
|
*/
|
|
public function uploadSource()
|
|
{
|
|
$entity = $this->getEntity();
|
|
$result = $this->upload->uploadSource($entity);
|
|
// phpcs:ignore Magento2.Functions.DiscouragedFunction
|
|
$extension = pathinfo($result['file'], PATHINFO_EXTENSION);
|
|
$sourceFile = $this->getWorkingDir() . $entity . '.' . $extension;
|
|
$sourceFileRelative = $this->_varDirectory->getRelativePath($sourceFile);
|
|
$this->_removeBom($sourceFile);
|
|
$this->createHistoryReport($sourceFileRelative, $entity, $extension, $result);
|
|
return $sourceFile;
|
|
}
|
|
|
|
/**
|
|
* Move uploaded file and provide source instance.
|
|
*
|
|
* @return Import\AbstractSource
|
|
* @throws LocalizedException
|
|
* @since 100.2.7
|
|
*/
|
|
public function uploadFileAndGetSource()
|
|
{
|
|
$sourceFile = $this->uploadSource();
|
|
try {
|
|
$source = $this->_getSourceAdapter($sourceFile);
|
|
} catch (\Exception $e) {
|
|
$this->_varDirectory->delete($this->_varDirectory->getRelativePath($sourceFile));
|
|
throw new LocalizedException(__($e->getMessage()));
|
|
}
|
|
|
|
return $source;
|
|
}
|
|
|
|
/**
|
|
* Remove BOM from a file
|
|
*
|
|
* @param string $sourceFile
|
|
* @return $this
|
|
* @throws FileSystemException
|
|
*/
|
|
protected function _removeBom($sourceFile)
|
|
{
|
|
$driver = $this->_varDirectory->getDriver();
|
|
$string = $driver->fileGetContents($this->_varDirectory->getAbsolutePath($sourceFile));
|
|
if ($string !== false && substr($string, 0, 3) == pack("CCC", 0xef, 0xbb, 0xbf)) {
|
|
$string = substr($string, 3);
|
|
$driver->filePutContents($this->_varDirectory->getAbsolutePath($sourceFile), $string);
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Validates source file and returns validation result
|
|
*
|
|
* @param AbstractSource $source
|
|
* @return bool
|
|
* @throws LocalizedException
|
|
*/
|
|
public function validateSource(AbstractSource $source)
|
|
{
|
|
return $this->localeEmulator->emulate(
|
|
fn () => $this->validateSourceCallback($source),
|
|
$this->getData('locale') ?: null
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Validates source file and returns validation result
|
|
*
|
|
* Before validate data the method requires to initialize error aggregator (ProcessingErrorAggregatorInterface)
|
|
* with 'validation strategy' and 'allowed error count' values to allow using this parameters in validation process.
|
|
*
|
|
* @param AbstractSource $source
|
|
* @return bool
|
|
* @throws LocalizedException
|
|
*/
|
|
private function validateSourceCallback(AbstractSource $source)
|
|
{
|
|
$this->addLogComment(__('Begin data validation'));
|
|
|
|
$errorAggregator = $this->getErrorAggregator();
|
|
$errorAggregator->initValidationStrategy(
|
|
$this->getData(self::FIELD_NAME_VALIDATION_STRATEGY),
|
|
$this->getData(self::FIELD_NAME_ALLOWED_ERROR_COUNT)
|
|
);
|
|
|
|
try {
|
|
$adapter = $this->_getEntityAdapter()->setSource($source);
|
|
$adapter->validateData();
|
|
} catch (\Exception $e) {
|
|
$errorAggregator->addError(
|
|
AbstractEntity::ERROR_CODE_SYSTEM_EXCEPTION,
|
|
ProcessingError::ERROR_LEVEL_CRITICAL,
|
|
null,
|
|
null,
|
|
$e->getMessage()
|
|
);
|
|
}
|
|
|
|
$messages = $this->getOperationResultMessages($errorAggregator);
|
|
$this->addLogComment($messages);
|
|
|
|
if ($errorAggregator->isErrorLimitExceeded()) {
|
|
return false;
|
|
}
|
|
|
|
if ($this->getProcessedRowsCount() <= $errorAggregator->getInvalidRowsCount()) {
|
|
$this->addLogComment(__('There are no valid rows to import.'));
|
|
return false;
|
|
}
|
|
|
|
$this->addLogComment(__('Import data validation is complete.'));
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Invalidate indexes by process codes.
|
|
*
|
|
* @return $this
|
|
* @throws LocalizedException
|
|
*/
|
|
public function invalidateIndex()
|
|
{
|
|
$relatedIndexers = $this->_importConfig->getRelatedIndexers($this->getEntity());
|
|
if (empty($relatedIndexers)) {
|
|
return $this;
|
|
}
|
|
|
|
foreach (array_keys($relatedIndexers) as $indexerId) {
|
|
try {
|
|
$indexer = $this->indexerRegistry->get($indexerId);
|
|
|
|
if (!$indexer->isScheduled()) {
|
|
$indexer->invalidate();
|
|
}
|
|
// phpcs:disable Magento2.CodeAnalysis.EmptyBlock.DetectedCatch
|
|
} catch (\InvalidArgumentException $e) {
|
|
}
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Gets array of entities and appropriate behaviours
|
|
* array(
|
|
* <entity_code> => array(
|
|
* 'token' => <behavior_class_name>,
|
|
* 'code' => <behavior_model_code>,
|
|
* ),
|
|
* ...
|
|
* )
|
|
*
|
|
* @return array
|
|
* @throws LocalizedException
|
|
*/
|
|
public function getEntityBehaviors()
|
|
{
|
|
$behaviourData = [];
|
|
$entities = $this->_importConfig->getEntities();
|
|
foreach ($entities as $entityCode => $entityData) {
|
|
$behaviorClassName = isset($entityData['behaviorModel']) ? $entityData['behaviorModel'] : null;
|
|
if ($behaviorClassName && class_exists($behaviorClassName)) {
|
|
/** @var $behavior AbstractBehavior */
|
|
$behavior = $this->_behaviorFactory->create($behaviorClassName);
|
|
$behaviourData[$entityCode] = [
|
|
'token' => $behaviorClassName,
|
|
'code' => $behavior->getCode() . '_behavior',
|
|
'notes' => $behavior->getNotes($entityCode),
|
|
];
|
|
} else {
|
|
throw new LocalizedException(
|
|
__('The behavior token for %1 is invalid.', $entityCode)
|
|
);
|
|
}
|
|
}
|
|
return $behaviourData;
|
|
}
|
|
|
|
/**
|
|
* Get array of unique entity behaviors
|
|
* array(
|
|
* <behavior_model_code> => <behavior_class_name>,
|
|
* ...
|
|
* )
|
|
*
|
|
* @return array
|
|
* @throws LocalizedException
|
|
*/
|
|
public function getUniqueEntityBehaviors()
|
|
{
|
|
$uniqueBehaviors = [];
|
|
$behaviourData = $this->getEntityBehaviors();
|
|
foreach ($behaviourData as $behavior) {
|
|
$behaviorCode = $behavior['code'];
|
|
if (!isset($uniqueBehaviors[$behaviorCode])) {
|
|
$uniqueBehaviors[$behaviorCode] = $behavior['token'];
|
|
}
|
|
}
|
|
return $uniqueBehaviors;
|
|
}
|
|
|
|
/**
|
|
* Retrieve processed reports entity types
|
|
*
|
|
* @param string|null $entity
|
|
* @return bool
|
|
* @throws LocalizedException
|
|
*/
|
|
public function isReportEntityType($entity = null)
|
|
{
|
|
$result = false;
|
|
if (!$entity) {
|
|
$entity = $this->getEntity();
|
|
}
|
|
if ($entity !== null && $this->_getEntityAdapter()->getEntityTypeCode() != $entity) {
|
|
$entities = $this->_importConfig->getEntities();
|
|
if (isset($entities[$entity])) {
|
|
try {
|
|
$result = $this->_getEntityAdapter()->isNeedToLogInHistory();
|
|
} catch (\Exception $e) {
|
|
throw new LocalizedException(
|
|
__('Please enter a correct entity model')
|
|
);
|
|
}
|
|
} else {
|
|
throw new LocalizedException(__('Please enter a correct entity model'));
|
|
}
|
|
} else {
|
|
$result = $this->_getEntityAdapter()->isNeedToLogInHistory();
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Create history report
|
|
*
|
|
* @param string $sourceFileRelative
|
|
* @param string $entity
|
|
* @param string $extension
|
|
* @param array $result
|
|
* @return $this
|
|
* @throws LocalizedException
|
|
*/
|
|
protected function createHistoryReport($sourceFileRelative, $entity, $extension = null, $result = null)
|
|
{
|
|
if ($this->isReportEntityType($entity)) {
|
|
if (is_array($sourceFileRelative)) {
|
|
$fileName = $sourceFileRelative['file_name'];
|
|
$sourceFileRelative = $this->_varDirectory->getRelativePath(self::IMPORT_DIR . $fileName);
|
|
} elseif (isset($result['name'])) {
|
|
$fileName = $result['name'];
|
|
} elseif ($extension !== null) {
|
|
$fileName = $entity . $extension;
|
|
} else {
|
|
// phpcs:disable Magento2.Functions.DiscouragedFunction.Discouraged
|
|
$fileName = basename($sourceFileRelative);
|
|
}
|
|
$copyName = $this->localeDate->gmtTimestamp() . '_' . $fileName;
|
|
$copyFile = self::IMPORT_HISTORY_DIR . $copyName;
|
|
try {
|
|
if ($this->_varDirectory->isExist($sourceFileRelative)) {
|
|
$this->_varDirectory->copyFile($sourceFileRelative, $copyFile);
|
|
} else {
|
|
$content = $this->_varDirectory->getDriver()->fileGetContents($sourceFileRelative);
|
|
$this->_varDirectory->writeFile($copyFile, $content);
|
|
}
|
|
} catch (FileSystemException $e) {
|
|
throw new LocalizedException(__('Source file coping failed'));
|
|
}
|
|
$this->importHistoryModel->addReport($copyName);
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get count of created items
|
|
*
|
|
* @return int
|
|
* @throws LocalizedException
|
|
*/
|
|
public function getCreatedItemsCount()
|
|
{
|
|
return $this->_getEntityAdapter()->getCreatedItemsCount();
|
|
}
|
|
|
|
/**
|
|
* Get count of updated items
|
|
*
|
|
* @return int
|
|
* @throws LocalizedException
|
|
*/
|
|
public function getUpdatedItemsCount()
|
|
{
|
|
return $this->_getEntityAdapter()->getUpdatedItemsCount();
|
|
}
|
|
|
|
/**
|
|
* Get count of deleted items
|
|
*
|
|
* @return int
|
|
* @throws LocalizedException
|
|
*/
|
|
public function getDeletedItemsCount()
|
|
{
|
|
return $this->_getEntityAdapter()->getDeletedItemsCount();
|
|
}
|
|
|
|
/**
|
|
* Retrieve Ids of Validated Rows
|
|
*
|
|
* @return int[]
|
|
*/
|
|
public function getValidatedIds() : array
|
|
{
|
|
return $this->_getEntityAdapter()->getIds() ?? [];
|
|
}
|
|
}
|