bigbridge-nl / product-import

Fast product import library for Magento 2
MIT License
113 stars 32 forks source link

How to use the programming library to import products #27

Closed jchirschy closed 3 years ago

jchirschy commented 3 years ago

Hello,

I'm pretty new to Magento. Could you help me to use the programming library to import products ?

I've installed the module and run CLI successfully. Could you give us more details on how to get started with the programming library ? Could you give us a complete php file as an example ?

Thanks for you help. Greetings JeanClaude

garfix commented 3 years ago

I am working on an example. May take a few days. :)

garfix commented 3 years ago

Hello JeanClaude,

This example is just a little something to get you started. Modify it in any way you need. This specific example shows a custom class that takes care of importing some products via a CSV file. The method processFile is the main entry to import the file. If you have an array of data instead of a csv file, call importData directly.

If the class is enough for you, you can strip out the rest of the code, but if what you need is a small self-sufficient command line script that loads Magento as well, then keep the bootstrap and application startup code. It loads a file called "abc.csv" that you would need to create. The file contains a name and a price. The class adds some necessary attributes to create a minimal product.

The example also makes the distinction between global attribute values and store view specific values; I hope this is clear. And as you can see, you can make config settings by modifying the $config object.

<?php

use BigBridge\ProductImport\Api\Data\Product;
use BigBridge\ProductImport\Api\Data\ProductStoreView;
use BigBridge\ProductImport\Api\Data\SimpleProduct;
use BigBridge\ProductImport\Api\ImportConfig;
use BigBridge\ProductImport\Api\ImporterFactory;

// =============================================================
// Example CSV file: abc.csv
//
// sku,name,price
// apple1,some apple,1.24
// banana4,this banana,0.75
// =============================================================

// =============================================================
// Bootstrap Magento
// =============================================================

$bootstrap = getcwd() . '/app/bootstrap.php';
if (!file_exists($bootstrap)) {
    die("Execute this command from the root of a shop.\n");
}

require_once $bootstrap;
\Magento\Framework\App\Bootstrap::create(getcwd(), []);

// =============================================================
// Startup
// =============================================================

/** @var MyProductImport $import */
$import = \Magento\Framework\App\ObjectManager::getInstance()->get(MyProductImport::class);
$results = $import->processFile("abc.csv");
print_r($results);

// =============================================================
// A custom import class
// =============================================================

class MyProductImport
{
    const HEADERS = ['sku', 'name', 'price'];

    /** @var ImporterFactory */
    protected $importerFactory;

    public function __construct(
        ImporterFactory $importerFactory
    )
    {
        $this->importerFactory = $importerFactory;
    }

    public function processFile(string $filePath): array
    {
        $result = [];

        list($productLabels, $messages) = $this->readFile($filePath);

        if (!empty($messages)) {
            return $result;
        }

        return $this->importData($productLabels);
    }

    protected function readFile(string $file): array
    {
        $messages = [];
        $data = [];

        $lines = file($file);

        $header = array_shift($lines);

        if (strpos($header, "\t") !== false) {
            $sep = "\t";
        } elseif (strpos($header, ",") !== false) {
            $sep = ',';
        } elseif (strpos($header, ";") !== false) {
            $sep = ';';
        } else {
            $messages[] = "Could not detect column separator in first line";
            goto end;
        }

        $headers = array_map('strtolower', array_map('trim', str_getcsv($header, $sep)));

        if ($headers !== self::HEADERS) {
            $messages[] = "The header of the CSV must be '" . implode(',', self::HEADERS) . "'";
            goto end;
        }

        $i = 1;
        foreach ($lines as $line) {
            $i++;
            $fields = array_map('trim', str_getcsv($line, $sep));
            if ($fields === ['']) {
                continue;
            }
            if (count($fields) !== count(self::HEADERS)) {
                $messages[] = "Line $i does not have the right number of columns";
                goto end;
            }
            $data[] = $fields;
        }

        end:

        return [$data, $messages];
    }

    public function importData(array $data): array
    {
        $messages = [];
        $result = [];
        $productIds = [];

        $config = new ImportConfig();
        $config->emptyNonTextValueStrategy = ImportConfig::EMPTY_NONTEXTUAL_VALUE_STRATEGY_REMOVE;

        $config->resultCallback = function(Product $product) use (&$messages, &$result, &$productIds) {
            if (!$product->isOk()) {
                $messages[] = sprintf("Line %s, sku = %s: %s",
                    $product->lineNumber, $product->getSku(), implode('; ', $product->getErrors()));
            } else {
                $result[] = $product->getSku();
                $productIds[] = $product->id;
            }
        };

        try {

            $importer = $this->importerFactory->createImporter($config);

            foreach ($data as $i => $datum) {

                $sku = $datum[0];
                $name = $datum[1];
                $price = $datum[2];

                $product = new SimpleProduct($sku);
                $product->setAttributeSetByName("Default");
                $product->addCategoriesByGlobalName(['Default Category/Desks', 'Default Category/Chairs', 'Default Category/Boards']);
                $product->setWebsitesByCode(['base']);

                // global scope data
                $global = $product->global();

                $global->setName($name);
                $global->setPrice($price);

                $global->setVisibility(ProductStoreView::VISIBILITY_BOTH);
                $global->setStatus(ProductStoreView::STATUS_DISABLED);
                $global->setTaxClassName("Taxable Goods");
                $global->generateUrlKey();

                // store view specific data (for example, store view code = nl)
                $dutch = $product->storeView('nl');
                $dutch->setName($name . ' met tulpen');
                $dutch->setPrice($price + 1.25);

                // stock data
                $stockItem = $product->defaultStockItem();
                $stockItem->setQty('100');
                $stockItem->setIsInStock(true);

                $product->lineNumber = $i + 2;

                $importer->importSimpleProduct($product);
            }

            // process any remaining products in the pipeline
            $importer->flush();

        } catch (Throwable $throwable) {
            // errors that were not foreseen by the importer (and should not occur)
            $messages[] = $throwable->getMessage();
        }

        return ['messages' => $messages, 'skus' => $result, 'ids' => $productIds];
    }
}
jchirschy commented 3 years ago

Hello @garfix,

That's great. It works perfectly.

Greetings