Hello everyone,
I'm currently working on a custom Magento 2 module that aims to automatically assign cross-sell relationships to specific “Holster” products. The module reads from a custom table called fit_builded, which contains two columns:
- holster_sku: SKU of the main Holster product.
- compatible_sku: One or more SKUs (separated by commas) that should be assigned as cross-sell products for the Holster.
The module then attempts to create cross-sell links for each Holster SKU using the data from fit_builded. I’ve tried multiple approaches, including using getCrossSellLinks / setCrossSellLinks and also getCrossSellProductLinks / setCrossSellProductLinks. Although the code executes without errors, the cross-sells do not appear in either the Magento admin (Product Edit Page -> Related Products, Up-Sells, and Cross-Sells) or the storefront (Cart page, etc.).
Below is the CrosssellManager.php class i'm using. The general workflow is:
- Retrieve the holster_sku and compatible_sku values from fit_builded.
- For each row, call a method that:
- Loads the Holster product.
- Obtains or initializes its extension attributes.
- Fetches existing cross-sell links, merges them with the new ones, and saves the product.
I have also tried clearing caches, but the cross-sell links still do not show up.
If anyone has encountered a similar issue or knows what might be missing, your insights would be greatly appreciated!
CrosssellManager.php :
<?php
namespace Compatibility\AutoCrosssell\Model;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Model\Product\LinkFactory;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Api\ExtensionAttributesFactory;
class CrosssellManager
{
/**
* @var ProductRepositoryInterface
*/
protected $productRepository;
/**
* @var LinkFactory
*/
protected $linkFactory;
/**
* @var ResourceConnection
*/
protected $resourceConnection;
/**
* @var ExtensionAttributesFactory
*/
private $extensionFactory;
public function __construct(
ProductRepositoryInterface $productRepository,
LinkFactory $linkFactory,
ResourceConnection $resourceConnection,
ExtensionAttributesFactory $extensionFactory
) {
$this->productRepository = $productRepository;
$this->linkFactory = $linkFactory;
$this->resourceConnection = $resourceConnection;
$this->extensionFactory = $extensionFactory;
}
private function getExtensionAttributesFactory()
{
return $this->extensionFactory;
}
/**
* Ejecuta la asignación de cross-sells a partir de la tabla fit_builded
*/
public function assignCrossSells()
{
$connection = $this->resourceConnection->getConnection();
$tableName = $this->resourceConnection->getTableName('fit_builded');
$select = $connection->select()->from($tableName, ['holster_sku', 'compatible_sku']);
$rows = $connection->fetchAll($select);
foreach ($rows as $row) {
$holsterSku = $row['holster_sku'];
$compatibleSkusStr = $row['compatible_sku'];
// Separar los SKUs por comas
$compatibleSkus = array_map('trim', explode(',', $compatibleSkusStr));
// Asignar cross-sell
$this->assignCrossSellsToHolster($holsterSku, $compatibleSkus);
}
}
/**
* Asigna la lista de SKUs compatibles como cross-sell de un holster dado
*
* @param string $holsterSku
* @param array $compatibleSkus
*/
protected function assignCrossSellsToHolster($holsterSku, array $compatibleSkus)
{
try {
// 1) Cargar el producto Holster
$holsterProduct = $this->productRepository->get($holsterSku);
// 2) Obtener (o inicializar) los extension attributes
$extensionAttributes = $holsterProduct->getExtensionAttributes();
if (!$extensionAttributes) {
$extensionAttributes = $this->getExtensionAttributesFactory()->create(\Magento\Catalog\Api\Data\ProductInterface::class);
}
// 3) Obtener la lista actual de cross-sell links
$existingCrossSellLinks = $extensionAttributes->getCrossSellLinks() ?: [];
// Guardar en un array los SKUs que ya están asociados como cross-sell
$existingSkus = [];
foreach ($existingCrossSellLinks as $link) {
$existingSkus[] = $link->getLinkedProductSku();
}
// 4) Construir nuevos links para los SKUs que no estén ya en cross-sell
$newLinks = [];
foreach ($compatibleSkus as $sku) {
if (!in_array($sku, $existingSkus)) {
// Crear un link de tipo cross-sell
$link = $this->linkFactory->create();
$link->setSku($holsterProduct->getSku()); // SKU del producto "padre"
$link->setLinkedProductSku($sku); // SKU del producto relacionado
$link->setLinkType(\Magento\Catalog\Model\Product\Link::LINK_TYPE_CROSSSELL);
// $link->setPosition(0); // si quieres controlar su orden
$newLinks[] = $link;
}
}
// 5) Combinar los enlaces existentes con los nuevos
$finalCrossSellLinks = array_merge($existingCrossSellLinks, $newLinks);
// 6) Setear la lista final de cross-sells en los extension attributes
$extensionAttributes->setCrossSellLinks($finalCrossSellLinks);
$holsterProduct->setExtensionAttributes($extensionAttributes);
// 7) Guardar el producto
$this->productRepository->save($holsterProduct);
} catch (NoSuchEntityException $ex) {
// SKU del Holster no existe
} catch (\Exception $ex) {
// Manejo genérico de errores
}
}
}