Magento 2 – Make JSON Calls Inside Controller

importjsonmagento2redirect

I'm developing a product import function by XML. The import works fine but the input XML is 9000 products and the request already uses 200MB memory with 100 products (even with the clearInstance() calls on each Product and StockItem).

Since it is PHP the request will use more and more memory the longer it runs. So I've decided to spread the functionality out over several requests. I want to loop through the products, execute or send the request foreach product which will import the product and then log the return value, and then continue the loop. Eventually this will become thousands of requests but that only costs time, not memory. The import will run once, at night so the time is not an issue.

Now I made two controllers: One main Index controller which is called from the cron job and another controller which is called by the Index controller. In the main controller I make a JSON redirect but it's not sent. How do I sent a request from inside the Index controller, wait for it's response and then continue execution?

Below is simplified code:

//Module/Import/Controller/Adminhtml/Import/Index.php
class Index extends \Magento\Backend\App\Action
{
    private $productRepository;
    private $productFactory;
    private $resultJsonFactory;

    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Magento\Catalog\Model\ProductRepository $productRepository,
        \Magento\Catalog\Model\ProductFactory $productFactory,
        \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory
    )
    {
        $this->productRepository = $productRepository;
        $this->productFactory = $productFactory;
        $this->resultJsonFactory = $resultJsonFactory;
        parent::__construct($context);
    }

    public function execute()
    {
        $file = 'import_feed.xml';
        $productNodes = $this->getProductNodesFromXml($file);
        foreach ($productNodes as $product) {
            // Turn XML node into data array
            $data = $this->convertValues();
            $data = json_encode($data);
            $resultRedirect = $this->resultRedirectFactory->create()
                ->setPath('module/import/product', array('data' => $data));
        }
    }
}

And a secundary controller which is called for each product:

//Module/Import/Controller/Adminhtml/Import/Product.php
class Product extends \Magento\Backend\App\Action
{
    private $productRepository;
    private $productFactory;
    private $stockRegistry;
    private $storeManager;
    private $jsonFactory;

    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Magento\Catalog\Model\ProductRepository $productRepository,
        \Magento\Catalog\Model\ProductFactory $productFactory,
        \Magento\CatalogInventory\Model\StockRegistry $stockRegistry,
        \Magento\Framework\Controller\Result\JsonFactory $jsonFactory,
        \Magento\Store\Model\StoreManagerInterface $storeManager
    )
    {
        $this->productRepository = $productRepository;
        $this->productFactory = $productFactory;
        $this->stockRegistry = $stockRegistry;
        $this->storeManager = $storeManager;
        $this->jsonFactory = $jsonFactory;
        parent::__construct($context);
    }

    public function execute()
    {
        $data = $this->getRequest()->getParam('data');
        $data = json_decode($data);

        // Create product model
        /** @var \Magento\Catalog\Model\Product $product */
        $product = $this->productFactory->create();

        // Set data, this is obviously more complicated in the real class
        $product->setData($data);

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

        $product->clearInstance();

        $result = $this->jsonFactory->create();
        $result->setHttpResponseCode(200);
        $result->setData(['success' => true]);
    }
}

How can I call the secundary controller from the Index controller and use its return value?

Best Answer

For your bulk product import you can use FireGento_FastSimpleImport

You just have to create 2D array from your json data.

/** @var \FireGento\FastSimpleImport\Model\Importer $importerModel */
$importerModel = $this->objectManager->create('FireGento\FastSimpleImport\Model\Importer');

$productsArray = array(
array(
        'sku' => 'test1',
        'attribute_set_code' => 'Default',
        'product_type' => 'simple',
        'product_websites' => 'base',
        'name' => 'Test Product1',
        'price' => '1000',
     ),
array(
        'sku' => 'test2',
        'attribute_set_code' => 'Default',
        'product_type' => 'simple',
        'product_websites' => 'base',
        'name' => 'Test Product 2',
        'price' => '1400',
     ),
// add more products here
);

try {
    $importerModel->processImport($productsArray);
} catch (Exception $e) {
    // insert into log $e->getMessage()
}

You have to creat Inner array for each product. By this you dont have to load product model and set-save data for each product. It will import in one go or you can make chunk of 250 to 300.

You can find documentation

Demo to refer.

And using this you dont have to create and call another controller.