![]() 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/Product/ |
<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Catalog\Model\Product; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Attribute\ScopeOverriddenValue; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Attribute\Source\Status; use Magento\Catalog\Model\Product\Option\Repository as OptionRepository; use Magento\Catalog\Model\ProductFactory; use Magento\Catalog\Model\ResourceModel\DuplicatedProductAttributesCopier; use Magento\Framework\EntityManager\MetadataPool; use Magento\Store\Model\Store; use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; /** * Catalog product copier. * * Creates product duplicate. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Copier { /** * @var Option\Repository */ protected $optionRepository; /** * @var CopyConstructorInterface */ protected $copyConstructor; /** * @var ProductFactory */ protected $productFactory; /** * @var MetadataPool */ protected $metadataPool; /** * @var ScopeOverriddenValue */ private $scopeOverriddenValue; /** * @var ProductRepositoryInterface */ private $productRepository; /** * @var DuplicatedProductAttributesCopier */ private $attributeCopier; /** * @param CopyConstructorInterface $copyConstructor * @param ProductFactory $productFactory * @param ScopeOverriddenValue $scopeOverriddenValue * @param OptionRepository|null $optionRepository * @param MetadataPool|null $metadataPool * @param ProductRepositoryInterface $productRepository * @param DuplicatedProductAttributesCopier $attributeCopier */ public function __construct( CopyConstructorInterface $copyConstructor, ProductFactory $productFactory, ScopeOverriddenValue $scopeOverriddenValue, OptionRepository $optionRepository, MetadataPool $metadataPool, ProductRepositoryInterface $productRepository, DuplicatedProductAttributesCopier $attributeCopier ) { $this->productFactory = $productFactory; $this->copyConstructor = $copyConstructor; $this->scopeOverriddenValue = $scopeOverriddenValue; $this->optionRepository = $optionRepository; $this->metadataPool = $metadataPool; $this->productRepository = $productRepository; $this->attributeCopier = $attributeCopier; } /** * Create product duplicate * * @param Product $product * @return Product */ public function copy(Product $product): Product { $metadata = $this->metadataPool->getMetadata(ProductInterface::class); /* Regardless in what scope the product was provided, for duplicating we want to clone product in Global scope first */ if ((int)$product->getStoreId() !== Store::DEFAULT_STORE_ID) { $product = $this->productRepository->getById($product->getId(), true, Store::DEFAULT_STORE_ID); } /** @var Product $duplicate */ $duplicate = $this->productFactory->create(); $productData = $product->getData(); $productData = $this->removeStockItem($productData); $duplicate->setData($productData); $duplicate->setOptions([]); $duplicate->setMetaTitle(null); $duplicate->setMetaKeyword(null); $duplicate->setMetaDescription(null); $duplicate->setIsDuplicate(true); $duplicate->setOriginalLinkId($product->getData($metadata->getLinkField())); $duplicate->setStatus(Status::STATUS_DISABLED); $duplicate->setCreatedAt(null); $duplicate->setUpdatedAt(null); $duplicate->setId(null); $duplicate->setStoreId(Store::DEFAULT_STORE_ID); $this->copyConstructor->build($product, $duplicate); $this->setDefaultUrl($product, $duplicate); $this->attributeCopier->copyProductAttributes($product, $duplicate); $this->setStoresUrl($product, $duplicate); $this->optionRepository->duplicate($product, $duplicate); return $duplicate; } /** * Set default URL. * * @param Product $product * @param Product $duplicate * @return void */ private function setDefaultUrl(Product $product, Product $duplicate) : void { $duplicate->setStoreId(Store::DEFAULT_STORE_ID); $resource = $product->getResource(); $attribute = $resource->getAttribute('url_key'); $productId = $product->getId(); $urlKey = $resource->getAttributeRawValue($productId, 'url_key', Store::DEFAULT_STORE_ID); do { $urlKey = $this->modifyUrl($urlKey); $duplicate->setUrlKey($urlKey); } while (!$attribute->getEntity()->checkAttributeUniqueValue($attribute, $duplicate)); $duplicate->setData('url_path', null); $duplicate->save(); } /** * Set URL for each store. * * @param Product $product * @param Product $duplicate * * @return void * @throws UrlAlreadyExistsException */ private function setStoresUrl(Product $product, Product $duplicate) : void { $storeIds = $duplicate->getStoreIds(); $productId = $product->getId(); $productResource = $product->getResource(); $attribute = $productResource->getAttribute('url_key'); $duplicate->setData('save_rewrites_history', false); foreach ($storeIds as $storeId) { $useDefault = !$this->scopeOverriddenValue->containsValue( ProductInterface::class, $product, 'url_key', $storeId ); if ($useDefault) { continue; } $duplicate->setStoreId($storeId); $urlKey = $productResource->getAttributeRawValue($productId, 'url_key', $storeId); $iteration = 0; do { if ($iteration === 10) { throw new UrlAlreadyExistsException(); } $urlKey = $this->modifyUrl($urlKey); $duplicate->setUrlKey($urlKey); $iteration++; } while (!$attribute->getEntity()->checkAttributeUniqueValue($attribute, $duplicate)); $duplicate->setData('url_path', null); $productResource->saveAttribute($duplicate, 'url_path'); $productResource->saveAttribute($duplicate, 'url_key'); } $duplicate->setStoreId(Store::DEFAULT_STORE_ID); } /** * Modify URL key. * * @param string $urlKey * @return string */ private function modifyUrl(string $urlKey) : string { return preg_match('/(.*)-(\d+)$/', $urlKey, $matches) ? $matches[1] . '-' . ($matches[2] + 1) : $urlKey . '-1'; } /** * Remove stock item * * @param array $productData * @return array */ private function removeStockItem(array $productData): array { if (isset($productData[ProductInterface::EXTENSION_ATTRIBUTES_KEY])) { $extensionAttributes = $productData[ProductInterface::EXTENSION_ATTRIBUTES_KEY]; if (null !== $extensionAttributes->getStockItem()) { $extensionAttributes->setData('stock_item', null); } } return $productData; } }