set related/upsell products programatically only works for 1 product
-
14-04-2021 - |
Pregunta
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();
Solución
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
Otros consejos
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);