Find this useful? Enter your email to receive occasional updates for securing PHP code.
Signing you up...
Thank you for signing up!
PHP Decode
<?php /** * Copyright Magento, Inc. All rights reserved. * See COPYING.txt for license ..
Decoded Output download
<?php
/**
* Copyright Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\CatalogImportExport\Model\Import\Product\Type;
use Magento\Catalog\Model\ResourceModel\Eav\Attribute;
use Magento\CatalogImportExport\Model\Import\Product;
use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface;
use Magento\Eav\Model\Entity\Attribute\Source\Table;
use Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\CollectionFactory as AttributeOptionCollectionFactory;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\EntityManager\MetadataPool;
/**
* Import entity abstract product type model
*
* phpcs:disable Magento2.Classes.AbstractApi
* @api
*
* @SuppressWarnings(PHPMD.TooManyFields)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
* @since 100.0.2
*/
abstract class AbstractType
{
/**
* @var array
*/
public static $commonAttributesCache = [];
/**
* Maintain a list of invisible attributes
*
* @var array
* @since 100.2.5
*/
public static $invAttributesCache = [];
/**
* Attribute Code to Id cache
*
* @var array
*/
public static $attributeCodeToId = [];
/**
* Product type attribute sets and attributes parameters.
*
* Example: [attr_set_name_1] => array(
* [attr_code_1] => array(
* 'options' => array(),
* 'type' => 'text', 'price', 'textarea', 'select', etc.
* 'id' => ..
* ),
* ...
* ),
* ...
*
* @var array
*/
protected $_attributes = [];
/**
* Attributes' codes which will be allowed anyway, independently from its visibility property.
*
* @var string[]
*/
protected $_forcedAttributesCodes = [];
/**
* Attributes with index (not label) value.
*
* @var string[]
*/
protected $_indexValueAttributes = [];
/**
* Validation failure entity specific message template definitions
*
* @var array
*/
protected $_messageTemplates = [];
/**
* Validation failure general message template definitions
*
* @var array
*/
protected $_genericMessageTemplates = [
RowValidatorInterface::ERROR_INVALID_WEIGHT => 'Weight value is incorrect',
RowValidatorInterface::ERROR_INVALID_WEBSITE => 'Provided Website code doesn\'t exist'
];
/**
* Column names that holds values with particular meaning.
*
* @var string[]
*/
protected $_specialAttributes = [];
/**
* Custom entity type fields mapping.
*
* @var string[]
*/
protected $_customFieldsMapping = [];
/**
* Product entity object.
*
* @var Product
*/
protected $_entityModel;
/**
* Product type (simple, etc.).
*
* @var string
*/
protected $_type;
/**
* @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory
*/
protected $_attrSetColFac;
/**
* @var \Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection
*/
protected $_prodAttrColFac;
/**
* @var \Magento\Framework\App\ResourceConnection
*/
protected $_resource;
/**
* @var \Magento\Framework\DB\Adapter\AdapterInterface
*/
protected $connection;
/**
* Product metadata pool
*
* @var \Magento\Framework\EntityManager\MetadataPool
* @since 100.1.0
*/
protected $metadataPool;
/**
* @var string
*/
private $productEntityLinkField;
/**
* @var AttributeOptionCollectionFactory
*/
private $attributeOptionCollectionFactory;
/**
* AbstractType constructor
*
* @param \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory $attrSetColFac
* @param \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory $prodAttrColFac
* @param ResourceConnection $resource
* @param array $params
* @param MetadataPool|null $metadataPool
* @param AttributeOptionCollectionFactory|null $attributeOptionCollectionFactory
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function __construct(
\Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory $attrSetColFac,
\Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory $prodAttrColFac,
\Magento\Framework\App\ResourceConnection $resource,
array $params,
MetadataPool $metadataPool = null,
AttributeOptionCollectionFactory $attributeOptionCollectionFactory = null
) {
$this->_attrSetColFac = $attrSetColFac;
$this->_prodAttrColFac = $prodAttrColFac;
$this->_resource = $resource;
$this->connection = $resource->getConnection();
$this->metadataPool = $metadataPool ?: $this->getMetadataPool();
$this->attributeOptionCollectionFactory = $attributeOptionCollectionFactory
?: ObjectManager::getInstance()->get(AttributeOptionCollectionFactory::class);
if ($this->isSuitable()) {
if (!isset($params[0])
|| !isset($params[1])
|| !is_object($params[0])
|| !$params[0] instanceof Product
) {
throw new \Magento\Framework\Exception\LocalizedException(__('Please correct the parameters.'));
}
$this->_entityModel = $params[0];
$this->_type = $params[1];
$this->initMessageTemplates(
array_merge($this->_genericMessageTemplates, $this->_messageTemplates)
);
$this->_initAttributes();
}
}
/**
* Initialize template for error message.
*
* @param array $templateCollection
* @return $this
*/
protected function initMessageTemplates(array $templateCollection)
{
foreach ($templateCollection as $errorCode => $message) {
$this->_entityModel->addMessageTemplate($errorCode, $message);
}
return $this;
}
/**
* Add attribute parameters to appropriate attribute set.
*
* @param string $attrSetName Name of attribute set.
* @param array $attrParams Refined attribute parameters.
* @param mixed $attribute
* @return \Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
protected function _addAttributeParams($attrSetName, array $attrParams, $attribute)
{
if (!$attrParams['apply_to'] || in_array($this->_type, $attrParams['apply_to'])) {
$this->_attributes[$attrSetName][$attrParams['code']] = $attrParams;
}
return $this;
}
/**
* Retrieve product Attribute
*
* @param string $attributeCode
* @param string $attributeSet
* @return array
*/
public function retrieveAttribute($attributeCode, $attributeSet)
{
if (isset($this->_attributes[$attributeSet]) && isset($this->_attributes[$attributeSet][$attributeCode])) {
return $this->_attributes[$attributeSet][$attributeCode];
}
return [];
}
/**
* Return product attributes for its attribute set specified in row data.
*
* @param array|string $attrSetData Product row data or simply attribute set name.
* @return array
*/
protected function _getProductAttributes($attrSetData)
{
if (is_array($attrSetData)) {
return $this->_attributes[$attrSetData[Product::COL_ATTR_SET]];
} else {
return $this->_attributes[$attrSetData];
}
}
/**
* Initialize attributes parameters for all attributes' sets.
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @return $this
*/
protected function _initAttributes()
{
// temporary storage for attributes' parameters to avoid double querying inside the loop
$entityId = $this->_entityModel->getEntityTypeId();
$entityAttributes = $this->connection->fetchAll(
$this->connection->select()->from(
['attr' => $this->_resource->getTableName('eav_entity_attribute')],
['attr.attribute_id']
)->joinLeft(
['set' => $this->_resource->getTableName('eav_attribute_set')],
'set.attribute_set_id = attr.attribute_set_id',
['set.attribute_set_name']
)->where(
$this->connection->quoteInto('attr.entity_type_id IN (?)', $entityId)
)
);
$absentKeys = [];
foreach ($entityAttributes as $attributeRow) {
if (!isset(self::$commonAttributesCache[$attributeRow['attribute_id']])) {
if (!isset($absentKeys[$attributeRow['attribute_set_name']])) {
$absentKeys[$attributeRow['attribute_set_name']] = [];
}
$absentKeys[$attributeRow['attribute_set_name']][] = $attributeRow['attribute_id'];
}
}
$addedAttributes = [];
foreach ($absentKeys as $attributeIds) {
$unknownAttributeIds = array_diff(
$attributeIds,
array_keys(self::$commonAttributesCache),
self::$invAttributesCache
);
if ($unknownAttributeIds) {
$addedAttributes[] = $this->attachAttributesByOnlyId($unknownAttributeIds);
}
}
if ($this->_forcedAttributesCodes) {
$addedAttributes[] = $this->attachAttributesByForcedCodes();
}
$addedAttributes = array_merge(...$addedAttributes);
$attributesToLoadFromTable = [];
foreach ($addedAttributes as $addedAttribute) {
if (isset($addedAttribute['options_use_table'])) {
$attributesToLoadFromTable[] = $addedAttribute['id'];
unset(self::$commonAttributesCache[$addedAttribute['id']]['options_use_table']);
}
}
foreach (array_chunk($attributesToLoadFromTable, 1000) as $attributesToLoadFromTableChunk) {
$collection = $this->attributeOptionCollectionFactory->create();
$collection->setAttributeFilter(['in' => $attributesToLoadFromTableChunk]);
$collection->setStoreFilter(\Magento\Store\Model\Store::DEFAULT_STORE_ID);
foreach ($collection as $option) {
$attributeId = $option->getAttributeId();
$value = strtolower($option->getValue());
self::$commonAttributesCache[$attributeId]['options'][$value] = $option->getOptionId();
}
}
foreach ($entityAttributes as $attributeRow) {
if (isset(self::$commonAttributesCache[$attributeRow['attribute_id']])) {
$attribute = self::$commonAttributesCache[$attributeRow['attribute_id']];
$this->_addAttributeParams($attributeRow['attribute_set_name'], $attribute, $attribute);
}
}
foreach (array_keys($this->_attributes) as $setName) {
foreach ($this->_forcedAttributesCodes as $code) {
$attributeId = self::$attributeCodeToId[$code] ?? null;
if (null === $attributeId) {
continue;
}
if (isset($this->_attributes[$setName][$code])) {
continue;
}
$attribute = self::$commonAttributesCache[$attributeId] ?? null;
if (!$attribute) {
continue;
}
$this->_addAttributeParams($setName, $attribute, $attribute);
}
}
return $this;
}
/**
* Attach Attributes By Id and _forcedAttributesCodes
*
* @param string $attributeSetName
* @param array $attributeIds
* @return void
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
* @deprecated use attachAttributesByOnlyId and attachAttributesByForcedCodes
* @see attachAttributesByOnlyId() and attachAttributesByForcedCodes()
*/
protected function attachAttributesById($attributeSetName, $attributeIds)
{
foreach ($this->_prodAttrColFac->create()->addFieldToFilter(
['main_table.attribute_id', 'main_table.attribute_code'],
[['in' => $attributeIds], ['in' => $this->_forcedAttributesCodes]]
) as $attribute) {
$this->attachAttribute($attribute);
}
}
/**
* Attach Attributes By Id
*
* @param array $attributeIds
* @return array
*/
private function attachAttributesByOnlyId(array $attributeIds) : array
{
$addedAttributes = [];
foreach ($this->_prodAttrColFac->create()->addFieldToFilter(
['main_table.attribute_id'],
[['in' => $attributeIds]]
) as $attribute) {
$cachedAttribute = $this->attachAttribute($attribute);
if (null !== $cachedAttribute) {
$addedAttributes[] = $cachedAttribute;
}
}
return $addedAttributes;
}
/**
* Attach Attributes By _forcedAttributesCodes
*
* @return array
*/
private function attachAttributesByForcedCodes() : array
{
$addedAttributes = [];
foreach ($this->_prodAttrColFac->create()->addFieldToFilter(
['main_table.attribute_code'],
[['in' => $this->_forcedAttributesCodes]]
) as $attribute) {
$cachedAttribute = $this->attachAttribute($attribute);
if (null !== $cachedAttribute) {
$addedAttributes[] = $cachedAttribute;
}
}
return $addedAttributes;
}
/**
* Attach Attribute to self::$commonAttributesCache or self::$invAttributesCache
*
* @param Attribute $attribute
* @return array|null
*/
private function attachAttribute(Attribute $attribute)
{
$cachedAttribute = null;
$attributeCode = $attribute->getAttributeCode();
$attributeId = $attribute->getId();
if ($attribute->getIsVisible() || in_array($attributeCode, $this->_forcedAttributesCodes)) {
if (!isset(self::$commonAttributesCache[$attributeId])) {
$defaultValue = $attribute->getDefaultValue();
$cachedAttribute = [
'id' => $attributeId,
'code' => $attributeCode,
'is_global' => $attribute->getIsGlobal(),
'is_required' => $attribute->getIsRequired(),
'is_unique' => $attribute->getIsUnique(),
'frontend_label' => $attribute->getFrontendLabel(),
'is_static' => $attribute->isStatic(),
'apply_to' => $attribute->getApplyTo(),
'type' => \Magento\ImportExport\Model\Import::getAttributeType($attribute),
'default_value' => (is_string($defaultValue) && strlen($defaultValue)) ?
$attribute->getDefaultValue() : null,
'options' => [],
];
$sourceModel = $attribute->getSourceModel();
if (Table::class === $sourceModel) {
$cachedAttribute['options_use_table'] = true;
} else {
$cachedAttribute['options'] = $this->_entityModel->getAttributeOptions(
$attribute,
$this->_indexValueAttributes
);
}
self::$commonAttributesCache[$attributeId] = $cachedAttribute;
self::$attributeCodeToId[$attributeCode] = $attributeId;
}
} else {
self::$invAttributesCache[] = $attributeId;
}
return $cachedAttribute;
}
/**
* Retrieve attribute from cache
*
* @param string $attributeCode
* @return mixed
*/
public function retrieveAttributeFromCache($attributeCode)
{
if (isset(self::$attributeCodeToId[$attributeCode]) && $id = self::$attributeCodeToId[$attributeCode]) {
if (isset(self::$commonAttributesCache[$id])) {
return self::$commonAttributesCache[$id];
}
}
return [];
}
/**
* Adding attribute option.
*
* In case we've dynamically added new attribute option during import we need to add it to our cache
* in order to keep it up to date.
*
* @param string $code
* @param string $optionKey
* @param string $optionValue
*
* @return $this
*/
public function addAttributeOption($code, $optionKey, $optionValue)
{
foreach ($this->_attributes as $attrSetName => $attrSetValue) {
if (isset($attrSetValue[$code])) {
$this->_attributes[$attrSetName][$code]['options'][$optionKey] = $optionValue;
}
}
return $this;
}
/**
* Have we check attribute for is_required? Used as last chance to disable this type of check.
*
* @param string $attrCode
* @return bool
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
protected function _isAttributeRequiredCheckNeeded($attrCode)
{
return true;
}
/**
* Validate particular attributes columns.
*
* @param array $rowData
* @param int $rowNum
* @return bool
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
protected function _isParticularAttributesValid(array $rowData, $rowNum)
{
return true;
}
/**
* Check price correction value validity (signed integer or float with or without percentage sign).
*
* @param string $value
* @return int
*/
protected function _isPriceCorr($value)
{
return preg_match('/^-?\d+\.?\d*%?$/', $value);
}
/**
* Particular attribute names getter.
*
* @return string[]
*/
public function getParticularAttributes()
{
return $this->_specialAttributes;
}
/**
* Return entity custom Fields mapping.
*
* @return string[]
*/
public function getCustomFieldsMapping()
{
return $this->_customFieldsMapping;
}
/**
* Validate row attributes. Pass VALID row data ONLY as argument.
*
* @param array $rowData
* @param int $rowNum
* @param bool $isNewProduct Optional
* @return bool
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function isRowValid(array $rowData, $rowNum, $isNewProduct = true)
{
$error = false;
$rowScope = $this->_entityModel->getRowScope($rowData);
if (Product::SCOPE_NULL != $rowScope && !empty($rowData[Product::COL_SKU])) {
foreach ($this->_getProductAttributes($rowData) as $attrCode => $attrParams) {
// check value for non-empty in the case of required attribute?
if (isset($rowData[$attrCode]) && (!is_array($rowData[$attrCode]) && strlen($rowData[$attrCode]) > 0
|| is_array($rowData[$attrCode]) && !empty($rowData[$attrCode]))) {
$error |= !$this->_entityModel->isAttributeValid($attrCode, $attrParams, $rowData, $rowNum);
} elseif ($this->_isAttributeRequiredCheckNeeded($attrCode) && $attrParams['is_required']) {
// For the default scope - if this is a new product or
// for an old product, if the imported doc has the column present for the attrCode
if (Product::SCOPE_DEFAULT == $rowScope &&
($isNewProduct || array_key_exists($attrCode, $rowData))) {
$this->_entityModel->addRowError(
RowValidatorInterface::ERROR_VALUE_IS_REQUIRED,
$rowNum,
$attrCode
);
$error = true;
}
}
}
}
$error |= !$this->_isParticularAttributesValid($rowData, $rowNum);
return !$error;
}
/**
* Additional check for model availability. If method returns FALSE - model is not suitable for data processing.
*
* @return bool
*/
public function isSuitable()
{
return true;
}
/**
* Adding default attribute to product before save.
*
* Prepare attributes values for save: exclude non-existent, static or with empty values attributes,
* set default values if needed.
*
* @param array $rowData
* @param bool $withDefaultValue
*
* @return array
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function prepareAttributesWithDefaultValueForSave(array $rowData, $withDefaultValue = true)
{
$resultAttrs = [];
foreach ($this->_getProductAttributes($rowData) as $attrCode => $attrParams) {
if ($attrParams['is_static']) {
continue;
}
$attrCode = mb_strtolower($attrCode);
if (isset($rowData[$attrCode]) && ((is_array($rowData[$attrCode]) && !empty($rowData[$attrCode]))
|| (!is_array($rowData[$attrCode]) && strlen(trim($rowData[$attrCode]))))) {
if (in_array($attrParams['type'], ['select', 'boolean'])) {
$resultAttrs[$attrCode] = $attrParams['options'][strtolower($rowData[$attrCode])];
} elseif ('multiselect' == $attrParams['type']) {
$resultAttrs[$attrCode] = [];
foreach ($this->_entityModel->parseMultiselectValues($rowData[$attrCode]) as $value) {
$resultAttrs[$attrCode][] = $attrParams['options'][strtolower($value)];
}
$resultAttrs[$attrCode] = implode(',', $resultAttrs[$attrCode]);
} else {
$resultAttrs[$attrCode] = $rowData[$attrCode];
}
} elseif (array_key_exists($attrCode, $rowData)) {
$resultAttrs[$attrCode] = $rowData[$attrCode];
} elseif ($withDefaultValue && null !== $attrParams['default_value'] && empty($rowData['_store'])) {
$resultAttrs[$attrCode] = $attrParams['default_value'];
}
}
return $resultAttrs;
}
/**
* Clear empty columns in the Row Data
*
* @param array $rowData
* @return array
*/
public function clearEmptyData(array $rowData)
{
foreach ($this->_getProductAttributes($rowData) as $attrCode => $attrParams) {
if (!$attrParams['is_static'] && !isset($rowData[$attrCode])) {
unset($rowData[$attrCode]);
}
if (isset($rowData[$attrCode])
&& $rowData[$attrCode] === $this->_entityModel->getEmptyAttributeValueConstant()
) {
$rowData[$attrCode] = null;
}
}
return $rowData;
}
/**
* Save product type specific data.
*
* @return $this
*/
public function saveData()
{
return $this;
}
/**
* Get product metadata pool
*
* @return \Magento\Framework\EntityManager\MetadataPool
* @since 100.1.0
*/
protected function getMetadataPool()
{
if (!$this->metadataPool) {
// phpcs:ignore Magento2.PHP.AutogeneratedClassNotInConstructor
$this->metadataPool = \Magento\Framework\App\ObjectManager::getInstance()
->get(\Magento\Framework\EntityManager\MetadataPool::class);
}
return $this->metadataPool;
}
/**
* Get product entity link field
*
* @return string
* @since 100.1.0
*/
protected function getProductEntityLinkField()
{
if (!$this->productEntityLinkField) {
$this->productEntityLinkField = $this->metadataPool
->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class)
->getLinkField();
}
return $this->productEntityLinkField;
}
/**
* Clean cached values.
*
* @since 100.2.0
*/
public function __destruct()
{
self::$attributeCodeToId = [];
self::$commonAttributesCache = [];
}
}
?>
Did this file decode correctly?
Original Code
<?php
/**
* Copyright Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\CatalogImportExport\Model\Import\Product\Type;
use Magento\Catalog\Model\ResourceModel\Eav\Attribute;
use Magento\CatalogImportExport\Model\Import\Product;
use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface;
use Magento\Eav\Model\Entity\Attribute\Source\Table;
use Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\CollectionFactory as AttributeOptionCollectionFactory;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\EntityManager\MetadataPool;
/**
* Import entity abstract product type model
*
* phpcs:disable Magento2.Classes.AbstractApi
* @api
*
* @SuppressWarnings(PHPMD.TooManyFields)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
* @since 100.0.2
*/
abstract class AbstractType
{
/**
* @var array
*/
public static $commonAttributesCache = [];
/**
* Maintain a list of invisible attributes
*
* @var array
* @since 100.2.5
*/
public static $invAttributesCache = [];
/**
* Attribute Code to Id cache
*
* @var array
*/
public static $attributeCodeToId = [];
/**
* Product type attribute sets and attributes parameters.
*
* Example: [attr_set_name_1] => array(
* [attr_code_1] => array(
* 'options' => array(),
* 'type' => 'text', 'price', 'textarea', 'select', etc.
* 'id' => ..
* ),
* ...
* ),
* ...
*
* @var array
*/
protected $_attributes = [];
/**
* Attributes' codes which will be allowed anyway, independently from its visibility property.
*
* @var string[]
*/
protected $_forcedAttributesCodes = [];
/**
* Attributes with index (not label) value.
*
* @var string[]
*/
protected $_indexValueAttributes = [];
/**
* Validation failure entity specific message template definitions
*
* @var array
*/
protected $_messageTemplates = [];
/**
* Validation failure general message template definitions
*
* @var array
*/
protected $_genericMessageTemplates = [
RowValidatorInterface::ERROR_INVALID_WEIGHT => 'Weight value is incorrect',
RowValidatorInterface::ERROR_INVALID_WEBSITE => 'Provided Website code doesn\'t exist'
];
/**
* Column names that holds values with particular meaning.
*
* @var string[]
*/
protected $_specialAttributes = [];
/**
* Custom entity type fields mapping.
*
* @var string[]
*/
protected $_customFieldsMapping = [];
/**
* Product entity object.
*
* @var Product
*/
protected $_entityModel;
/**
* Product type (simple, etc.).
*
* @var string
*/
protected $_type;
/**
* @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory
*/
protected $_attrSetColFac;
/**
* @var \Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection
*/
protected $_prodAttrColFac;
/**
* @var \Magento\Framework\App\ResourceConnection
*/
protected $_resource;
/**
* @var \Magento\Framework\DB\Adapter\AdapterInterface
*/
protected $connection;
/**
* Product metadata pool
*
* @var \Magento\Framework\EntityManager\MetadataPool
* @since 100.1.0
*/
protected $metadataPool;
/**
* @var string
*/
private $productEntityLinkField;
/**
* @var AttributeOptionCollectionFactory
*/
private $attributeOptionCollectionFactory;
/**
* AbstractType constructor
*
* @param \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory $attrSetColFac
* @param \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory $prodAttrColFac
* @param ResourceConnection $resource
* @param array $params
* @param MetadataPool|null $metadataPool
* @param AttributeOptionCollectionFactory|null $attributeOptionCollectionFactory
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function __construct(
\Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory $attrSetColFac,
\Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory $prodAttrColFac,
\Magento\Framework\App\ResourceConnection $resource,
array $params,
MetadataPool $metadataPool = null,
AttributeOptionCollectionFactory $attributeOptionCollectionFactory = null
) {
$this->_attrSetColFac = $attrSetColFac;
$this->_prodAttrColFac = $prodAttrColFac;
$this->_resource = $resource;
$this->connection = $resource->getConnection();
$this->metadataPool = $metadataPool ?: $this->getMetadataPool();
$this->attributeOptionCollectionFactory = $attributeOptionCollectionFactory
?: ObjectManager::getInstance()->get(AttributeOptionCollectionFactory::class);
if ($this->isSuitable()) {
if (!isset($params[0])
|| !isset($params[1])
|| !is_object($params[0])
|| !$params[0] instanceof Product
) {
throw new \Magento\Framework\Exception\LocalizedException(__('Please correct the parameters.'));
}
$this->_entityModel = $params[0];
$this->_type = $params[1];
$this->initMessageTemplates(
array_merge($this->_genericMessageTemplates, $this->_messageTemplates)
);
$this->_initAttributes();
}
}
/**
* Initialize template for error message.
*
* @param array $templateCollection
* @return $this
*/
protected function initMessageTemplates(array $templateCollection)
{
foreach ($templateCollection as $errorCode => $message) {
$this->_entityModel->addMessageTemplate($errorCode, $message);
}
return $this;
}
/**
* Add attribute parameters to appropriate attribute set.
*
* @param string $attrSetName Name of attribute set.
* @param array $attrParams Refined attribute parameters.
* @param mixed $attribute
* @return \Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
protected function _addAttributeParams($attrSetName, array $attrParams, $attribute)
{
if (!$attrParams['apply_to'] || in_array($this->_type, $attrParams['apply_to'])) {
$this->_attributes[$attrSetName][$attrParams['code']] = $attrParams;
}
return $this;
}
/**
* Retrieve product Attribute
*
* @param string $attributeCode
* @param string $attributeSet
* @return array
*/
public function retrieveAttribute($attributeCode, $attributeSet)
{
if (isset($this->_attributes[$attributeSet]) && isset($this->_attributes[$attributeSet][$attributeCode])) {
return $this->_attributes[$attributeSet][$attributeCode];
}
return [];
}
/**
* Return product attributes for its attribute set specified in row data.
*
* @param array|string $attrSetData Product row data or simply attribute set name.
* @return array
*/
protected function _getProductAttributes($attrSetData)
{
if (is_array($attrSetData)) {
return $this->_attributes[$attrSetData[Product::COL_ATTR_SET]];
} else {
return $this->_attributes[$attrSetData];
}
}
/**
* Initialize attributes parameters for all attributes' sets.
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @return $this
*/
protected function _initAttributes()
{
// temporary storage for attributes' parameters to avoid double querying inside the loop
$entityId = $this->_entityModel->getEntityTypeId();
$entityAttributes = $this->connection->fetchAll(
$this->connection->select()->from(
['attr' => $this->_resource->getTableName('eav_entity_attribute')],
['attr.attribute_id']
)->joinLeft(
['set' => $this->_resource->getTableName('eav_attribute_set')],
'set.attribute_set_id = attr.attribute_set_id',
['set.attribute_set_name']
)->where(
$this->connection->quoteInto('attr.entity_type_id IN (?)', $entityId)
)
);
$absentKeys = [];
foreach ($entityAttributes as $attributeRow) {
if (!isset(self::$commonAttributesCache[$attributeRow['attribute_id']])) {
if (!isset($absentKeys[$attributeRow['attribute_set_name']])) {
$absentKeys[$attributeRow['attribute_set_name']] = [];
}
$absentKeys[$attributeRow['attribute_set_name']][] = $attributeRow['attribute_id'];
}
}
$addedAttributes = [];
foreach ($absentKeys as $attributeIds) {
$unknownAttributeIds = array_diff(
$attributeIds,
array_keys(self::$commonAttributesCache),
self::$invAttributesCache
);
if ($unknownAttributeIds) {
$addedAttributes[] = $this->attachAttributesByOnlyId($unknownAttributeIds);
}
}
if ($this->_forcedAttributesCodes) {
$addedAttributes[] = $this->attachAttributesByForcedCodes();
}
$addedAttributes = array_merge(...$addedAttributes);
$attributesToLoadFromTable = [];
foreach ($addedAttributes as $addedAttribute) {
if (isset($addedAttribute['options_use_table'])) {
$attributesToLoadFromTable[] = $addedAttribute['id'];
unset(self::$commonAttributesCache[$addedAttribute['id']]['options_use_table']);
}
}
foreach (array_chunk($attributesToLoadFromTable, 1000) as $attributesToLoadFromTableChunk) {
$collection = $this->attributeOptionCollectionFactory->create();
$collection->setAttributeFilter(['in' => $attributesToLoadFromTableChunk]);
$collection->setStoreFilter(\Magento\Store\Model\Store::DEFAULT_STORE_ID);
foreach ($collection as $option) {
$attributeId = $option->getAttributeId();
$value = strtolower($option->getValue());
self::$commonAttributesCache[$attributeId]['options'][$value] = $option->getOptionId();
}
}
foreach ($entityAttributes as $attributeRow) {
if (isset(self::$commonAttributesCache[$attributeRow['attribute_id']])) {
$attribute = self::$commonAttributesCache[$attributeRow['attribute_id']];
$this->_addAttributeParams($attributeRow['attribute_set_name'], $attribute, $attribute);
}
}
foreach (array_keys($this->_attributes) as $setName) {
foreach ($this->_forcedAttributesCodes as $code) {
$attributeId = self::$attributeCodeToId[$code] ?? null;
if (null === $attributeId) {
continue;
}
if (isset($this->_attributes[$setName][$code])) {
continue;
}
$attribute = self::$commonAttributesCache[$attributeId] ?? null;
if (!$attribute) {
continue;
}
$this->_addAttributeParams($setName, $attribute, $attribute);
}
}
return $this;
}
/**
* Attach Attributes By Id and _forcedAttributesCodes
*
* @param string $attributeSetName
* @param array $attributeIds
* @return void
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
* @deprecated use attachAttributesByOnlyId and attachAttributesByForcedCodes
* @see attachAttributesByOnlyId() and attachAttributesByForcedCodes()
*/
protected function attachAttributesById($attributeSetName, $attributeIds)
{
foreach ($this->_prodAttrColFac->create()->addFieldToFilter(
['main_table.attribute_id', 'main_table.attribute_code'],
[['in' => $attributeIds], ['in' => $this->_forcedAttributesCodes]]
) as $attribute) {
$this->attachAttribute($attribute);
}
}
/**
* Attach Attributes By Id
*
* @param array $attributeIds
* @return array
*/
private function attachAttributesByOnlyId(array $attributeIds) : array
{
$addedAttributes = [];
foreach ($this->_prodAttrColFac->create()->addFieldToFilter(
['main_table.attribute_id'],
[['in' => $attributeIds]]
) as $attribute) {
$cachedAttribute = $this->attachAttribute($attribute);
if (null !== $cachedAttribute) {
$addedAttributes[] = $cachedAttribute;
}
}
return $addedAttributes;
}
/**
* Attach Attributes By _forcedAttributesCodes
*
* @return array
*/
private function attachAttributesByForcedCodes() : array
{
$addedAttributes = [];
foreach ($this->_prodAttrColFac->create()->addFieldToFilter(
['main_table.attribute_code'],
[['in' => $this->_forcedAttributesCodes]]
) as $attribute) {
$cachedAttribute = $this->attachAttribute($attribute);
if (null !== $cachedAttribute) {
$addedAttributes[] = $cachedAttribute;
}
}
return $addedAttributes;
}
/**
* Attach Attribute to self::$commonAttributesCache or self::$invAttributesCache
*
* @param Attribute $attribute
* @return array|null
*/
private function attachAttribute(Attribute $attribute)
{
$cachedAttribute = null;
$attributeCode = $attribute->getAttributeCode();
$attributeId = $attribute->getId();
if ($attribute->getIsVisible() || in_array($attributeCode, $this->_forcedAttributesCodes)) {
if (!isset(self::$commonAttributesCache[$attributeId])) {
$defaultValue = $attribute->getDefaultValue();
$cachedAttribute = [
'id' => $attributeId,
'code' => $attributeCode,
'is_global' => $attribute->getIsGlobal(),
'is_required' => $attribute->getIsRequired(),
'is_unique' => $attribute->getIsUnique(),
'frontend_label' => $attribute->getFrontendLabel(),
'is_static' => $attribute->isStatic(),
'apply_to' => $attribute->getApplyTo(),
'type' => \Magento\ImportExport\Model\Import::getAttributeType($attribute),
'default_value' => (is_string($defaultValue) && strlen($defaultValue)) ?
$attribute->getDefaultValue() : null,
'options' => [],
];
$sourceModel = $attribute->getSourceModel();
if (Table::class === $sourceModel) {
$cachedAttribute['options_use_table'] = true;
} else {
$cachedAttribute['options'] = $this->_entityModel->getAttributeOptions(
$attribute,
$this->_indexValueAttributes
);
}
self::$commonAttributesCache[$attributeId] = $cachedAttribute;
self::$attributeCodeToId[$attributeCode] = $attributeId;
}
} else {
self::$invAttributesCache[] = $attributeId;
}
return $cachedAttribute;
}
/**
* Retrieve attribute from cache
*
* @param string $attributeCode
* @return mixed
*/
public function retrieveAttributeFromCache($attributeCode)
{
if (isset(self::$attributeCodeToId[$attributeCode]) && $id = self::$attributeCodeToId[$attributeCode]) {
if (isset(self::$commonAttributesCache[$id])) {
return self::$commonAttributesCache[$id];
}
}
return [];
}
/**
* Adding attribute option.
*
* In case we've dynamically added new attribute option during import we need to add it to our cache
* in order to keep it up to date.
*
* @param string $code
* @param string $optionKey
* @param string $optionValue
*
* @return $this
*/
public function addAttributeOption($code, $optionKey, $optionValue)
{
foreach ($this->_attributes as $attrSetName => $attrSetValue) {
if (isset($attrSetValue[$code])) {
$this->_attributes[$attrSetName][$code]['options'][$optionKey] = $optionValue;
}
}
return $this;
}
/**
* Have we check attribute for is_required? Used as last chance to disable this type of check.
*
* @param string $attrCode
* @return bool
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
protected function _isAttributeRequiredCheckNeeded($attrCode)
{
return true;
}
/**
* Validate particular attributes columns.
*
* @param array $rowData
* @param int $rowNum
* @return bool
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
protected function _isParticularAttributesValid(array $rowData, $rowNum)
{
return true;
}
/**
* Check price correction value validity (signed integer or float with or without percentage sign).
*
* @param string $value
* @return int
*/
protected function _isPriceCorr($value)
{
return preg_match('/^-?\d+\.?\d*%?$/', $value);
}
/**
* Particular attribute names getter.
*
* @return string[]
*/
public function getParticularAttributes()
{
return $this->_specialAttributes;
}
/**
* Return entity custom Fields mapping.
*
* @return string[]
*/
public function getCustomFieldsMapping()
{
return $this->_customFieldsMapping;
}
/**
* Validate row attributes. Pass VALID row data ONLY as argument.
*
* @param array $rowData
* @param int $rowNum
* @param bool $isNewProduct Optional
* @return bool
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function isRowValid(array $rowData, $rowNum, $isNewProduct = true)
{
$error = false;
$rowScope = $this->_entityModel->getRowScope($rowData);
if (Product::SCOPE_NULL != $rowScope && !empty($rowData[Product::COL_SKU])) {
foreach ($this->_getProductAttributes($rowData) as $attrCode => $attrParams) {
// check value for non-empty in the case of required attribute?
if (isset($rowData[$attrCode]) && (!is_array($rowData[$attrCode]) && strlen($rowData[$attrCode]) > 0
|| is_array($rowData[$attrCode]) && !empty($rowData[$attrCode]))) {
$error |= !$this->_entityModel->isAttributeValid($attrCode, $attrParams, $rowData, $rowNum);
} elseif ($this->_isAttributeRequiredCheckNeeded($attrCode) && $attrParams['is_required']) {
// For the default scope - if this is a new product or
// for an old product, if the imported doc has the column present for the attrCode
if (Product::SCOPE_DEFAULT == $rowScope &&
($isNewProduct || array_key_exists($attrCode, $rowData))) {
$this->_entityModel->addRowError(
RowValidatorInterface::ERROR_VALUE_IS_REQUIRED,
$rowNum,
$attrCode
);
$error = true;
}
}
}
}
$error |= !$this->_isParticularAttributesValid($rowData, $rowNum);
return !$error;
}
/**
* Additional check for model availability. If method returns FALSE - model is not suitable for data processing.
*
* @return bool
*/
public function isSuitable()
{
return true;
}
/**
* Adding default attribute to product before save.
*
* Prepare attributes values for save: exclude non-existent, static or with empty values attributes,
* set default values if needed.
*
* @param array $rowData
* @param bool $withDefaultValue
*
* @return array
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function prepareAttributesWithDefaultValueForSave(array $rowData, $withDefaultValue = true)
{
$resultAttrs = [];
foreach ($this->_getProductAttributes($rowData) as $attrCode => $attrParams) {
if ($attrParams['is_static']) {
continue;
}
$attrCode = mb_strtolower($attrCode);
if (isset($rowData[$attrCode]) && ((is_array($rowData[$attrCode]) && !empty($rowData[$attrCode]))
|| (!is_array($rowData[$attrCode]) && strlen(trim($rowData[$attrCode]))))) {
if (in_array($attrParams['type'], ['select', 'boolean'])) {
$resultAttrs[$attrCode] = $attrParams['options'][strtolower($rowData[$attrCode])];
} elseif ('multiselect' == $attrParams['type']) {
$resultAttrs[$attrCode] = [];
foreach ($this->_entityModel->parseMultiselectValues($rowData[$attrCode]) as $value) {
$resultAttrs[$attrCode][] = $attrParams['options'][strtolower($value)];
}
$resultAttrs[$attrCode] = implode(',', $resultAttrs[$attrCode]);
} else {
$resultAttrs[$attrCode] = $rowData[$attrCode];
}
} elseif (array_key_exists($attrCode, $rowData)) {
$resultAttrs[$attrCode] = $rowData[$attrCode];
} elseif ($withDefaultValue && null !== $attrParams['default_value'] && empty($rowData['_store'])) {
$resultAttrs[$attrCode] = $attrParams['default_value'];
}
}
return $resultAttrs;
}
/**
* Clear empty columns in the Row Data
*
* @param array $rowData
* @return array
*/
public function clearEmptyData(array $rowData)
{
foreach ($this->_getProductAttributes($rowData) as $attrCode => $attrParams) {
if (!$attrParams['is_static'] && !isset($rowData[$attrCode])) {
unset($rowData[$attrCode]);
}
if (isset($rowData[$attrCode])
&& $rowData[$attrCode] === $this->_entityModel->getEmptyAttributeValueConstant()
) {
$rowData[$attrCode] = null;
}
}
return $rowData;
}
/**
* Save product type specific data.
*
* @return $this
*/
public function saveData()
{
return $this;
}
/**
* Get product metadata pool
*
* @return \Magento\Framework\EntityManager\MetadataPool
* @since 100.1.0
*/
protected function getMetadataPool()
{
if (!$this->metadataPool) {
// phpcs:ignore Magento2.PHP.AutogeneratedClassNotInConstructor
$this->metadataPool = \Magento\Framework\App\ObjectManager::getInstance()
->get(\Magento\Framework\EntityManager\MetadataPool::class);
}
return $this->metadataPool;
}
/**
* Get product entity link field
*
* @return string
* @since 100.1.0
*/
protected function getProductEntityLinkField()
{
if (!$this->productEntityLinkField) {
$this->productEntityLinkField = $this->metadataPool
->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class)
->getLinkField();
}
return $this->productEntityLinkField;
}
/**
* Clean cached values.
*
* @since 100.2.0
*/
public function __destruct()
{
self::$attributeCodeToId = [];
self::$commonAttributesCache = [];
}
}
Function Calls
| None |
Stats
| MD5 | c1975744be9bee2b86e29defca73612e |
| Eval Count | 0 |
| Decode Time | 102 ms |