5

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();

5 Answers 5

11

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

2
  • thanks for the gist, with object manager it works, but I don't want to use the object manager. productLinks->create() throws an non-existing method create. I've checked the available methods and there is no create in Magento\Catalog\Api\Data\ProductLinkInterface. then I realise I should use Factories, so using Magento\Catalog\Api\Data\ProductLinkInterfaceFactory and create() did the job Commented Sep 15, 2016 at 8:57
  • 1
    Yes, you can see more: Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper::setProductLinks. Commented Sep 15, 2016 at 8:58
1

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")....
2
  • thanks for the explanation. It is clearer now what happened. I guess it was harder to see it because var_dump crashes the site and xdebug is so slow so I cannot inspect how objects look. Anyway I should have used Factories: Magento\Catalog\Api\Data\ProductLinkInterfaceFactory and then create() method. Commented Sep 15, 2016 at 8:59
  • 1
    @ClaudiuCreanga. That's even better. You should use the factory. But I wanted to give you a quick solution with minimum code change. "Bafta".
    – Marius
    Commented Sep 15, 2016 at 9:00
1

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);
    // ...
}
0
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();
    }
}
0

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);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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