Spamworldpro Mini Shell
Spamworldpro


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/app/code/Cnc/Catalog/Console/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/corals/old/app/code/Cnc/Catalog/Console/ImportImages.php
<?php
/**
 * @license http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
 * @author Radosław Łańcucki <[email protected]>
 * @copyright Copyright (c) 2020 Kaliop Digital Commerce (https://digitalcommerce.kaliop.com)
 */
declare(strict_types=1);

namespace Cnc\Catalog\Console;

use Cnc\Catalog\Helper\Data as CncCatalogHelper;
use Cnc\Catalog\Model\Product\ImportFactory;
use Cnc\Catalog\Model\ResourceModel\DataFactory as SourceDataFactory;
use Magento\Catalog\Api\CategoryRepositoryInterface;
use Magento\Catalog\Model\Category;
use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection;
use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory;
use Magento\Catalog\Model\ResourceModel\Product\Collection as ProductCollection;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Filesystem\Driver\File;
use Magento\Framework\Setup\SampleData\FixtureManager;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class ImportImages extends Command
{
    private const ARGUMENT_IMAGE_IMPORT_CATEGORIES = 'categories';
    private const ARGUMENT_IMAGE_IMPORT_PRODUCTS = 'products';
    private const IMPORT_FILE_PATH_CATEGORY_IMAGES = 'Cnc_Catalog::fixtures/category_images.csv';
    private const IMPORT_FILE_PATH_PRODUCT_IMAGES = 'Cnc_Catalog::fixtures/products.csv';
    private const CSV_DELIMITER = ',';
    private const FILE_IMPORT_PATH = 'pub/media/import';
    private const FILE_IMPORT_SUB_FOLDER_PATH = 'pub/media/import/diaporama';
    private const FILE_MEDIA_PATH_CATEGORY = 'pub/media/catalog/category';
    private const CATEGORY_MEDIA_ATTRIBUTE = [
        'image',
        'small_image',
        'thumbnail'
    ];
    /**
     * Characters that MUST be removed from Image name
     */
    private const IMAGE_NAME_SPECIAL_CHARACTERS = [
        ' ',
        '°',
        '+',
        '(',
        ')',
        ';',
        '#',
        '$',
        '&'
    ];

    /**
     * @var CategoryRepositoryInterface
     */
    private $categoryRepository;

    /**
     * @var CategoryCollectionFactory
     */
    private $categoryCollectionFactory;

    /**
     * @var ProductCollectionFactory
     */
    private $productCollectionFactory;

    /**
     * @var SourceDataFactory
     */
    private $sourceDataFactory;

    /**
     * @var ImportFactory
     */
    private $importFactory;

    /**
     * @var File
     */
    private $file;

    /**
     * @var FixtureManager
     */
    private $fixtureManager;

    /**
     * @var ResourceConnection
     */
    private $resourceConnection;

    /**
     * @var CncCatalogHelper
     */
    private $cncCatalogHelper;

    /**
     * @var array
     */
    private $imageSubFolders = [];

    /**
     * ImportImages constructor.
     * @param CategoryCollectionFactory $categoryCollectionFactory
     * @param ProductCollectionFactory $productCollectionFactory
     * @param CategoryRepositoryInterface $categoryRepository
     * @param File $file
     * @param FixtureManager $fixtureManager
     * @param SourceDataFactory $sourceDataFactory
     * @param ImportFactory $importFactory
     * @param ResourceConnection $resourceConnection
     * @param CncCatalogHelper $cncCatalogHelper
     * @param string|null $name
     */
    public function __construct(
        CategoryCollectionFactory $categoryCollectionFactory,
        ProductCollectionFactory $productCollectionFactory,
        CategoryRepositoryInterface $categoryRepository,
        File $file,
        FixtureManager $fixtureManager,
        SourceDataFactory $sourceDataFactory,
        ImportFactory $importFactory,
        ResourceConnection $resourceConnection,
        CncCatalogHelper $cncCatalogHelper,
        string $name = null
    ) {
        parent::__construct($name);
        $this->categoryCollectionFactory = $categoryCollectionFactory;
        $this->productCollectionFactory = $productCollectionFactory;
        $this->categoryRepository = $categoryRepository;
        $this->file = $file;
        $this->fixtureManager = $fixtureManager;
        $this->sourceDataFactory = $sourceDataFactory;
        $this->importFactory = $importFactory;
        $this->resourceConnection = $resourceConnection;
        $this->cncCatalogHelper = $cncCatalogHelper;
    }

    /**
     * @inheritDoc
     */
    protected function configure()
    {
        $this->setName('catalog:image:import');
        $this->setDescription('Products/Categories image import');
        $this->addOption(
            self::ARGUMENT_IMAGE_IMPORT_CATEGORIES,
            'c',
            InputOption::VALUE_OPTIONAL,
            'Category Images import',
            []
        );
        $this->addOption(
            self::ARGUMENT_IMAGE_IMPORT_PRODUCTS,
            'p',
            InputOption::VALUE_OPTIONAL,
            'Category Images import',
            []
        );
        parent::configure();
    }

    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     * @return int|void|null
     * @throws \Magento\Framework\Exception\CouldNotSaveException
     * @throws \Magento\Framework\Exception\FileSystemException
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $isCategoriesImport = $input->getOption(self::ARGUMENT_IMAGE_IMPORT_CATEGORIES);
        $isProductsImport = $input->getOption(self::ARGUMENT_IMAGE_IMPORT_PRODUCTS);

        if ($isCategoriesImport) {
            $output->writeln("<info>Started for Category images</info>");
            $this->importCategoryImages($output);
            $output->writeln("<info>Finished for Category images</info>");
        }

        if ($isProductsImport) {
            $output->writeln("<info>Started for Product images</info>");
            $this->importProductImages($output);
            $output->writeln("<info>Finished for Product images</info>");
        }

        if (!$isCategoriesImport && !$isProductsImport) {
            $output->writeln(
                "<info>Please type -categories or -products (or both) option(s) to process images</info>"
            );
        }

        $output->writeln("<info>Finished Import images</info>");
    }

    /**
     * @param OutputInterface $output
     * @throws \Magento\Framework\Exception\CouldNotSaveException
     * @throws \Magento\Framework\Exception\FileSystemException
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    private function importCategoryImages(OutputInterface $output): void
    {
        $categoriesData = $this->loadImportFile(self::IMPORT_FILE_PATH_CATEGORY_IMAGES);
        $categoryIds = array_column($categoriesData, 'categories_id');
        /** @var Category[] $categories */
        $categories = $this->getCategories($categoryIds);

        foreach ($categoriesData as $categoryData) {
            $categoryId = $categoryData['categories_id'];
            $image = $categoryData['categories_image'] ?? null;

            //Do not process not imported Category
            if (!isset($categories[$categoryId])) {
                $output->writeln("<info>Category {$categoryId} is not imported!</info>");
                continue;
            }

            if (!is_string($image)) {
                $output->writeln("<info>Category {$categoryId} no image detected!</info>");
                continue;
            }

            if (!$this->copyFile($image, self::FILE_MEDIA_PATH_CATEGORY)) {
                $output->writeln("<info>Category {$categoryId} image {$image} cannot be copied!</info>");
                continue;
            }

            /** @var Category $category */
            $category = $categories[$categoryId];

            $category->setImage($image, self::CATEGORY_MEDIA_ATTRIBUTE, true, false);
            $category->setStoreId(0);
            $this->categoryRepository->save($category);
        }
    }

    /**
     * @param string $filename
     * @return array
     * @throws \Magento\Framework\Exception\FileSystemException
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    private function loadImportFile(string $filename): array
    {
        $data = [];
        $fileName = $this->fixtureManager->getFixture($filename);
        $file = $this->file->fileOpen($fileName, "r");
        $header = $this->file->fileGetCsv($file, 0, self::CSV_DELIMITER);

        while ($row = $this->file->fileGetCsv($file, 0, self::CSV_DELIMITER)) {
            $data[] = array_filter(array_combine($header, $row));
        }
        return $data;
    }

    /**
     * @param string $filename
     * @param string $destination
     * @return bool
     * @throws \Magento\Framework\Exception\FileSystemException
     */
    private function copyFile(string $filename, string $destination)
    {
        $filePath = $this->cncCatalogHelper->getRealPathCaseInsensitive(
            self::FILE_IMPORT_PATH .
            DIRECTORY_SEPARATOR .
            $filename
        );
        $destination = $this->file->getRealPath($destination) . DIRECTORY_SEPARATOR . $filename;

        if (!$this->file->isExists($filePath)) {
            return false;
        }

        $this->file->copy($filePath, $destination);

        return true;
    }

    /**
     * @param array $categoryIds
     * @return array
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    private function getCategories(array $categoryIds): array
    {
        /** @var CategoryCollection $collection */
        $collection = $this->categoryCollectionFactory->create();
        $collection->addAttributeToFilter(
            \Cnc\Catalog\Model\Config::CATEGORY_ATTRIBUTE_CODE_OSCOMMERCE_CAT_ID,
            ['in' => $categoryIds]
        );

        $items = [];
        foreach ($collection->getItems() as $category) {
            $categoryId = $category->getData(\Cnc\Catalog\Model\Config::CATEGORY_ATTRIBUTE_CODE_OSCOMMERCE_CAT_ID);
            $items[$categoryId] = $category;
        }

        return $items;
    }

    /**
     * @param OutputInterface $output
     * @throws \Magento\Framework\Exception\FileSystemException
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    private function importProductImages(OutputInterface $output): void
    {
        $importedProducts = $this->loadImportFile(self::IMPORT_FILE_PATH_PRODUCT_IMAGES);
        $currentProducts = $this->getProducts([], true);

        $data = $this->getProductsImportData($output, $importedProducts, $currentProducts);

        if (!empty($data)) {
            $this->cleanProductImages($data);
        }

        $sourceModel = $this->sourceDataFactory->create(['data' => [$data]]);
        $product = $this->importFactory->create(['importData' => $sourceModel]);

        try {
            $product->importData();
        } catch (\Exception $exception) {
            var_export($exception->getMessage());
        }
    }

    /**
     * @param array $importData
     */
    private function cleanProductImages(array $importData): void
    {
        $skus = array_keys($importData);
        $select = $this->resourceConnection->getConnection()->select()
            ->from(
                ['cpe' => $this->resourceConnection->getTableName('catalog_product_entity')]
            )
            ->joinLeft(
                ['val' => $this->resourceConnection->getTableName('catalog_product_entity_media_gallery_value')],
                'val.entity_id = cpe.entity_id'
            )
            ->joinLeft(
                ['gal' => $this->resourceConnection->getTableName('catalog_product_entity_media_gallery')],
                'gal.value_id = val.value_id'
            )
            ->where('cpe.sku IN (?)', $skus)
            ->where('gal.media_type != ?', "external-video")
            ->reset(\Zend_Db_Select::COLUMNS)
            ->columns([
                'value_id' => 'val.value_id'
            ]);

        $valueIds = $this->resourceConnection->getConnection()->fetchAll($select);

        if (!empty($valueIds)) {
            $connection = $this->resourceConnection->getConnection();
            $connection->delete(
                $this->resourceConnection->getTableName('catalog_product_entity_media_gallery'),
                ['value_id IN (?)' => array_column($valueIds, 'value_id')]
            );
        }
    }

    /**
     * @param array $skus
     * @param bool $asArray
     * @return array
     */
    private function getProducts(array $skus, bool $asArray = false): array
    {
        /** @var ProductCollection $collection */
        $collection = $this->productCollectionFactory->create();

        if (!empty($skus)) {
            $collection->addFieldToFilter('sku', ['in' => $skus]);
        }

        if ($asArray) {
            $select = $collection->getSelect();
            $select->joinLeft(
                ['attribute_set' => $collection->getConnection()->getTableName('eav_attribute_set')],
                'e.attribute_set_id = attribute_set.attribute_set_id',
                ['attribute_set.attribute_set_name']
            );
            $select->reset('columns')
                ->columns(['sku', 'entity_id', 'type_id', 'attribute_set.attribute_set_name']);

            return $collection->getConnection()->fetchAssoc($select);
        }

        return $collection->getItems();
    }

    /**
     * @param OutputInterface $output
     * @param array $importedData
     * @param array $currentProducts
     * @return array
     */
    private function getProductsImportData(OutputInterface $output, array $importedData, array $currentProducts): array
    {
        $data = [];
        $this->imageSubFolders = $this->getImageSubFolders();

        foreach ($importedData as $product) {
            $simpleSku = $product['products_model'];
            $groupedSku = $product['products_group'] ?? '';
            $image = $product['products_image'] ?? null;

            //Do not import images for not imported Product
            if (!isset($currentProducts[$simpleSku])) {
                $output->writeln("<info>Product {$simpleSku} is not imported!</info>");
                continue;
            }

            if (is_string($image)) {
                $image = $this->getValidatedImageName(self::FILE_IMPORT_PATH, $image);
            } else {
                $output->writeln("<info>Product {$simpleSku} main image is not defined!</info>");
            }

            $additionalImages = $this->getAdditionalImages($simpleSku);

            $data[$simpleSku] = $this->addProductImportData(
                $simpleSku,
                $image,
                $additionalImages,
                $currentProducts[$simpleSku]['attribute_set_name'],
                $currentProducts[$simpleSku]['type_id']
            );

            //Check potential grouped Product
            if (!empty($groupedSku)) {
                //Additional condition to change grouped sku when products.csv file have same sku for simple and grouped
                if (!isset($currentProducts[$groupedSku]) || $currentProducts[$groupedSku]['type_id'] === 'simple') {
                    $groupedSku = $groupedSku . '-g';
                }

                if (isset($currentProducts[$groupedSku])
                    && $currentProducts[$groupedSku]['type_id'] === 'grouped'
                    && isset($product['products_order'])
                    && $product['products_order'] == 1) {
                    $data[$groupedSku] = $this->addProductImportData(
                        $groupedSku,
                        $image,
                        $additionalImages,
                        $currentProducts[$groupedSku]['attribute_set_name'],
                        $currentProducts[$groupedSku]['type_id']
                    );
                }
            }
        }

        return $data;
    }

    /**
     * @param string $sku
     * @param string $image
     * @param string $additionalImages
     * @param string $attributeSet
     * @param string $type
     * @return array
     */
    private function addProductImportData(
        string $sku,
        $image,
        string $additionalImages,
        string $attributeSet,
        string $type
    ): array {
        $data = [
            'sku' => $sku,
            'image' => $image,
            'small_image' => $image,
            'thumbnail' => $image,
            'product_type' => $type,
            '_attribute_set' => $attributeSet
        ];

        if (!empty($additionalImages)) {
            $data['_media_image'] = $additionalImages;
        }

        return $data;
    }

    /**
     * @param string $path
     * @param string $image
     * @return string
     * @throws \Magento\Framework\Exception\FileSystemException
     */
    private function getValidatedImageName(string $path, string &$image): string
    {
        $caseSensitiveFixedPath = $this->cncCatalogHelper->getRealPathCaseInsensitive(
            $path .
            DIRECTORY_SEPARATOR .
            $image
        );
        if ($caseSensitiveFixedPath) {
            $pathAsArray = explode(DIRECTORY_SEPARATOR, $caseSensitiveFixedPath);
            //Set fixed name of image file because of problems with letters size.
            $image = end($pathAsArray);
        }
        foreach (self::IMAGE_NAME_SPECIAL_CHARACTERS as $specialCharacter) {
            if (strpos($image, $specialCharacter) !== false) {
                $image = $this->getFixedImageFile($path, $image, $specialCharacter);
            }
        }

        return $image;
    }

    /**
     * @param string $path
     * @param string $image
     * @param string $specialCharacter
     * @return string
     * @throws \Magento\Framework\Exception\FileSystemException
     */
    private function getFixedImageFile(string $path, string $image, string $specialCharacter): string
    {
        $oldPath = $this->file->getRealPath($path . DIRECTORY_SEPARATOR . $image);
        $newImage = str_replace($specialCharacter, '', $image);
        $newPath = $this->file->getRealPath($path) . DIRECTORY_SEPARATOR . $newImage;

        if ($this->file->isExists($oldPath)) {
            $this->file->rename($oldPath, $newPath);
        }

        return $newImage;
    }

    /**
     * @param string $path
     * @return string
     */
    private function getFixedImagePath(string $path): string
    {
        $newPath = $path;

        foreach (self::IMAGE_NAME_SPECIAL_CHARACTERS as $specialCharacter) {
            if (strpos($path, $specialCharacter) !== false) {
                $newPath = str_replace($specialCharacter, '', $path);
            }
        }

        //We need to create New Path and copy all files from old Path in case of data normalization
        if ($newPath !== $path) {
            if (!$this->file->isDirectory($newPath)) {
                $this->file->createDirectory($newPath);
                $files = $this->file->readDirectory($path);

                foreach ($files as $file) {
                    $newFile = str_replace($path, $newPath, $file);
                    $this->file->copy($file, $newFile);
                }
            }
        }

        return $newPath;
    }

    /**
     * @return array
     */
    private function getImageSubFolders(): array
    {
        $list = [];

        foreach ($this->file->readDirectory(self::FILE_IMPORT_SUB_FOLDER_PATH) as $dirPath) {
            $dirName = explode('/', $dirPath);
            $sku = end($dirName);
            $list[$sku] = $sku;
        }

        return $list;
    }

    /**
     * @param string $sku
     * @return string
     * @throws \Magento\Framework\Exception\FileSystemException
     */
    private function getAdditionalImages(string $sku): string
    {
        $additionalImages = [];

        if (isset($this->imageSubFolders[$sku])) {
            $dirPath = self::FILE_IMPORT_SUB_FOLDER_PATH . DIRECTORY_SEPARATOR . $sku;
            $dirPath = $this->getFixedImagePath($dirPath);

            if ($this->file->isDirectory($dirPath)) {
                $files = $this->file->readDirectory($dirPath);

                foreach ($files as $file) {
                    if ($this->file->isFile($file)) {
                        $parts = explode('/', $file);
                        $file = end($parts);
                        $image = $this->getValidatedImageName($dirPath, $file);
                        //Importer requires relative path starting from main import Media folder
                        $additionalImages[] = str_replace(self::FILE_IMPORT_PATH . DIRECTORY_SEPARATOR, '', $dirPath) .
                            DIRECTORY_SEPARATOR . $image;
                    }
                }
            }
        }

        return implode(',', $additionalImages);
    }
}

Spamworldpro Mini