Third & GroveThird & Grove
Feb 9, 2017 - Curtis Ogle

Importing products into Magento2, Part 2

I previously wrote about making a command line tool to import products into Magento. My solution at the time was to create some PHP files full of keyed arrays that a custom Magento command would iterate over in order to create the new records. This solution worked well enough, but required you to generate those data files and also update the Magento command for any new keys that may have been added.

Core Magento has the ability to import products from a CSV through the admin interface. With just a little bit of code, we can reuse the code that drives that import process and use it as a command line option. This will allow a developers to script product imports easily for deployments or for local environment setup. You can either hand craft your CSV import files, or simply export a CSV from some other Magento install to get things going.

I'm not going to cover everything about this module, just basics needed to create this import tool. If you don't already have one, create a Magento module skeleton before you continue reading.

Create app/code/VendorName/ModuleName/Commands/CommandLineImport.php as below:

<?php
 
namespace VendorName\ModuleName\Commands;
 
use Symfony\Component\Console\Command\Command;
use Magento\Framework\App\State;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Magento\ImportExport\Model\ImportFactory;
use Magento\ImportExport\Model\Import\Source\CsvFactory;
use Magento\Framework\Filesystem\Directory\ReadFactory;
 
/**
 * Command to import products.
 */
class CommandLineImport extends Command
{
    /**
     * @var State $state
     */
    private $state;
 
    /**
     * @var Import $importFactory
     */
    protected $importFactory;
 
    /**
     * @var CsvFactory
     */
    private $csvSourceFactory;
 
    /**
     * @var ReadFactory
     */
    private $readFactory;
 
    /**
     * Constructor
     *
     * @param State $state  A Magento app State instance
     * @param ImportFactory $importFactory Factory to create entiry importer
     * @param CsvFactory $csvSourceFactory Factory to read CSV files
     * @param ReadFactory $readFactory Factory to read files from filesystem
     *
     * @return void
     */
    public function __construct(
      State $state,
      ImportFactory $importFactory,
      CsvFactory $csvSourceFactory,
      ReadFactory $readFactory
    ) {
        $this->state = $state;
        $this->importFactory = $importFactory;
        $this->csvSourceFactory = $csvSourceFactory;
        $this->readFactory = $readFactory;
        parent::__construct();
    }
 
    /**
     * Configures arguments and display options for this command.
     *
     * @return void
     */
    protected function configure()
    {
        $this->setName('yourmodule:import-products');
        $this->setDescription('Imports products into Magento from a CSV');
        $this->addArgument('import_path', InputArgument::REQUIRED, 'The path of the import file (ie. ../../path/to/file.csv)');
        parent::configure();
    }
 
    /**
     * Executes the command to add products to the database.
     *
     * @param InputInterface  $input  An input instance
     * @param OutputInterface $output An output instance
     *
     * @return void
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // We cannot use core functions (like saving a product) unless the area
        // code is explicitly set.
        try {
            $this->state->setAreaCode('adminhtml');
        } catch (\Magento\Framework\Exception\LocalizedException $e) {
            // Intentionally left empty.
        }
 
        $import_path = $input->getArgument('import_path');
        $import_file = pathinfo($import_path);
 
        $import = $this->importFactory->create();
        $import->setData(
            array(
                'entity' => 'catalog_product',
                'behavior' => 'append',
                'validation_strategy' => 'validation-stop-on-errors',
            )
        );
 
        $read_file = $this->readFactory->create($import_file['dirname']);
        $csvSource = $this->csvSourceFactory->create(
            array(
                'file' => $import_file['basename'],
                'directory' => $read_file,
            )
        );
 
        $validate = $import->validateSource($csvSource);
        if (!$validate) {
          $output->writeln('<error>Unable to validate the CSV.</error>');
        }
 
        $result = $import->importSource();
        if ($result) {
          $import->invalidateIndex();
        }
 
        $output->writeln("<info>Finished importing products from $import_path</info>");
    }
}

 

Next, create app/code/VendorName/ModuleName/etc/di.xml as below:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Framework\Console\CommandList">
        <arguments>
            <argument name="commands" xsi:type="array">
                <item name="command_line_import" xsi:type="object">VendorName\ModuleName\Commands\CommandLineImport</item>
            </argument>
        </arguments>
    </type>
</config>

 

That's it! You can now try this out by running bin/magento yourmodule:import-products ../../path/to/products.csv.

You may need to take a look at a the sample CSV file that Magento provides if you get any validation errors. Or, you could create a product by hand and then use Magento to export a CSV and try to import it after making a few changes.

One last note, this module could easily be extended to handle the other types of items that core Magento allows to import through the UI. This module is locked down to only import products, but it would not be hard to extend it to handle other entities.

Happy Magento-ing!