1

I've written a custom CLI command to import products from an external database into Magento, however whenever I try to save the product, an Exception is thrown.

[Magento\Framework\Exception\SessionException]
Area code not set: Area code must be set before starting a session.

[Magento\Framework\Exception\LocalizedException]
Area code is not set

I have set the area code as per other questions -

public function __construct(State $state, ProductRepositoryInterface $prepo)
{
    // We cannot use core functions (like saving a product) unless the area
    // code is explicitly set.
    try {
        $state->setAreaCode('adminhtml');
    } catch (\Magento\Framework\Exception\LocalizedException $e) {
        // Intentionally left empty.
        echo $e->getMessage();
        exit;
    }
    parent::__construct();
}

However I still get the exception. Please can someone help - this has been driving me mad for days.

HERE IS THE SCRIPT CAUSING THE ISSUE

<?php
/**
 * Created by PhpStorm.
 * User: steve
 * Date: 30/06/2017
 * Time: 10:34
 */
namespace BlackLab\Merlin\Console\Command;
use Symfony\Component\Console\Input\{InputInterface, InputArgument, InputOption};
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Command\Command;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Model\Product\Attribute\Source\Status;
use Magento\Framework\App\{ObjectManager, State};

class MerlinImport extends Command {

    protected $objectManager;
    protected $attributeRepository;
    protected $tableFactory;
    protected $attributeOptionManagement;
    protected $optionLabelFactory;
    protected $optionFactory;
    protected $_output;
    protected $_input;
    protected $_page = '/Merlin.asmx';
    protected $_ua = 'Merlin-Rainbow/1.0 (compatible; Mozilla 4.0; MSIE 5)';
    protected $_url = '';
    protected $_datasource = "driver={PostgreSQL UNICODE};";
    protected $_headers = '';
    protected $_options = [

    ];
    protected $_processing = [];
    protected $state;

    /**
     * Constructor
     *
     * @param State $state A Magento app State instance
     *
     * @return void
     */
    public function __construct(\Magento\Framework\App\State $state,
        \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository,
        \Magento\Eav\Model\Entity\Attribute\Source\TableFactory $tableFactory,
        \Magento\Eav\Api\AttributeOptionManagementInterface $attributeOptionManagement,
        \Magento\Eav\Api\Data\AttributeOptionLabelInterfaceFactory $optionLabelFactory,
        \Magento\Eav\Api\Data\AttributeOptionInterfaceFactory $optionFactory)
    {
        // We cannot use core functions (like saving a product) unless the area
        // code is explicitly set.
        $this->state = $state;

        $this->objectManager = ObjectManager::getInstance();

        $this->attributeRepository = $attributeRepository;
        $this->tableFactory = $tableFactory;
        $this->attributeOptionManagement = $attributeOptionManagement;
        $this->optionLabelFactory = $optionLabelFactory;
        $this->optionFactory = $optionFactory;
        parent::__construct();
    }

    /**
     * Configures arguments and display options for this command.
     *
     * @return void
     */
    protected function configure()
    {
        $this->setName('merlin:import');
        $this->setDescription('Import Merlin products & categories into Magento');
        $this->addArgument('process', InputArgument::REQUIRED, 'The type of import (products/categories/all) is required.');
        parent::configure();
    }

