Why it happens
This happens when there are inconsistent positions for the related products. This might happen after a migration from magento 1. It was the case for me also.
The problem is cause by the method Magento\Catalog\Model\ProductLink\CollectionProvider::getCollection()
.
That's the one responsible for retrieving the products.
The part that does not behave as expected is this:
foreach ($output as $item) {
$itemPosition = $item['position'];
if (!isset($sorterItems[$itemPosition])) {
$sorterItems[$itemPosition] = $item;
} else {
$newPosition = $itemPosition + 1;
$sorterItems[$newPosition] = $item;
}
}
Let's take 2 cases.
THe one that works:
Let's say
$output = [
['position' => 0],
['position' => 2],
['position' => 1],
];
(I ignored the other values for the related items as they are not important).
After running the code above this results in
Array
(
[0] => Array
(
[position] => 0
)
[2] => Array
(
[position] => 2
)
[1] => Array
(
[position] => 1
)
)
which is later sorted by the ksort
function.
So 3 items on input, 3 on output. So far so good.
But let's take this case:
$output = [
['position' => 0], // or null
['position' => 1],
['position' => 0], // or null
];
The same piece of code returns:
Array
(
[0] => Array
(
[position] => 0
)
[1] => Array
(
[position] => 0
)
)
So 3 items in, 2 out.
Solution
I solve this by rewriting the method mentioned above and ensuring there are no position clashes.
To override it, add this in the di.xml
file in one of your custom modules
<preference for="Magento\Catalog\Model\ProductLink\CollectionProvider" type="[Vendor]\[Module]\Model\ProductLink\CollectionProvider" />
Then create the class [Vendor]\[Module]\Model\ProductLink\CollectionProvider
in the file [Vendor]/[Module]/Model/ProductLink/CollectionProvider.php
The class should look like this:
<?php
namespace [Vendor]\[Module]\Model\ProductLink;
use Magento\Framework\Exception\NoSuchEntityException;
class CollectionProvider extends \Magento\Catalog\Model\ProductLink\CollectionProvider
{
/**
* Get product collection by link type
*
* @param \Magento\Catalog\Model\Product $product
* @param string $type
* @return array
* @throws NoSuchEntityException
*/
public function getCollection(\Magento\Catalog\Model\Product $product, $type)
{
if (!isset($this->providers[$type])) {
throw new NoSuchEntityException(__('Collection provider is not registered'));
}
$products = $this->providers[$type]->getLinkedProducts($product);
$converter = $this->converterPool->getConverter($type);
$output = [];
$sorterItems = [];
foreach ($products as $item) {
$output[$item->getId()] = $converter->convert($item);
}
foreach ($output as $item) {
$itemPosition = (int)$item['position'];
while (isset($sorterItems[$itemPosition])) {
$itemPosition++;
}
$sorterItems[$itemPosition] = $item;
}
ksort($sorterItems);
return $sorterItems;
}
}