TransWikia.com

faster way to update attributes/ update product programmtically with stock data in Magento 2?

Magento Asked by Niraj Patel on November 15, 2021

I have this code but Anybody tell me faster way to update products.

This below code takes much time. So any alternative solution for to save product in fast way and Can I also put sleep function at end of the for loop to less consume memory ?
Thank you

  public function updateProducts(){

        foreach($productIds as $productId){
            $objectManager = MagentoFrameworkAppObjectManager::getInstance(); // instance of object manager
            $product = $objectManager->create('MagentoCatalogModelProduct');
            $product->load($productId);
            $product->setSku('my-sku'); // Set your sku here
            $product->setName('Sample Simple Product'); // Name of Product
            $product->setStatus(1); // Status on product enabled/ disabled 1/0
            $product->setWeight(10); // weight of product
            $product->setVisibility(4); 
            $product->setPrice(100); // price of product
            $product->setStockData(
                                    array(
                                        'use_config_manage_stock' => 0,
                                        'manage_stock' => 1,
                                        'is_in_stock' => 1,
                                        'qty' => 14
                                    )
                                );
    $imageUrl = "https://example.com/img1.jpg";
    $tmpDir = $this->getMediaDirTmpDir();
    $newFileName = $tmpDir . baseName($imageUrl);
                        $result = $this->file->read($imageUrl, $newFileName);
                        if ($result) {
                             $_product->addImageToMediaGallery($newFileName,array('small','thumbnail','base'), $imageType, true, false);
                        }
            $product->save();
          unset($product);
        }
}
    protected function getMediaDirTmpDir(){
            return $this->directoryList->getPath(DirectoryList::MEDIA) . DIRECTORY_SEPARATOR . 'tmp';
        }

4 Answers

If you using console command for this propose that is very fast an useful things. For console command you need to do this below code. In between you have to put the product update csv in var/upload/ folder or you select other folder.

  1. add below code in di.xml file in appcode[Vendor][Namespace]etc
<?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="MagentoFrameworkConsoleCommandList">
            <arguments>
                <argument name="commands" xsi:type="array">
                    <item name="bulk_product_update" xsi:type="object">[Vendor][Namespace]ConsoleCommandUpdateProduct</item>
                </argument>
            </arguments>
        </type>
    </config>
  1. Create UpdateProduct.php in [Vendor][Namespace]ConsoleCommand
<?php

    namespace [Vendor][Namespace]ConsoleCommand;

    use SymfonyComponentConsoleCommandCommand;
    use SymfonyComponentConsoleInputInputInterface;
    use SymfonyComponentConsoleOutputOutputInterface;

    class UpdateProduct extends Command
    {
        public function __construct(
         MagentoStoreModelStoreManagerInterface $storeManager,
         MagentoCatalogModelResourceModelProductAction $productAction,
         MagentoCatalogModelProductFactory $productFactory,
         MagentoCatalogInventoryApiStockRegistryInterface $stockRegistry,
         MagentoFrameworkFileCsv $csv,
         MagentoFrameworkFilesystemDriverFile $fileDriver,
         MagentoFrameworkFilesystemDirectoryList $dir,
         MagentoFrameworkFilesystemIoFile $file
       )
       {
        $this->storeManager = $storeManager;
        $this->productAction = $productAction;
        $this->productFactory = $productFactory;
        $this->stockRegistry = $stockRegistry;
        $this->csv = $csv;
        $this->fileDriver = $fileDriver;
        $this->dir = $dir;
        $this->file = $file;
        parent::__construct();
      }

        protected function configure()
        {
            $this->setName('bulk:productupdate')
                 ->setDescription('Bulk product update with stock & Images');

            parent::configure();
        }

        protected function execute(InputInterface $input, OutputInterface $output)
        {
          $csvFilePath = $this->dir->getPath('var').'/upload/product_data.csv';
           if (!$this->fileDriver->isExists($csvFilePath)) {
                throw new MagentoFrameworkExceptionLocalizedException(__('Invalid file upload attempt.'));
           }

        $storeIds = array_keys($this->storeManager->getStores());
        $csvData = $this->csv->getData($csvFilePath);
        $attributeArray = array();
        foreach ($csvData as $row => $data) {
           if ($row > 0){
             
            $product = $this->productFactory->create()->load($data[$attributeArray['id']]);
           
            if(isset($data[$attributeArray['qty']])) {
                $stockData =  array(
                                        'use_config_manage_stock' => $data[$attributeArray['use_config_manage_stock']],
                                        'manage_stock' => $data[$attributeArray['manage_stock']],
                                        'is_in_stock' => $data[$attributeArray['is_in_stock']],
                                        'qty' => $data[$attributeArray['qty']]
                                    );
               $stockItem = $this->stockRegistry->getStockItem($data[$attributeArray['id']]); 
               $stockItem->setData($stockData);
               $stockItem->save(); 
           }

           $updateAttributes['sku'] = $data[$attributeArray['sku']];
           $updateAttributes['name'] = $data[$attributeArray['name']];
           $updateAttributes['status'] = $data[$attributeArray['status']];
           $updateAttributes['weight'] = $data[$attributeArray['weight']];
           $updateAttributes['visibility'] = $data[$attributeArray['visibility']];
           $updateAttributes['price'] = $data[$attributeArray['price']];

           foreach ($storeIds as $storeId) {
                $this->productAction->updateAttributes([$data[$attributeArray['id']]], $updateAttributes, $storeId);
           }

           if (isset($data[$attributeArray['image']])) {
               $imageUrl = $data[$attributeArray['image']];
               $newFileName = $this->dir->getPath('media'). '/tmp/' . baseName($imageUrl);
               $result = $this->file->read($imageUrl, $newFileName);
               if ($result) {
                             $_product->addImageToMediaGallery($newFileName, array('small','thumbnail','base'), true, false);
              }
           }
           $product->save();
           $output->writeln('Update Complete Product: '.$data[$attributeArray['sku']]);

        }
           } else {
             foreach($data as $key => $dataValue) {
                 $attributeArray[$dataValue] = $key;
             }
           }
        }


            $output->writeln('All product Update complete');
        }
    }