    /**
     * Execute the command to add products or categories.
     *
     * @param InputInterface $input An input instance
     * @param OutputInterface $output An output instance
     *
     * @return void
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
//      $this->state->setAreaCode("admin");
        $this->_input = $input;
        $this->_output = $output;
        $process = $input->getArgument('process');

        $config = $this->objectManager->create('BlackLab\Merlin\Model\Config')
                                      ->load(1);

        if (!$config) {
            $output->writeln('<error>No Configuration found please setup the importer in Merlin > Configuration</error>');
        }
        else {
            // Set the config
            $this->_options    = $config->toArray();
            $this->_url        = 'http://' . $this->_options['host'] . ':' . $this->_options['port'];
            $this->_datasource .= 'server=' . $this->_options['dbhost']
                                  . ';database=' . $this->_options['dbname']
                                  . ';uid=' . $this->_options['dbuser']
                                  . ';pwd=' . $this->_options['dbpass'];

            $this->_getProcessing();

            switch ( $process ) {
                case "products":
                    $this->importProducts();
                    break;

            }
        }
    }

    public function importProducts() {
        $this->state->setAreaCode("admin");
        if (isset($this->_processing['products'])) {
            if ($this->_processing['products']['is_processing'] == 1) {
                return;
            }
        }
        $this->_output->writeln('<info>Running product importer</info>');

        $merlinProducts = $this->objectManager->create( 'BlackLab\Merlin\Model\Products' )->getCollection();
        $merlinProducts->getSelect();
        foreach ($merlinProducts->load() as $merlinProduct) {
            // Get the object data as an array
            $mPrd = $merlinProduct->getData();
            $psuedo = $this->objectManager->create( 'BlackLab\Merlin\Model\Psuedo' )
                                          ->getCollection()
                                          ->addFieldToFilter( 'stock_part', $merlinProduct->getPart() )
                                          ->getFirstItem();
            $psd = $psuedo->getData();
            if (empty($psd)) {
                $this->_output->writeln('<info>No Psuedo object for : ' . $merlinProduct->getPart() . '</info>');
                continue;
            }
            $mPrd['title'] = $psd['name'];
            $mPrd['sku'] = $psd['part'];

            // Get the category for the product
            $category = $this->objectManager->create( 'BlackLab\Merlin\Model\Categories' )
                                            ->getCollection()
                                            ->addFieldToFilter( 'merlin_id', $mPrd['category'] )
                                            ->getFirstItem();
            if ($category->getId() == null) {
                if ($merlinProduct->getMagentoId() > 0) {
                    $product = $this->objectManager->create('\Magento\Catalog\Model\Product')
                                                    ->getCollection()
                                                    ->addFieldToFilter('sku', $mPrd['sku'])
                                                    ->getFirstItem();
                    if ($product->getId() !== null) {
                        $product->setStoreId(0);
                        $product->delete();
                        $merlinProduct->setMagentoId(0);
                        $merlinProduct->save();
                    }
                }
                continue;
            }
            $cat = $category->getData();
            $mPrd['magento_category'] = $cat['magento_id'];

            // Get the price group
            $pgroup = $this->objectManager->create( 'BlackLab\Merlin\Model\PriceGroup' )
                                          ->getCollection()
                                          ->addFieldToFilter( 'part', $mPrd['part'] )
                                          ->getFirstItem();
            $pg = $pgroup->getData();

            $mPrd['disc1'] = $pg['disc1'];
            $mPrd['disc2'] = $pg['disc2'];
            $mPrd['break'] = $pg['break'];

            // Get the related products
            $related = $this->objectManager->create( 'BlackLab\Merlin\Model\ProductAlt' )
                                           ->getCollection()
                                           ->addFieldToFilter( 'part_id', $mPrd['stock_id'] )
                                           ->addFieldToFilter( 'alt_type', 'L');
            // Get the upsells
            $upsells = $this->objectManager->create( 'BlackLab\Merlin\Model\ProductAlt' )
                                           ->getCollection()
                                           ->addFieldToFilter( 'part_id', $mPrd['id'] )
                                           ->addFieldToFilter( 'alt_type', 'A');
            // Get the crossells
            $crossells = $this->objectManager->create( 'BlackLab\Merlin\Model\ProductAlt' )
                                             ->getCollection()
                                             ->addFieldToFilter( 'part_id', $mPrd['id'] )
                                             ->addFieldToFilter( 'alt_type', 'S');
            // Get the pictures
            $mPrd['pictures'] = [];
            $pictures = $this->objectManager->create( 'BlackLab\Merlin\Model\Pictures' )
                                            ->getCollection()
                                            ->addFieldToFilter( 'part', $mPrd['part'] );
            foreach ($pictures->load() as $picture) {
                $mPrd['pictures'][] = [
                    'path' => str_replace("public/../","",$picture->getPath())
                ];
            }

            if ($mPrd['magento_id'] == null || $mPrd['magento_id'] == 0) {
                $this->_output->writeln('<info>Importing product : ' . $merlinProduct->getPart() . '</info>');
                $magentoId = $this->importProduct($mPrd, $related, $crossells, $upsells);
                $merlinProduct->setMagentoId($magentoId);
                $merlinProduct->save();
            }
            else {

            }
        }
    }

    public function importProduct($prd, $related, $crossells, $upsells) {
        $product = $this->objectManager->create('\Magento\Catalog\Model\Product');
        $product->setWebsiteIds([0]);
        $product->setSku($prd['sku']); // Set your sku here
        $product->setName($prd['title']); // Name of Product
        $product->setAttributeSetId(4); // Attribute set id
        $product->setStatus(1); // Status on product enabled/ disabled 1/0
        $product->setStoreId(0);
        $product->setWeight(floatval($prd['weight'])); // weight of product
        $product->setVisibility(4); // visibilty of product (catalog / search / catalog, search / Not visible individually)
        $product->setTaxClassId(2); // Tax class id
        $product->setTypeId('simple'); // type of product (simple/virtual/downloadable/configurable)
        $product->setCategoryIds([$prd['magento_category']]);
        $product->setPrice($prd['disc1']); // price of product
        $product->setDescription($prd['description']);
        $product->setWebsiteIds(array(1));
        $product->setStatus(Status::STATUS_ENABLED);
        $product->setAttributeSetId($product->getDefaultAttributeSetId());
        $product->setUrlKey($this->_slugify($prd['title'] . ' ' . $prd['sku']));
        $product->setTierPrice(
            array(
                [
                    'website' => 'all',
                    'customer_group_id' => 'all',
                    'price_qty' => (int) $prd['break'],
                    'price' => floatval($prd['disc2'])
                ]
            )
        );

        foreach ($prd['pictures'] as $picture) {
//          var_dump($picture['path']);
            if (file_exists($picture['path'])) {
                $product->addImageToMediaGallery( $picture['path'], array(
                    'image',
                    'small_image',
                    'thumbnail'
                ), true, false );
            }
        }
        $stock = $this->objectManager->create( 'BlackLab\Merlin\Model\Stock' )
                                     ->getCollection()
                                     ->addFieldToFilter( 'part', $prd['part'] )
                                     ->getFirstItem();

        $stk = $this->objectManager->create( 'BlackLab\Merlin\Model\Stkuser' )
                                   ->getCollection()
                                   ->addFieldToFilter( 'part', $prd['part'] )
                                   ->getFirstItem();

        $duty = $this->objectManager->create( 'BlackLab\Merlin\Model\Duty' )
                                    ->getCollection()
                                    ->addFieldToFilter( 'code', $stock->getDutyCode() )
                                    ->getFirstItem();

        $product->setStockData(
            array(
                'use_config_manage_stock' => 1,
                'manage_stock' => 1,
                'is_in_stock' => ($stock->getQtyHand() > 0) ? 1:0,
                'qty' => $stock->getQtyHand()
            )
        );
        $product->setCategoryIds([$prd['magento_category']]);
//          // Now that we've got a valid product - lets set the attributes.
        $product->setData('uoi',$stock->getUoi());
        $product->setData('duoi',$stock->getDuoi());
        $product->setData('barrel_ratio',$stock->getBarrelRatio());
        $product->setData('flag_redundant',$stock->getFlagRedundant());
        $product->setData('wine_flag',$stock->getWinesFlag());
        $product->setData('stock_num',$stock->getPart());
        $product->setData('date_amended',$stock->getDateAmended());
        $product->setData('last_sold',$stock->getLastSold());
        $product->setData('sop_cost',$stock->getCostSop());
        $product->setData('recommended',$stock->getAltPart());
        $product->setData('barcode_ean',$stock->getMerlinId());
        $product->setData('manufacturer',$duty->getDescription());
        $product->setData('hide_price', ($stk->getUserchk10() == 'Y') ? 'Yes' : 'No');
        $product->setUrlKey($this->_slugify($prd['title'] . ' ' . $prd['sku']));

        $linkData = [];
        $i = 1;
        // Related Products
        foreach ($related->load() as $rProduct) {
            $rProd = $this->objectManager->create( 'BlackLab\Merlin\Model\Products' )->getCollection()
                                         ->addFieldToFilter( 'id', $rProduct->getAltId() )
                                         ->getFirstItem();
            $rPsuedo = $this->objectManager->create( 'BlackLab\Merlin\Model\Psuedo' )
                                           ->getCollection()
                                           ->addFieldToFilter( 'stock_part', $rProd['part'] )
                                           ->getFirstItem();
            $rPsd = $rPsuedo->getData();
            if (empty($rPsd)) {
                continue;
            }
            $productLink = $this->objectManager->create('Magento\Catalog\Api\Data\ProductLinkInterface')
                                               ->setSku($product->getSku())
                                               ->setLinkedProductSku($rPsd['part'])
                                               ->setPosition($i)
                                               ->setLinkType('related');
            $i++;
            $linkData[] = $productLink;
        }
        $i = 1;
        // Upsell Products
        foreach ($upsells->load() as $uProduct) {
            $uProd = $this->objectManager->create( 'BlackLab\Merlin\Model\Products' )->getCollection()
                                         ->addFieldToFilter( 'id', $uProduct->getAltId() )
                                         ->getFirstItem();
            $uPsuedo = $this->objectManager->create( 'BlackLab\Merlin\Model\Psuedo' )
                                           ->getCollection()
                                           ->addFieldToFilter( 'stock_part', $uProd['part'] )
                                           ->getFirstItem();
            $uPsd = $uPsuedo->getData();
            if (empty($uPsd)) {
                continue;
            }
            $productLink = $this->objectManager->create('Magento\Catalog\Api\Data\ProductLinkInterface')
                                               ->setSku($product->getSku())
                                               ->setLinkedProductSku($uPsd['part'])
                                               ->setPosition($i)
                                               ->setLinkType('upsell');
            $i++;
            $linkData[] = $productLink;
        }
        $i = 1;
        // Crossell Products
        foreach ($upsells->load() as $uProduct) {
            $cProd = $this->objectManager->create( 'BlackLab\Merlin\Model\Products' )->getCollection()
                                         ->addFieldToFilter( 'id', $uProduct->getAltId() )
                                         ->getFirstItem();
            $cPsuedo = $this->objectManager->create( 'BlackLab\Merlin\Model\Psuedo' )
                                           ->getCollection()
                                           ->addFieldToFilter( 'stock_part', $cProd['part'] )
                                           ->getFirstItem();
            $cPsd = $cPsuedo->getData();
            if (empty($cPsd)) {
                continue;
            }
            $productLink = $this->objectManager->create('Magento\Catalog\Api\Data\ProductLinkInterface')
                                               ->setSku($product->getSku())
                                               ->setLinkedProductSku($cPsd['part'])
                                               ->setPosition($i)
                                               ->setLinkType('crosssell');
            $i++;
            $linkData[] = $productLink;
        }
        if (count($linkData) > 0) {
            $product->setProductLinks($linkData);
        }
        try {
            $product->save();
            $this->_output->writeln('Created product = <comment>' . $prd['name'] . '</comment>');
            return $product->getId();
        } catch (Exception $e) {
            $this->_output->writeln('<error>Unable to create product ' . $prd['name'] . '</error>');
        }
    }
}
0

5 Answers 5

0

Replace

$state->setAreaCode('admin');

instead of

$state->setAreaCode('adminhtml');
1
  • Thanks, @rakesh I've tried that, still throws the exception Jul 4, 2017 at 10:51
0

I assume you have correct injections on the class State and ProductRepositoryInterface. Try to rewrite the code as follows:

protected $state;

public function __construct(State $state, ProductRepositoryInterface $prepo)
{
    $this->state = $state;
    //rest of codes
}

//You should have this function
public function execute(){
    $this->state->setAreaCode("adminhtml");
    //rest of codes
}

Ref:

4
  • Tried this - still throws an exception Jul 4, 2017 at 11:07
  • I think there may be something wrong with my DI. I can see the area code is being set in the state object - but when it saves the area code is not set - it look like the State object is not being injected correctly Jul 4, 2017 at 11:53
  • How about changing State to \Magento\Framework\App\State?
    – PY Yick
    Jul 5, 2017 at 2:36
  • No difference - I have added my import script to my original question. Jul 5, 2017 at 7:38
0

Area is not set in Magento CLI (it is not required for any core commands). It can be set in your command implementation:

public function __construct(
    \Magento\Framework\App\State $state
) {
    $state->setAreaCode('adminhtml'); // or 'frontend', depending on your needs
}

Source : : 'Area code not set' issue in custom CLI commands in Magento 2

0

The issue is not the setting of the area code it is the classes you are injecting. You found the right question but this answer explains it best.

https://magento.stackexchange.com/a/162145/70343

Although what the answer doesnt say is that it is bad practice to inject proxies as they are potentially expensive in terms of resource. Which in my opinion makes them useless.

I put together some shell scripts which create various things including creating products

https://github.com/DominicWatts/Faker/blob/master/Helper/Product.php

Which is used here in this console script

https://github.com/DominicWatts/Faker/blob/4172d1ca71166f7f3f20cc6067f6073bed24a129/Console/Command/Product.php#L53

Although that doesnt really demonstrate your problem. if you take a look at this

https://github.com/DominicWatts/Faker/blob/master/Helper/Order.php#L113-L114

See how I had to load the cart interface. Reason being i couldn't inject it as area code is not set on initialisation of the object (therefore doesnt understand what session to use among other things).

So one by one you are going to have to try using object manager instead of injecting.

0

I would take a look at the emulateAreaCode function in State class.

would result in something like:

public function execute() {
$this->state->emulateAreaCode(
'adminhtml',
[$this, "extractedMethod"],
[$parameters]
});

Where extractedMethod is the part that is actually throwing the Area code error.

In my experience this sometimes works better than setArea.

Also, run your command using bin/magento instead of n98-magerun2. sometimes helps as well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.