0

I am working in a process to will weekly updating a list of products and assign them to a sales category. The list could have 2000 products or more each time. Right now I am using the method assignProductToCategories from \Magento\Catalog\Api\CategoryLinkManagementInterface

foreach($updatedSalesProducts as $_sku => $_categoryIds){
   $productModel       = $this->_productRepository->get($_sku, true, \Magento\Store\Model\Store::DEFAULT_STORE_ID, true);
   $currentCategories  = $productModel->getCategoryIds();
   $_categoryIds        = array_merge($currentCategories, $_categoryIds);
   $_categoryIds        = array_unique($_categoryIds);

   $this->_categoryLinkManagementInterface->assignProductToCategories($_sku, $_categoryIds);
}

However, each product is taking around 4 seconds to update. So, in total, all the processes are taking 2 hours or more. Is there a faster way to assign the 2000 products to the same category without taking too much time?

4
  • I think you should use setPostedProducts method on the category and in arguments products ids. Then save the category. Jul 29 '21 at 4:56
  • How you getting the product collection? Means which product collection you need to update and assign those into this category? Jul 29 '21 at 5:02
  • @HamendraSunthwal, yes, I am trying that other option at this moment. However, it is strange because the current getPostedProducts count does not match the products assigned to the category right now. Also, I am facing an "SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry" error that throws when I tried to save the category. Jul 29 '21 at 15:49
  • @DhirenVasoya the ERP generates a CSV with the product SKU, and the categories the product should be assigned are divided into different columns. This CSV could be 1000, 2000 and the last file from yesterday was 6000. Jul 29 '21 at 15:51
0

Following @HamendraSunthwa comment, I tried doing it using the setPostedProducts options. The code ends something like this:

First I changed the generation of $updatedSalesProducts. Before it was a relation sku => categoryIds. Now is categoryId => sku's to has all the skus assigned to a same category. Like this:

Before:

SKU1 => [
  CategoryId1,
  CategoryId2,
  CategoryId3
]

After:

CategoryId1 => [
  SKU1,
  SKU2,
  SKU3
]

Now the code looks like this:

foreach($updatedSalesProducts as $_categoryId => $_skusArray){
  $category                 = $this->_categoryRepository->get($_categoryId);
  $originalProductPositions = $category->getProductsPosition();
  $newProductPositions      = $originalProductPositions;
  $position                 = 0;

  foreach($_skusArray as $_sku){
    $newProductPositions[$this->_productResourceModel->getIdBySku($_sku)] = $position;
    $position++;
  }

  if(count($originalProductPositions) != count($newProductPositions)){
    $category->setPostedProducts($newProductPositions);
    
    try{
      $category->save();
    }catch(\Exception $e){
      $this->_logger->info($e);
    }
  }
}

In the firsts two tests, I faced an error throw "SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry" in url_rewrite table. I deleted the records that caused the conflict, and rerun the script, and the rows were generated again, and the products were assigned to the category successfully,

0

$preparedProducts is an array should look like [product_id] => product_position

$categories can be string or array and its category id

/**
 * Assign product to categories
 */
public function assignToCategories($preparedProducts,$categories)
{
    try {
        if($preparedProducts && $categories){
            foreach($categories as $categoryId){
                $setPositionInCategory = $this->categoryRepository->get($categoryId, Store::DEFAULT_STORE_ID);
                $productPositions      = $setPositionInCategory->getProductsPosition();
                $setPositionInCategory->setPostedProducts(($preparedProducts + $productPositions));
                $setPositionInCategory->save();
            }
        }
    } catch (Exception $exception) {
        $this->messageManager->addErrorMessage(__("An error occured: %1", $exception->getMessage()));
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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