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?