At last you need to run in terminal below command:

bin/magento bulk:productupdate

Answered by Ritesh Santra on November 15, 2021

In order to update product attribute programmatically I use below way which is much faster then to save whole object

public function __construct(
.....
    MagentoCatalogModelResourceModelProductAction $productAction,
    ......  
) {
    $this->productAction = $productAction;
}



public function saveProductAttribute()
{
    $cnt = 0;
    
        try{

            $product = $this->productRepository->get($sku,false, null,true);
            if ($product->getSku()) {
                $updateAttributes['attribute_code1']  = $value1;
                $updateAttributes['attribute_code2']  = $value2;
                $updateAttributes['attribute_code3']  = $value3;
                $updateAttributes['attribute_code4']  = $value4;
                $updateAttributes['attribute_code5']  = $value5;
                $updateAttributes['attribute_code6']  = $value6;
                $updateAttributes['attribute_code7']  = $value7;
                $updateAttributes['attribute_code8']  = $value8;
                // in below code 0 is store Id 
                $this->productAction->updateAttributes([$product->getId()], $updateAttributes, 0);
                $cnt++;
            }
        } catch (Exception $e){

        }
    
    return $this;
}

Answered by Murtuza Zabuawala on November 15, 2021

I have a solution working on my local environment. Before posting the full solution, I'd like to verify your requirements:

  • we are after something that performs faster than your solution but you do not specify whether the speed is over 1 sku, many skus or 1000..

My import has pros and cons: I do not claim it competes with Magento and it is not ready for production as it is but it is not far off if you accept that some validations may go amiss..

  • for instance: I do not verify whether the sku in the import is unique

  • I assume it is fine to imagine the images may all be in pub/media/tmp/ folder

  • my import assigns random data only to the attributes that appear in your question (see below how these random data are generated) and therefore you understand in the real world you will have some csv parsing to do? Also, the data outside the scope of your data is not updated, or even set to a null value for instance.. (that means special_price might need work to do..)

    public function getStockData()
              {
                  return [
                      'use_config_manage_stock' => 0,
                      'manage_stock' => rand(0,1),
                      'is_in_stock' => rand(0,1),
                      'qty' => rand(1, 456)
                  ];
              }
    
              private function getProductRandomData(int $productId)
              {
                  return [
                      'sku' => sprintf('SKUBAT%s', $productId),
                      'name' => sprintf('Name for product %s', $productId),
                      'status' => rand(0,1),
                      'weight' => rand(1, 456),
                      'visibility' => rand(0, 4),
                      'price' => rand(3, 67)
                  ];
              } 
    
  • I do not validate the image extension but I create the image folders like Magento does.

  • finally, I do not use the product save function and that is why I am faster. Yet, I do run the indexes once the collection of products has been updated. That is why over 1 or 100 product, I may not be faster as the indexes will take a while to run. But over 10000, I will likely beat your script by couple of hours

In short, my solution is only as good as the time we have in a weekend and I hope you appreciate I have not come up with a genius script that outperforms years of work from Magento team.

If you could acknowledge the above is something that you have considered, I will post the solution.

https://bitbucket.org/magstaging/importbatchproduct/src

the above module has a command to run the product import.

php bin/magento mbs:import:product '1,2,3,4,5,6'

will import your data and images very fast, I'd say in 15 minutes for 10k but of course it depends on your server. But it will be definitely faster than your script as no product load is performed and no product save either.

It means unfortunately, the script may bypass plugin you may have on product save function for instance. But I can't read your system and I hope it will make your import happy..

Answered by Herve Tribouilloy on November 15, 2021

I answered a similar question on this ticket, you can create a command import by using the default Magento import model.

That means you can use Magento import template CSV file like UI at System -> Data Transfer -> Import / Export

Ref: https://magento.stackexchange.com/a/318174/85035

Let me know if you need a more detailed script.

Thanks

Answered by vinhphon on November 15, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP