EmicoEcommerce/Magento2TweakwiseExport

View on GitHub
TestHelper/Data/ProductProvider.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php

/**
 * Tweakwise (https://www.tweakwise.com/) - All Rights Reserved
 *
 * @copyright Copyright (c) 2017-2022 Tweakwise.com B.V. (https://www.tweakwise.com)
 * @license   Proprietary and confidential, Unauthorized copying of this file, via any medium is strictly prohibited
 */

namespace Tweakwise\Magento2TweakwiseExport\TestHelper\Data;

use Tweakwise\Magento2TweakwiseExport\TestHelper\Data\Product\AttributeProvider;
use Faker\Factory;
use Faker\Generator;
use Magento\Catalog\Api\CategoryLinkManagementInterface;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Api\Data\ProductInterfaceFactory;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\ResourceModel\Eav\Attribute;
use Magento\Catalog\Model\ResourceModel\Product\Action as ProductAction;
use Magento\Catalog\Model\ResourceModel\Product\Website\Link as WebsiteLink;
use Magento\CatalogInventory\Api\Data\StockItemInterface;
use Magento\CatalogInventory\Api\StockRegistryInterface;
use Magento\Eav\Model\Config as EavConfig;
use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface;
use Magento\Framework\App\Area;
use Magento\Store\Model\App\Emulation;
use Magento\Store\Model\Store;
use Magento\Store\Model\StoreManagerInterface;

class ProductProvider
{
    /**
     * Product default stock qty
     */
    private const DEFAULT_STOCK_QTY = 100;

    /**
     * @var Generator
     */
    protected $faker;

    /**
     * @var ProductRepositoryInterface
     */
    protected $productRepository;

    /**
     * @var ProductInterfaceFactory
     */
    protected $productFactory;

    /**
     * @var StockRegistryInterface
     */
    protected $stockRegistry;

    /**
     * @var CategoryLinkManagementInterface
     */
    protected $categoryLinkManagement;

    /**
     * @var EntityHydrator
     */
    protected $hydrator;

    /**
     * @var CategoryProvider
     */
    protected $categoryProvider;

    /**
     * @var AttributeProvider
     */
    protected $attributeProvider;

    /**
     * @var Emulation
     */
    protected $emulation;

    /**
     * @var ProductAction
     */
    protected $productAction;

    /**
     * @var StoreManagerInterface
     */
    protected $storeManager;

    /**
     * @var EavConfig
     */
    protected $eavConfig;

    /**
     * @var WebsiteLink
     */
    protected $websiteLink;

    /**
     * CategoryDataProvider constructor.
     *
     * @param ProductRepositoryInterface $productRepository
     * @param ProductInterfaceFactory $productFactory
     * @param StockRegistryInterface $stockRegistry
     * @param CategoryLinkManagementInterface $categoryLinkManagement
     * @param EntityHydrator $hydrator
     * @param CategoryProvider $categoryProvider
     * @param AttributeProvider $attributeProvider
     * @param Emulation $emulation
     * @param ProductAction $productAction
     * @param StoreManagerInterface $storeManager
     * @param EavConfig $eavConfig
     * @param Link $websiteLink
     */
    public function __construct(
        ProductRepositoryInterface $productRepository,
        ProductInterfaceFactory $productFactory,
        StockRegistryInterface $stockRegistry,
        CategoryLinkManagementInterface $categoryLinkManagement,
        EntityHydrator $hydrator,
        CategoryProvider $categoryProvider,
        AttributeProvider $attributeProvider,
        Emulation $emulation,
        ProductAction $productAction,
        StoreManagerInterface $storeManager,
        EavConfig $eavConfig,
        WebsiteLink $websiteLink
    ) {
        $this->faker = Factory::create();
        $this->productRepository = $productRepository;
        $this->productFactory = $productFactory;
        $this->stockRegistry = $stockRegistry;
        $this->categoryLinkManagement = $categoryLinkManagement;
        $this->hydrator = $hydrator;
        $this->categoryProvider = $categoryProvider;
        $this->attributeProvider = $attributeProvider;
        $this->emulation = $emulation;
        $this->productAction = $productAction;
        $this->storeManager = $storeManager;
        $this->eavConfig = $eavConfig;
        $this->websiteLink = $websiteLink;
    }

    /**
     * @param array $data
     * @param array $extensionAttributeData
     * @return ProductInterface
     */
    public function create(array $data = [], array $extensionAttributeData = []): ProductInterface
    {
        $product = $this->productFactory->create();

        // Set product defaults
        $product->setSku('test-' . $this->faker->uuid);
        $product->setName($this->faker->name);
        $product->setTypeId(Product\Type::TYPE_SIMPLE);
        $product->setVisibility(Product\Visibility::VISIBILITY_BOTH);
        $product->setPrice($this->faker->randomNumber(2));
        $product->setAttributeSetId($this->attributeProvider->getSetId());
        $product->setStatus(Product\Attribute\Source\Status::STATUS_ENABLED);

        // Overwrite with provided data
        $this->hydrator->hydrate($data, $product);
        $this->hydrator->hydrate($extensionAttributeData, $product->getExtensionAttributes());

        // Save product
        $product = $this->save($product);

        // Ensure product qty
        $data['qty'] = $data['qty'] ?? self::DEFAULT_STOCK_QTY;
        if ($data['qty'] > 0 && !isset($data['is_in_stock'])) {
            $data['is_in_stock'] = 1;
        }

        $this->updateStockItem($product, $data);

        // Assign product to categories
        $categoryIds = $data['category_ids'] ?? [$this->categoryProvider->getDefaultRootId()];
        $this->categoryLinkManagement->assignProductToCategories($product->getSku(), $categoryIds);

        return $product;
    }

    /**
     * @param ProductInterface $product
     * @return ProductInterface
     */
    public function save(ProductInterface $product)
    {
        // Save product
        $this->emulation->startEnvironmentEmulation(Store::DEFAULT_STORE_ID, Area::AREA_ADMINHTML);
        try {
            return $this->productRepository->save($product);
        } finally {
            $this->emulation->stopEnvironmentEmulation();
        }
    }

    /**
     * @param ProductInterface $product
     * @param string $attribute
     * @param mixed $value
     * @param string|null $store
     */
    public function saveAttribute(ProductInterface $product, string $attribute, $value, string $store = null)
    {
        $product = clone $product;

        $attributeObject = $this->eavConfig->getAttribute(Product::ENTITY, $attribute);
        $attributeObject->setData(Attribute::KEY_IS_GLOBAL, ScopedAttributeInterface::SCOPE_STORE);

        $updateData = [$attribute => $value];
        if ($store) {
            $storeId = $this->storeManager->getStore($store)->getId();
        } else {
            $storeId = Store::DEFAULT_STORE_ID;
        }

        $this->hydrator->hydrate($updateData, $product);

        /** @var $product Product */
        $this->productAction->updateAttributes([$product->getId()], $updateData, $storeId);
    }

    /**
     * @param ProductInterface $product
     * @param array $data
     * @return StockItemInterface
     */
    public function updateStockItem(ProductInterface $product, array $data): StockItemInterface
    {
        $stockItem = $this->stockRegistry->getStockItemBySku($product->getSku());
        $this->hydrator->hydrate($data, $stockItem);
        $this->stockRegistry->updateStockItemBySku($product->getSku(), $stockItem);
        return $stockItem;
    }

    /**
     * @param ProductInterface $product
     * @param array $websiteIds
     */
    public function saveWebsiteLink(ProductInterface $product, array $websiteIds)
    {
        $this->websiteLink->saveWebsiteIds($product, $websiteIds);
    }
}