![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/old/vendor/magento/module-catalog/Model/ |
<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Catalog\Model; use Magento\Catalog\Api\CategoryLinkManagementInterface; use Magento\Catalog\Api\Data\ProductAttributeInterface; use Magento\Catalog\Api\Data\ProductExtension; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Attribute\ScopeOverriddenValue; use Magento\Catalog\Model\Product\Gallery\MimeTypeExtensionMap; use Magento\Catalog\Model\ProductRepository\MediaGalleryProcessor; use Magento\Catalog\Model\ResourceModel\Product\Collection; use Magento\Eav\Model\Entity\Attribute\Exception as AttributeException; use Magento\Framework\Api\Data\ImageContentInterfaceFactory; use Magento\Framework\Api\ImageContentValidatorInterface; use Magento\Framework\Api\ImageProcessorInterface; use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; use Magento\Framework\Api\SearchCriteriaInterface; use Magento\Framework\DB\Adapter\ConnectionException; use Magento\Framework\DB\Adapter\DeadlockException; use Magento\Framework\DB\Adapter\LockWaitException; use Magento\Framework\EntityManager\Operation\Read\ReadExtensions; use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\StateException; use Magento\Framework\Exception\TemporaryState\CouldNotSaveException as TemporaryCouldNotSaveException; use Magento\Framework\Exception\ValidatorException; use Magento\Store\Model\Store; use Magento\Catalog\Api\Data\EavAttributeInterface; /** * @inheritdoc * * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) */ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterface { /** * @var \Magento\Catalog\Api\ProductCustomOptionRepositoryInterface */ protected $optionRepository; /** * @var ProductFactory */ protected $productFactory; /** * @var Product[] */ protected $instances = []; /** * @var Product[] */ protected $instancesById = []; /** * @var \Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper */ protected $initializationHelper; /** * @var \Magento\Catalog\Api\Data\ProductSearchResultsInterfaceFactory */ protected $searchResultsFactory; /** * @var \Magento\Framework\Api\SearchCriteriaBuilder */ protected $searchCriteriaBuilder; /** * @var \Magento\Framework\Api\FilterBuilder */ protected $filterBuilder; /** * @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory */ protected $collectionFactory; /** * @var \Magento\Catalog\Model\ResourceModel\Product */ protected $resourceModel; /** * @var Product\Initialization\Helper\ProductLinks */ protected $linkInitializer; /** * @var Product\LinkTypeProvider */ protected $linkTypeProvider; /** * @var \Magento\Store\Model\StoreManagerInterface */ protected $storeManager; /** * @var \Magento\Catalog\Api\ProductAttributeRepositoryInterface */ protected $attributeRepository; /** * @var \Magento\Catalog\Api\ProductAttributeRepositoryInterface */ protected $metadataService; /** * @var \Magento\Framework\Api\ExtensibleDataObjectConverter */ protected $extensibleDataObjectConverter; /** * @var \Magento\Framework\Filesystem */ protected $fileSystem; /** * @deprecated 103.0.2 * * @var ImageContentInterfaceFactory */ protected $contentFactory; /** * @deprecated 103.0.2 * * @var ImageProcessorInterface */ protected $imageProcessor; /** * @var \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface */ protected $extensionAttributesJoinProcessor; /** * @deprecated 103.0.2 * * @var \Magento\Catalog\Model\Product\Gallery\Processor */ protected $mediaGalleryProcessor; /** * @var MediaGalleryProcessor */ private $mediaProcessor; /** * @var CollectionProcessorInterface */ private $collectionProcessor; /** * @var int */ private $cacheLimit = 0; /** * @var \Magento\Framework\Serialize\Serializer\Json */ private $serializer; /** * @var ReadExtensions */ private $readExtensions; /** * @var CategoryLinkManagementInterface */ private $linkManagement; /** * @var ScopeOverriddenValue */ private $scopeOverriddenValue; /** * ProductRepository constructor. * @param ProductFactory $productFactory * @param \Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper $initializationHelper * @param \Magento\Catalog\Api\Data\ProductSearchResultsInterfaceFactory $searchResultsFactory * @param ResourceModel\Product\CollectionFactory $collectionFactory * @param \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder * @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository * @param ResourceModel\Product $resourceModel * @param Product\Initialization\Helper\ProductLinks $linkInitializer * @param Product\LinkTypeProvider $linkTypeProvider * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Framework\Api\FilterBuilder $filterBuilder * @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $metadataServiceInterface * @param \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter * @param Product\Option\Converter $optionConverter * @param \Magento\Framework\Filesystem $fileSystem * @param ImageContentValidatorInterface $contentValidator * @param ImageContentInterfaceFactory $contentFactory * @param MimeTypeExtensionMap $mimeTypeExtensionMap * @param ImageProcessorInterface $imageProcessor * @param \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $extensionAttributesJoinProcessor * @param CollectionProcessorInterface $collectionProcessor [optional] * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer * @param int $cacheLimit [optional] * @param ReadExtensions $readExtensions * @param CategoryLinkManagementInterface $linkManagement * @param ScopeOverriddenValue|null $scopeOverriddenValue * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( ProductFactory $productFactory, \Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper $initializationHelper, \Magento\Catalog\Api\Data\ProductSearchResultsInterfaceFactory $searchResultsFactory, \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $collectionFactory, \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder, \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository, \Magento\Catalog\Model\ResourceModel\Product $resourceModel, \Magento\Catalog\Model\Product\Initialization\Helper\ProductLinks $linkInitializer, \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Framework\Api\FilterBuilder $filterBuilder, \Magento\Catalog\Api\ProductAttributeRepositoryInterface $metadataServiceInterface, \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter, \Magento\Catalog\Model\Product\Option\Converter $optionConverter, \Magento\Framework\Filesystem $fileSystem, ImageContentValidatorInterface $contentValidator, ImageContentInterfaceFactory $contentFactory, MimeTypeExtensionMap $mimeTypeExtensionMap, ImageProcessorInterface $imageProcessor, \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $extensionAttributesJoinProcessor, CollectionProcessorInterface $collectionProcessor = null, \Magento\Framework\Serialize\Serializer\Json $serializer = null, $cacheLimit = 1000, ReadExtensions $readExtensions = null, CategoryLinkManagementInterface $linkManagement = null, ?ScopeOverriddenValue $scopeOverriddenValue = null ) { $this->productFactory = $productFactory; $this->collectionFactory = $collectionFactory; $this->initializationHelper = $initializationHelper; $this->searchResultsFactory = $searchResultsFactory; $this->searchCriteriaBuilder = $searchCriteriaBuilder; $this->resourceModel = $resourceModel; $this->linkInitializer = $linkInitializer; $this->linkTypeProvider = $linkTypeProvider; $this->storeManager = $storeManager; $this->attributeRepository = $attributeRepository; $this->filterBuilder = $filterBuilder; $this->metadataService = $metadataServiceInterface; $this->extensibleDataObjectConverter = $extensibleDataObjectConverter; $this->fileSystem = $fileSystem; $this->contentFactory = $contentFactory; $this->imageProcessor = $imageProcessor; $this->extensionAttributesJoinProcessor = $extensionAttributesJoinProcessor; $this->collectionProcessor = $collectionProcessor ?: $this->getCollectionProcessor(); $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance() ->get(\Magento\Framework\Serialize\Serializer\Json::class); $this->cacheLimit = (int)$cacheLimit; $this->readExtensions = $readExtensions ?: \Magento\Framework\App\ObjectManager::getInstance() ->get(ReadExtensions::class); $this->linkManagement = $linkManagement ?: \Magento\Framework\App\ObjectManager::getInstance() ->get(CategoryLinkManagementInterface::class); $this->scopeOverriddenValue = $scopeOverriddenValue ?: \Magento\Framework\App\ObjectManager::getInstance() ->get(ScopeOverriddenValue::class); } /** * @inheritdoc */ public function get($sku, $editMode = false, $storeId = null, $forceReload = false) { $cacheKey = $this->getCacheKey([$editMode, $storeId === null ? $storeId : (int) $storeId]); $cachedProduct = $this->getProductFromLocalCache($sku, $cacheKey); if ($cachedProduct === null || $forceReload) { $product = $this->productFactory->create(); $productId = $this->resourceModel->getIdBySku($sku); if (!$productId) { throw new NoSuchEntityException( __("The product that was requested doesn't exist. Verify the product and try again.") ); } if ($editMode) { $product->setData('_edit_mode', true); } if ($storeId !== null) { $product->setData('store_id', (int) $storeId); } $product->load($productId); $this->cacheProduct($cacheKey, $product); $cachedProduct = $product; } return $cachedProduct; } /** * @inheritdoc */ public function getById($productId, $editMode = false, $storeId = null, $forceReload = false) { $cacheKey = $this->getCacheKey([$editMode, $storeId]); if (!isset($this->instancesById[$productId][$cacheKey]) || $forceReload) { $product = $this->productFactory->create(); if ($editMode) { $product->setData('_edit_mode', true); } if ($storeId !== null) { $product->setData('store_id', $storeId); } $product->load($productId); if (!$product->getId()) { throw new NoSuchEntityException( __("The product that was requested doesn't exist. Verify the product and try again.") ); } $this->cacheProduct($cacheKey, $product); } return $this->instancesById[$productId][$cacheKey]; } /** * Get key for cache * * @param array $data * @return string */ protected function getCacheKey($data) { $serializeData = []; foreach ($data as $key => $value) { if (is_object($value)) { $serializeData[$key] = $value->getId(); } else { $serializeData[$key] = $value; } } $serializeData = $this->serializer->serialize($serializeData); return sha1($serializeData); } /** * Add product to internal cache and truncate cache if it has more than cacheLimit elements. * * @param string $cacheKey * @param ProductInterface $product * @return void */ private function cacheProduct($cacheKey, ProductInterface $product) { $this->instancesById[$product->getId()][$cacheKey] = $product; $this->saveProductInLocalCache($product, $cacheKey); if ($this->cacheLimit && count($this->instances) > $this->cacheLimit) { $offset = round($this->cacheLimit / -2); $this->instancesById = array_slice($this->instancesById, $offset, null, true); $this->instances = array_slice($this->instances, $offset, null, true); } } /** * Merge data from DB and updates from request * * @param array $productData * @param bool $createNew * @return ProductInterface|Product * @throws NoSuchEntityException */ protected function initializeProductData(array $productData, $createNew) { unset($productData['media_gallery']); if ($createNew) { $product = $this->productFactory->create(); $this->assignProductToWebsites($product); } elseif (!empty($productData['id'])) { $this->removeProductFromLocalCacheById($productData['id']); $product = $this->getById($productData['id']); } else { $this->removeProductFromLocalCacheBySku($productData['sku']); $product = $this->get($productData['sku']); } foreach ($productData as $key => $value) { $product->setData($key, $value); } return $product; } /** * Assign product to websites. * * @param \Magento\Catalog\Model\Product $product * @return void */ private function assignProductToWebsites(\Magento\Catalog\Model\Product $product) { if ($this->storeManager->getStore(true)->getCode() == \Magento\Store\Model\Store::ADMIN_CODE) { $websiteIds = array_keys($this->storeManager->getWebsites()); } else { $websiteIds = [$this->storeManager->getStore()->getWebsiteId()]; } $product->setWebsiteIds($websiteIds); } /** * Process new gallery media entry. * * @deprecated 103.0.2 * @see MediaGalleryProcessor::processNewMediaGalleryEntry() * * @param ProductInterface $product * @param array $newEntry * @return $this * @throws InputException * @throws StateException * @throws \Magento\Framework\Exception\LocalizedException */ protected function processNewMediaGalleryEntry( ProductInterface $product, array $newEntry ) { $this->getMediaGalleryProcessor()->processNewMediaGalleryEntry($product, $newEntry); return $this; } /** * Process product links, creating new links, updating and deleting existing links * * @param ProductInterface $product * @param \Magento\Catalog\Api\Data\ProductLinkInterface[] $newLinks * @return $this * @throws NoSuchEntityException */ private function processLinks(ProductInterface $product, $newLinks) { if ($newLinks === null) { // If product links were not specified, don't do anything return $this; } // Clear all existing product links and then set the ones we want $linkTypes = $this->linkTypeProvider->getLinkTypes(); foreach (array_keys($linkTypes) as $typeName) { $this->linkInitializer->initializeLinks($product, [$typeName => []]); } // Set each linktype info if (!empty($newLinks)) { $productLinks = []; foreach ($newLinks as $link) { $productLinks[$link->getLinkType()][] = $link; } foreach ($productLinks as $type => $linksByType) { $assignedSkuList = []; /** @var \Magento\Catalog\Api\Data\ProductLinkInterface $link */ foreach ($linksByType as $link) { $assignedSkuList[] = $link->getLinkedProductSku(); } $linkedProductIds = $this->resourceModel->getProductsIdsBySkus($assignedSkuList); $linksToInitialize = []; foreach ($linksByType as $link) { $linkDataArray = $this->extensibleDataObjectConverter ->toNestedArray($link, [], \Magento\Catalog\Api\Data\ProductLinkInterface::class); $linkedSku = $link->getLinkedProductSku(); if (!isset($linkedProductIds[$linkedSku])) { throw new NoSuchEntityException( __('The Product with the "%1" SKU doesn\'t exist.', $linkedSku) ); } $linkDataArray['product_id'] = $linkedProductIds[$linkedSku]; $linksToInitialize[$linkedProductIds[$linkedSku]] = $linkDataArray; } $this->linkInitializer->initializeLinks($product, [$type => $linksToInitialize]); } } $product->setProductLinks($newLinks); return $this; } /** * Process Media gallery data before save product. * * Compare Media Gallery Entries Data with existing Media Gallery * * If Media entry has not value_id set it as new * * If Existing entry 'value_id' absent in Media Gallery set 'removed' flag * * Merge Existing and new media gallery * * @param ProductInterface $product contains only existing media gallery items * @param array $mediaGalleryEntries array which contains all media gallery items * @return $this * @throws InputException * @throws StateException * @throws LocalizedException * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function processMediaGallery(ProductInterface $product, $mediaGalleryEntries) { $this->getMediaGalleryProcessor()->processMediaGallery($product, $mediaGalleryEntries); return $this; } /** * @inheritdoc * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function save(ProductInterface $product, $saveOptions = false) { $assignToCategories = false; $tierPrices = $product->getData('tier_price'); $productDataToChange = $product->getData(); try { $existingProduct = $product->getId() ? $this->getById($product->getId()) : $this->get($product->getSku()); $product->setData( $this->resourceModel->getLinkField(), $existingProduct->getData($this->resourceModel->getLinkField()) ); if (!$product->hasData(Product::STATUS)) { $product->setStatus($existingProduct->getStatus()); } /** @var ProductExtension $extensionAttributes */ $extensionAttributes = $product->getExtensionAttributes(); if (empty($extensionAttributes->__toArray())) { $product->setExtensionAttributes($existingProduct->getExtensionAttributes()); $assignToCategories = true; } } catch (NoSuchEntityException $e) { $existingProduct = null; } $productDataArray = $this->extensibleDataObjectConverter ->toNestedArray($product, [], ProductInterface::class); $productDataArray = array_replace($productDataArray, $product->getData()); $ignoreLinksFlag = $product->getData('ignore_links_flag'); $productLinks = null; if (!$ignoreLinksFlag && $ignoreLinksFlag !== null) { $productLinks = $product->getProductLinks(); } if (!isset($productDataArray['store_id'])) { $productDataArray['store_id'] = (int) $this->storeManager->getStore()->getId(); } $product = $this->initializeProductData($productDataArray, empty($existingProduct)); $this->processLinks($product, $productLinks); if (isset($productDataArray['media_gallery'])) { $this->processMediaGallery($product, $productDataArray['media_gallery']['images']); } if (!$product->getOptionsReadonly()) { $product->setCanSaveCustomOptions(true); } $validationResult = $this->resourceModel->validate($product); if (true !== $validationResult) { throw new \Magento\Framework\Exception\CouldNotSaveException( __('Invalid product data: %1', implode(',', $validationResult)) ); } if ($tierPrices !== null) { $product->setData('tier_price', $tierPrices); } try { $stores = $product->getStoreIds(); $websites = $product->getWebsiteIds(); } catch (NoSuchEntityException $exception) { $stores = null; $websites = null; } if (!empty($existingProduct) && is_array($stores) && is_array($websites)) { $hasDataChanged = false; $productAttributes = $product->getAttributes(); if ($productAttributes !== null && $product->getStoreId() !== Store::DEFAULT_STORE_ID && (count($stores) > 1 || count($websites) === 1) ) { foreach ($productAttributes as $attribute) { $attributeCode = $attribute->getAttributeCode(); $value = $product->getData($attributeCode); if ($existingProduct->getData($attributeCode) === $value && $attribute->getScope() !== EavAttributeInterface::SCOPE_GLOBAL_TEXT && !is_array($value) && !$attribute->isStatic() && !array_key_exists($attributeCode, $productDataToChange) && $value !== null && !$this->scopeOverriddenValue->containsValue( ProductInterface::class, $product, $attributeCode, $product->getStoreId() ) ) { $product->setData( $attributeCode, $attributeCode === ProductAttributeInterface::CODE_SEO_FIELD_URL_KEY ? false : null ); $hasDataChanged = true; } } if ($hasDataChanged) { $product->setData('_edit_mode', true); } } } $this->saveProduct($product); if ($assignToCategories === true && $product->getCategoryIds()) { $this->linkManagement->assignProductToCategories( $product->getSku(), $product->getCategoryIds() ); } $this->removeProductFromLocalCacheBySku($product->getSku()); $this->removeProductFromLocalCacheById($product->getId()); return $this->get($product->getSku(), false, $product->getStoreId()); } /** * @inheritdoc */ public function delete(ProductInterface $product) { $sku = $product->getSku(); $productId = $product->getId(); try { $this->removeProductFromLocalCacheBySku($product->getSku()); $this->removeProductFromLocalCacheById($product->getId()); $this->resourceModel->delete($product); } catch (ValidatorException $e) { throw new CouldNotSaveException(__($e->getMessage()), $e); } catch (\Exception $e) { throw new \Magento\Framework\Exception\StateException( __('The "%1" product couldn\'t be removed.', $sku), $e ); } $this->removeProductFromLocalCacheBySku($sku); $this->removeProductFromLocalCacheById($productId); return true; } /** * @inheritdoc */ public function deleteById($sku) { $product = $this->get($sku); return $this->delete($product); } /** * @inheritdoc */ public function getList(SearchCriteriaInterface $searchCriteria) { /** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $collection */ $collection = $this->collectionFactory->create(); $this->extensionAttributesJoinProcessor->process($collection); $collection->addAttributeToSelect('*'); $collection->joinAttribute('status', 'catalog_product/status', 'entity_id', null, 'inner'); $collection->joinAttribute('visibility', 'catalog_product/visibility', 'entity_id', null, 'inner'); $this->joinPositionField($collection, $searchCriteria); $this->collectionProcessor->process($searchCriteria, $collection); $collection->load(); $collection->addCategoryIds(); $this->addExtensionAttributes($collection); $searchResult = $this->searchResultsFactory->create(); $searchResult->setSearchCriteria($searchCriteria); $searchResult->setItems($collection->getItems()); $searchResult->setTotalCount($collection->getSize()); foreach ($collection->getItems() as $product) { $this->cacheProduct( $this->getCacheKey( [ false, $product->getStoreId() ] ), $product ); } return $searchResult; } /** * Add extension attributes to loaded items. * * @param Collection $collection * @return Collection */ private function addExtensionAttributes(Collection $collection) : Collection { foreach ($collection->getItems() as $item) { $this->readExtensions->execute($item); } return $collection; } /** * Helper function that adds a FilterGroup to the collection. * * @deprecated 102.0.0 * @param \Magento\Framework\Api\Search\FilterGroup $filterGroup * @param Collection $collection * @return void */ protected function addFilterGroupToCollection( \Magento\Framework\Api\Search\FilterGroup $filterGroup, Collection $collection ) { $fields = []; $categoryFilter = []; foreach ($filterGroup->getFilters() as $filter) { $conditionType = $filter->getConditionType() ?: 'eq'; if ($filter->getField() == 'category_id') { $categoryFilter[$conditionType][] = $filter->getValue(); continue; } $fields[] = ['attribute' => $filter->getField(), $conditionType => $filter->getValue()]; } if ($categoryFilter) { $collection->addCategoriesFilter($categoryFilter); } if ($fields) { $collection->addFieldToFilter($fields); } } /** * Clean internal product cache * * @return void */ public function cleanCache() { $this->instances = null; $this->instancesById = null; } /** * Retrieve media gallery processor. * * @return MediaGalleryProcessor */ private function getMediaGalleryProcessor() { if (null === $this->mediaProcessor) { $this->mediaProcessor = \Magento\Framework\App\ObjectManager::getInstance() ->get(MediaGalleryProcessor::class); } return $this->mediaProcessor; } /** * Retrieve collection processor * * @deprecated 102.0.0 * @return CollectionProcessorInterface */ private function getCollectionProcessor() { if (!$this->collectionProcessor) { $this->collectionProcessor = \Magento\Framework\App\ObjectManager::getInstance()->get( // phpstan:ignore "Class Magento\Catalog\Model\Api\SearchCriteria\ProductCollectionProcessor not found." \Magento\Catalog\Model\Api\SearchCriteria\ProductCollectionProcessor::class ); } return $this->collectionProcessor; } /** * Gets product from the local cache by SKU. * * @param string $sku * @param string $cacheKey * @return Product|null */ private function getProductFromLocalCache(string $sku, string $cacheKey) { $preparedSku = $this->prepareSku($sku); return $this->instances[$preparedSku][$cacheKey] ?? null; } /** * Removes product in the local cache by sku. * * @param string $sku * @return void */ private function removeProductFromLocalCacheBySku(string $sku): void { $preparedSku = $this->prepareSku($sku); unset($this->instances[$preparedSku]); } /** * Removes product in the local cache by id. * * @param string|null $id * @return void */ private function removeProductFromLocalCacheById(?string $id): void { unset($this->instancesById[$id]); } /** * Saves product in the local cache by sku. * * @param Product $product * @param string $cacheKey * @return void */ private function saveProductInLocalCache(Product $product, string $cacheKey): void { $preparedSku = $this->prepareSku($product->getSku()); $this->instances[$preparedSku][$cacheKey] = $product; } /** * Converts SKU to lower case and trims. * * @param string $sku * @return string */ private function prepareSku(string $sku): string { return mb_strtolower(trim($sku)); } /** * Save product resource model. * * @param ProductInterface|Product $product * @throws TemporaryCouldNotSaveException * @throws InputException * @throws CouldNotSaveException * @throws LocalizedException */ private function saveProduct($product): void { try { $this->removeProductFromLocalCacheBySku($product->getSku()); $this->removeProductFromLocalCacheById($product->getId()); $this->resourceModel->save($product); } catch (ConnectionException $exception) { throw new TemporaryCouldNotSaveException( __('Database connection error'), $exception, $exception->getCode() ); } catch (DeadlockException $exception) { throw new TemporaryCouldNotSaveException( __('Database deadlock found when trying to get lock'), $exception, $exception->getCode() ); } catch (LockWaitException $exception) { throw new TemporaryCouldNotSaveException( __('Database lock wait timeout exceeded'), $exception, $exception->getCode() ); } catch (AttributeException $exception) { throw InputException::invalidFieldValue( $exception->getAttributeCode(), $product->getData($exception->getAttributeCode()), $exception ); } catch (ValidatorException $e) { throw new CouldNotSaveException(__($e->getMessage())); } catch (LocalizedException $e) { throw $e; } catch (\Exception $e) { throw new CouldNotSaveException( __('The product was unable to be saved. Please try again.'), $e ); } } /** * Join category position field to make sorting by position possible. * * @param Collection $collection * @param SearchCriteriaInterface $searchCriteria * @return void */ private function joinPositionField( Collection $collection, SearchCriteriaInterface $searchCriteria ): void { $categoryIds = [[]]; foreach ($searchCriteria->getFilterGroups() as $filterGroup) { foreach ($filterGroup->getFilters() as $filter) { if ($filter->getField() === 'category_id') { $filterValue = $filter->getValue(); $categoryIds[] = is_array($filterValue) ? $filterValue : explode(',', $filterValue ?? ''); } } } $categoryIds = array_unique(array_merge(...$categoryIds)); if (count($categoryIds) === 1) { $collection->joinField( 'position', 'catalog_category_product', 'position', 'product_id=entity_id', ['category_id' => current($categoryIds)], 'left' ); } } }