Question

I am trying to update products programatically and I'm having issues with the related/upsell section.

It updates only with the last product sku. So if I am trying to link 3 products with a parent one as in the example below, only the last one will appear in the related section for product parent_sku.

$linkDataAll = [];
$skuLinks = "9780500420584,9780500544679,9780500650936";
$skuLinks = explode(",",$skuLinks);

foreach($skuLinks as $skuLink) {
  //check first that the product exist
  $linkedProduct = $this->productFactory->create()->loadByAttribute("sku",$skuLink);
  if($linkedProduct) {
    $linkData = $this->productLinks //Magento\Catalog\Api\Data\ProductLinkInterface
                    ->setSku("parent_sku")
                    ->setLinkedProductSku($skuLink)
                    ->setLinkType("related");
    $linkDataAll[] = $linkData;
  }

}
if($linkDataAll) {
  print(count($linkDataAll)); //gives 3
  $product->setProductLinks($linkDataAll);
}
$product->save();
Was it helpful?

Solution

This part caused your issue, if you created this object outsize the foreach loop, that means it is a "global" object. So, we need to create a new object inside the loop.

$linkData = $productLinks //Magento\Catalog\Api\Data\ProductLinkInterface

It should like this:

 /** @var \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory $productLinks **/
$linkData = $productLinks->create();

See more here Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper::setProductLinks

Set related products programatically Magento 2

OTHER TIPS

You are always editing the same object.
$this->productLinks is an object so it gets passed around by reference.
$linkData (which is the same as $this->productLinks) is always are reference to the same object.
So $linkDataAll will be an array of 3 elements in your case, but they will all point to the same object.
When first entering the loop, you add some properties to $this->productLinks and add it to the $linkDataAll array.
On the second loop, you add some properties to the same $this->productLinks object and add it again in the array, but this way your first element in the array gets changed.

A simple solution would be to replace

$linkData = $this->productLinks->setSku("parent_sku")....

with

$linkData = clone $this->productLinks;
$linkData->setSku("parent_sku")....

You need to add the method setPosition to $productLinks

foreach($skuLinks as $n $skuLink) {
    // ...
    $this->productLinks
        ->setSku("parent_sku")
        ->setLinkedProductSku($skuLink)
        ->setLinkType("related")
        ->setPosition($n + 1);
    // ...
}

I have same issue, But after above comment i have update the ProductLinkInterfaceFactory Object, And create new

$productLink = $this->productLinkFactory->create()

every time in loop, Now its work for me.

<?php
namespace {{namespace}};
use Magento\Catalog\Api\Data\ProductLinkInterfaceFactory as ProductLinkFactory;
class {{classname}} extends \Magento\Framework\App\Config\Value
{   protected $productLinkFactory;
    protected $productrepInter;  
    public function __construct(
          ....
          ProductLinkFactory $productLinkFactory,
          \Magento\Catalog\Api\ProductRepositoryInterface $productrepInter,
          ....          
    ) {
        ....
        $this->productLinkFactory = $productLinkFactory;
        $this->productrepInter = $productrepInter;
        ....        
    }   
    public function afterSave()
    {   

              $productSku = 'main_sku';
              $relatedSku = 'rel_sku1;rel_sku2';
              $skuLinks = explode(";",$relatedSku);                            
              $linkData = array();
              foreach($skuLinks as $index=>$skuLink)              {
              {
                  $productLink  = $this->productLinkFactory->create();  
                  $linkData[] =  $productLink ->setSku($productSku)
                            ->setLinkedProductSku($skuLink)
                            ->setPosition($index)
                            ->setLinkType('related')
                            ; 
              }
                $product = $this->productrepInter->get($productSku);
                $product->setProductLinks($linkData)->save();
        }              
        return parent::afterSave();
    }
}

Use \Magento\Catalog\Api\ProductLinkManagementInterface $productLinkManagement

before:

if($linkDataAll) {
  print(count($linkDataAll)); //gives 3
  $product->setProductLinks($linkDataAll);
}
$product->save();

after:

$this->productLinkManagement->setProductLinks("parent_sku", $linkDataAll);
Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top