10 use Magento\Catalog\Api\Data\ProductExtension;
16 use Magento\Framework\Api\Data\ImageContentInterfaceFactory;
147 private $collectionProcessor;
152 private $cacheLimit = 0;
162 private $readExtensions;
205 \
Magento\Catalog\Api\ProductAttributeRepositoryInterface $metadataServiceInterface,
230 $this->metadataService = $metadataServiceInterface;
236 $this->collectionProcessor = $collectionProcessor ?: $this->getCollectionProcessor();
239 $this->cacheLimit = (int)$cacheLimit;
241 ->get(ReadExtensions::class);
247 public function get($sku, $editMode =
false,
$storeId =
null, $forceReload =
false)
250 $cachedProduct = $this->getProductFromLocalCache($sku, $cacheKey);
251 if ($cachedProduct ===
null || $forceReload) {
252 $product = $this->productFactory->create();
254 $productId = $this->resourceModel->getIdBySku($sku);
257 __(
"The product that was requested doesn't exist. Verify the product and try again.")
261 $product->setData(
'_edit_mode',
true);
267 $this->cacheProduct($cacheKey,
$product);
271 return $cachedProduct;
280 if (!isset($this->instancesById[
$productId][$cacheKey]) || $forceReload) {
281 $product = $this->productFactory->create();
283 $product->setData(
'_edit_mode',
true);
291 __(
"The product that was requested doesn't exist. Verify the product and try again.")
294 $this->cacheProduct($cacheKey,
$product);
296 return $this->instancesById[
$productId][$cacheKey];
310 $serializeData[$key] =
$value->getId();
312 $serializeData[$key] =
$value;
315 $serializeData = $this->serializer->serialize($serializeData);
316 return sha1($serializeData);
329 $this->saveProductInLocalCache(
$product, $cacheKey);
331 if ($this->cacheLimit && count($this->instances) > $this->cacheLimit) {
332 $offset = round($this->cacheLimit / -2);
333 $this->instancesById = array_slice($this->instancesById, $offset,
null,
true);
334 $this->instances = array_slice($this->instances, $offset,
null,
true);
350 $product = $this->productFactory->create();
351 $this->assignProductToWebsites(
$product);
353 $this->removeProductFromLocalCache(
$productData[
'sku']);
354 $product = $this->
get($productData[
'sku']);
373 $websiteIds = array_keys($this->storeManager->getWebsites());
375 $websiteIds = [$this->storeManager->getStore()->getWebsiteId()];
391 protected function processNewMediaGalleryEntry(
396 $contentDataObject = $newEntry[
'content'];
402 $relativeFilePath = $this->imageProcessor->processImageContent($mediaTmpPath, $contentDataObject);
403 $tmpFilePath =
$mediaConfig->getTmpMediaShortUrl($relativeFilePath);
405 if (!
$product->hasGalleryAttribute()) {
406 throw new StateException(
407 __(
"The product that was requested doesn't exist. Verify the product and try again.")
411 $imageFileUri = $this->getMediaGalleryProcessor()->addImage(
414 isset($newEntry[
'types']) ? $newEntry[
'types'] : [],
416 isset($newEntry[
'disabled']) ? $newEntry[
'disabled'] :
true 419 $this->getMediaGalleryProcessor()->updateImage(
423 'label' => $newEntry[
'label'],
424 'position' => $newEntry[
'position'],
425 'disabled' => $newEntry[
'disabled'],
426 'media_type' => $newEntry[
'media_type'],
448 $linkTypes = $this->linkTypeProvider->getLinkTypes();
449 foreach (array_keys($linkTypes) as $typeName) {
450 $this->linkInitializer->initializeLinks(
$product, [$typeName => []]);
457 $productLinks[
$link->getLinkType()][] =
$link;
460 foreach ($productLinks as
$type => $linksByType) {
461 $assignedSkuList = [];
463 foreach ($linksByType as
$link) {
464 $assignedSkuList[] =
$link->getLinkedProductSku();
466 $linkedProductIds = $this->resourceModel->getProductsIdsBySkus($assignedSkuList);
468 $linksToInitialize = [];
469 foreach ($linksByType as
$link) {
470 $linkDataArray = $this->extensibleDataObjectConverter
471 ->toNestedArray(
$link, [], \
Magento\Catalog\Api\Data\ProductLinkInterface::class);
472 $linkedSku =
$link->getLinkedProductSku();
473 if (!isset($linkedProductIds[$linkedSku])) {
474 throw new NoSuchEntityException(
475 __(
'The Product with the "%1" SKU doesn\'t exist.', $linkedSku)
478 $linkDataArray[
'product_id'] = $linkedProductIds[$linkedSku];
479 $linksToInitialize[$linkedProductIds[$linkedSku]] = $linkDataArray;
482 $this->linkInitializer->initializeLinks(
$product, [
$type => $linksToInitialize]);
505 protected function processMediaGallery(ProductInterface
$product, $mediaGalleryEntries)
507 $existingMediaGallery =
$product->getMediaGallery(
'images');
510 if (!empty($existingMediaGallery)) {
511 foreach ($mediaGalleryEntries as $entry) {
512 if (isset($entry[
'value_id'])) {
513 $entriesById[$entry[
'value_id']] = $entry;
515 $newEntries[] = $entry;
518 foreach ($existingMediaGallery as $key => &$existingEntry) {
519 if (isset($entriesById[$existingEntry[
'value_id']])) {
520 $updatedEntry = $entriesById[$existingEntry[
'value_id']];
521 if ($updatedEntry[
'file'] ===
null) {
522 unset($updatedEntry[
'file']);
524 $existingMediaGallery[$key] = array_merge($existingEntry, $updatedEntry);
527 $existingEntry[
'removed'] =
true;
530 $product->setData(
'media_gallery', [
"images" => $existingMediaGallery]);
532 $newEntries = $mediaGalleryEntries;
535 $images = (array)
$product->getMediaGallery(
'images');
536 $images = $this->determineImageRoles(
$product, $images);
538 $this->getMediaGalleryProcessor()->clearMediaAttribute(
$product, array_keys(
$product->getMediaAttributes()));
540 foreach ($images as
$image) {
541 if (!isset(
$image[
'removed']) && !empty(
$image[
'types'])) {
542 $this->getMediaGalleryProcessor()->setMediaAttribute(
$product,
$image[
'types'],
$image[
'file']);
546 foreach ($newEntries as $newEntry) {
547 if (!isset($newEntry[
'content'])) {
548 throw new InputException(
__(
'The image content is invalid. Verify the content and try again.'));
551 $contentDataObject = $this->contentFactory->create()
555 $newEntry[
'content'] = $contentDataObject;
556 $this->processNewMediaGalleryEntry(
$product, $newEntry);
558 $finalGallery =
$product->getData(
'media_gallery');
559 $newEntryId = key(array_diff_key(
$product->getData(
'media_gallery')[
'images'], $entriesById));
560 $newEntry = array_replace_recursive($newEntry, $finalGallery[
'images'][$newEntryId]);
561 $entriesById[$newEntryId] = $newEntry;
562 $finalGallery[
'images'][$newEntryId] = $newEntry;
563 $product->setData(
'media_gallery', $finalGallery);
573 public function save(ProductInterface
$product, $saveOptions =
false)
578 $existingProduct = $this->
get($product->getSku());
581 $this->resourceModel->getLinkField(),
582 $existingProduct->getData($this->resourceModel->getLinkField())
585 $product->setStatus($existingProduct->getStatus());
591 $product->setExtensionAttributes($existingProduct->getExtensionAttributes());
593 }
catch (NoSuchEntityException $e) {
594 $existingProduct =
null;
597 $productDataArray = $this->extensibleDataObjectConverter
598 ->toNestedArray(
$product, [], ProductInterface::class);
599 $productDataArray = array_replace($productDataArray,
$product->getData());
600 $ignoreLinksFlag =
$product->getData(
'ignore_links_flag');
601 $productLinks =
null;
602 if (!$ignoreLinksFlag && $ignoreLinksFlag !==
null) {
603 $productLinks =
$product->getProductLinks();
605 $productDataArray[
'store_id'] = (int)$this->storeManager->getStore()->getId();
608 $this->processLinks(
$product, $productLinks);
609 if (isset($productDataArray[
'media_gallery'])) {
610 $this->processMediaGallery(
$product, $productDataArray[
'media_gallery'][
'images']);
613 if (!
$product->getOptionsReadonly()) {
614 $product->setCanSaveCustomOptions(
true);
617 $validationResult = $this->resourceModel->validate(
$product);
618 if (
true !== $validationResult) {
619 throw new \Magento\Framework\Exception\CouldNotSaveException(
620 __(
'Invalid product data: %1', implode(
',', $validationResult))
629 $this->removeProductFromLocalCache(
$product->getSku());
630 unset($this->instancesById[
$product->getId()]);
632 return $this->
get($product->getSku(),
false,
$product->getStoreId());
643 $this->removeProductFromLocalCache(
$product->getSku());
644 unset($this->instancesById[
$product->getId()]);
645 $this->resourceModel->delete(
$product);
648 }
catch (\Exception $e) {
649 throw new \Magento\Framework\Exception\StateException(
650 __(
'The "%1" product couldn\'t be removed.', $sku)
653 $this->removeProductFromLocalCache($sku);
675 $this->extensionAttributesJoinProcessor->process(
$collection);
678 $collection->joinAttribute(
'status',
'catalog_product/status',
'entity_id',
null,
'inner');
679 $collection->joinAttribute(
'visibility',
'catalog_product/visibility',
'entity_id',
null,
'inner');
687 $searchResult = $this->searchResultsFactory->create();
690 $searchResult->setTotalCount(
$collection->getSize());
697 $product->getStoreId()
704 return $searchResult;
713 private function addExtensionAttributes(Collection
$collection) : Collection
716 $this->readExtensions->execute(
$item);
730 \
Magento\Framework\Api\Search\FilterGroup $filterGroup,
734 $categoryFilter = [];
735 foreach ($filterGroup->getFilters() as $filter) {
736 $conditionType = $filter->getConditionType() ?:
'eq';
738 if ($filter->getField() ==
'category_id') {
739 $categoryFilter[$conditionType][] = $filter->getValue();
742 $fields[] = [
'attribute' => $filter->getField(), $conditionType => $filter->getValue()];
745 if ($categoryFilter) {
761 $this->instances =
null;
762 $this->instancesById =
null;
774 $imagesWithRoles = [];
775 foreach ($images as
$image) {
776 if (!isset(
$image[
'types'])) {
778 if (isset(
$image[
'file'])) {
786 $imagesWithRoles[] =
$image;
788 return $imagesWithRoles;
796 private function getMediaGalleryProcessor()
798 if (
null === $this->mediaGalleryProcessor) {
800 ->get(\
Magento\Catalog\Model\Product\Gallery\Processor::class);
811 private function getCollectionProcessor()
813 if (!$this->collectionProcessor) {
815 'Magento\Catalog\Model\Api\SearchCriteria\ProductCollectionProcessor' 818 return $this->collectionProcessor;
828 private function getProductFromLocalCache(
string $sku,
string $cacheKey)
830 $preparedSku = $this->prepareSku($sku);
832 return $this->instances[$preparedSku][$cacheKey] ??
null;
841 private function removeProductFromLocalCache(
string $sku) :void
843 $preparedSku = $this->prepareSku($sku);
844 unset($this->instances[$preparedSku]);
854 private function saveProductInLocalCache(Product
$product,
string $cacheKey) : void
856 $preparedSku = $this->prepareSku(
$product->getSku());
857 $this->instances[$preparedSku][$cacheKey] =
$product;
866 private function prepareSku(
string $sku): string
868 return mb_strtolower(trim($sku));
880 private function saveProduct(
$product): void
883 $this->removeProductFromLocalCache(
$product->getSku());
884 unset($this->instancesById[
$product->getId()]);
885 $this->resourceModel->save(
$product);
886 }
catch (ConnectionException $exception) {
887 throw new TemporaryCouldNotSaveException(
888 __(
'Database connection error'),
890 $exception->getCode()
892 }
catch (DeadlockException $exception) {
893 throw new TemporaryCouldNotSaveException(
894 __(
'Database deadlock found when trying to get lock'),
896 $exception->getCode()
898 }
catch (LockWaitException $exception) {
899 throw new TemporaryCouldNotSaveException(
900 __(
'Database lock wait timeout exceeded'),
902 $exception->getCode()
904 }
catch (AttributeException $exception) {
906 $exception->getAttributeCode(),
907 $product->getData($exception->getAttributeCode()),
910 }
catch (ValidatorException $e) {
911 throw new CouldNotSaveException(
__($e->getMessage()));
912 }
catch (LocalizedException $e) {
914 }
catch (\Exception $e) {
915 throw new CouldNotSaveException(
916 __(
'The product was unable to be saved. Please try again.'),
$extensibleDataObjectConverter
initializeProductData(array $productData, $createNew)
addFilterGroupToCollection(\Magento\Framework\Api\Search\FilterGroup $filterGroup, Collection $collection)
getById($productId, $editMode=false, $storeId=null, $forceReload=false)
save(\Magento\Catalog\Api\Data\ProductInterface $product, $saveOptions=false)
$extensionAttributesJoinProcessor
const BASE64_ENCODED_DATA
__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)
getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria)