How to create a custom product link type grid in Magento 2?
As we see how to add a custom product link type using Data Patch in the previous blog, now we will get an idea of how to create a custom product link type UI grid and how to change the label of the product type section in the admin add/edit product form.
>> Create di.xml file to define custom product link type
File Path: app/code/Mage2/CustomProductLink/etc/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<!-- Retrieve saved products in custom link type UI grid start -->
<preference for="Magento\Catalog\Model\Product"
type="Mage2\CustomProductLink\Model\Product"/>
<!-- Retrieve saved products in custom link type UI grid end -->
<!-- Inject LinkTypeProvider dependency start -->
<type name="Magento\Catalog\Model\Product\LinkTypeProvider">
<arguments>
<argument name="linkTypes" xsi:type="array">
<item name="parts" xsi:type="const">
Mage2\CustomProductLink\Model\Product::PARTS_LINK_TYPE_ID
</item>
</argument>
</arguments>
</type>
<!-- Inject LinkTypeProvider dependency end -->
<!-- Create custom link type model start -->
<preference for="Magento\Catalog\Model\Product\Link"
type="Mage2\CustomProductLink\Model\Product\Link"/>
<!-- Create custom link type model end -->
<!-- Inject CollectionProvider dependency start -->
<type name="Magento\Catalog\Model\ProductLink\CollectionProvider">
<arguments>
<argument name="providers" xsi:type="array">
<item name="parts" xsi:type="object">
Mage2\CustomProductLink\Model\ProductLink\CollectionProvider\PartsLinkProducts
</item>
</argument>
</arguments>
</type>
<!-- Inject CollectionProvider dependency end -->
<!-- Update product model class start -->
<type name="Mage2\CustomProductLink\Model\ProductLink\CollectionProvider\PartsLinkProducts">
<plugin name="update_parts_link_model" type="Mage2\CustomProductLink\Plugin\UpdatePartsLinkModel"/>
</type>
<!-- Update product model class end -->
</config>
>> Retrieve saved products in custom link type UI grid
File path: app/code/Mage2/CustomProductLink/Model/Product.php
<?php
declare(strict_types=1);
namespace Mage2\CustomProductLink\Model;
class Product extends \Magento\Catalog\Model\Product
{
public const PARTS_LINK_CODE = 'parts';
public const PARTS_LINK_TYPE_ID = 6;
/**
* Retrieve array of parts products
*
* @return array
*/
public function getPartsProducts(): array
{
if (!$this->hasPartsProducts()) {
$products = [];
foreach ($this->getPartsProductCollection() as $product) {
$products[] = $product;
}
$this->setPartsProducts($products);
}
return $this->getData('parts_products');
}
/**
* Retrieve parts products identifiers
*
* @return array
*/
public function getPartsIds(): array
{
if (!$this->hasPartsProductIds()) {
$ids = [];
foreach ($this->getPartsProducts() as $product) {
$ids[] = $product->getId();
}
$this->setPartsProductIds($ids);
}
return $this->getData('parts_product_ids');
}
/**
* Retrieve collection parts product
*
* @return \Magento\Catalog\Model\ResourceModel\Product\Link\Product\Collection
*/
public function getPartsProductCollection(): \Magento\Catalog\Model\ResourceModel\Product\Link\Product\Collection
{
$collection = $this->getLinkInstance()
->setLinkTypeId(self::PARTS_LINK_TYPE_ID)
->getProductCollection()
->setIsStrongMode();
$collection->setProduct($this);
return $collection;
}
/**
* Retrieve collection parts link
*
* @return \Magento\Catalog\Model\ResourceModel\Product\Link\Collection
*/
public function getPartsLinkCollection(): \Magento\Catalog\Model\ResourceModel\Product\Link\Collection
{
$collection = $this->getLinkInstance()->setLinkTypeId(self::PARTS_LINK_TYPE_ID)->getLinkCollection();
$collection->setProduct($this);
$collection->addLinkTypeIdFilter();
$collection->addProductIdFilter();
$collection->joinAttributes();
return $collection;
}
}
>> Create custom link type model
File Path: app/code/Mage2/CustomProductLink/Model/Product/Link.php
<?php
declare(strict_types=1);
namespace Mage2\CustomProductLink\Model\Product;
use Mage2\CustomProductLink\Model\Product;
class Link extends \Magento\Catalog\Model\Product\Link
{
/**
* Set parts link type id
*
* @return $this
*/
public function usePartsLinks(): static
{
$this->setLinkTypeId(Product::PARTS_LINK_TYPE_ID);
return $this;
}
}
>> Inject CollectionProvider dependency
File Path: app/code/Mage2/CustomProductLink/Model/ProductLink/CollectionProvider/PartsLinkProducts.php
<?php
declare(strict_types=1);
namespace Mage2\CustomProductLink\Model\ProductLink\CollectionProvider;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\ProductLink\CollectionProviderInterface;
class PartsLinkProducts implements CollectionProviderInterface
{
/**
* Get part products
*
* @param Product $product
* @return array
*/
public function getLinkedProducts(Product $product): array
{
$products = $product->getPartsProducts();
if (!isset($products)) {
return [];
}
return $products;
}
}
>> Update product model class
File Path: app/code/Mage2/CustomProductLink/Plugin/UpdatePartsLinkModel.php
<?php
declare(strict_types=1);
namespace Mage2\CustomProductLink\Plugin;
use Mage2\CustomProductLink\Model\ProductFactory;
use Mage2\CustomProductLink\Model\ProductLink\CollectionProvider\PartsLinkProducts;
class UpdatePartsLinkModel
{
/**
* @var ProductFactory
*/
private ProductFactory $productFactory;
/**
* @param ProductFactory $productFactory
*/
public function __construct(
ProductFactory $productFactory
) {
$this->productFactory = $productFactory;
}
/**
* @param PartsLinkProducts $subject
* @param object $product
* @return array
*/
public function beforeGetLinkedProducts(PartsLinkProducts $subject, object $product): array
{
$currentProduct = $this->productFactory->create()->load($product->getId());
return [$currentProduct];
}
}
>> Create di.xml file to create custom product link type UI grid
File Path: app/code/Mage2/CustomProductLink/etc/adminhtml/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<!-- Create custom product link type UI grid start -->
<preference for="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Related"
type="Mage2\CustomProductLink\Ui\DataProvider\Product\Form\Modifier\PartsRelated"/>
<type name="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Related">
<plugin name="update_modify_meta" type="Mage2\CustomProductLink\Plugin\UpdateModifyMeta"/>
</type>
<!-- Create custom product link type UI grid end -->
</config>
>> Add custom product link type scope
File Path: app/code/Mage2/CustomProductLink/Ui/DataProvider/Product/Form/Modifier/PartsRelated.php
<?php
declare(strict_types=1);
namespace Mage2\CustomProductLink\Ui\DataProvider\Product\Form\Modifier;
use Magento\Catalog\Api\ProductLinkRepositoryInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Helper\Image as ImageHelper;
use Magento\Catalog\Model\Locator\LocatorInterface;
use Magento\Catalog\Model\Product\Attribute\Source\Status;
use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Related;
use Magento\Eav\Api\AttributeSetRepositoryInterface;
use Magento\Framework\UrlInterface;
use Mage2\CustomProductLink\Model\Product;
class PartsRelated extends Related
{
public function __construct(
LocatorInterface $locator,
UrlInterface $urlBuilder,
ProductLinkRepositoryInterface $productLinkRepository,
ProductRepositoryInterface $productRepository,
ImageHelper $imageHelper,
Status $status,
AttributeSetRepositoryInterface $attributeSetRepository,
$scopeName = '',
$scopePrefix = ''
) {
parent::__construct($locator, $urlBuilder, $productLinkRepository, $productRepository, $imageHelper, $status,
$attributeSetRepository, $scopeName, $scopePrefix);
}
/**
* Retrieve all data scopes
*
* @return array
*/
protected function getDataScopes()
{
$data = parent::getDataScopes();
$data[] = Product::PARTS_LINK_CODE;
return $data;
}
}
>> Create a plugin to add parts product link config to product page
File Path: app/code/Mage2/CustomProductLink/Plugin/UpdateModifyMeta.php
<?php
declare(strict_types=1);
namespace Mage2\CustomProductLink\Plugin;
use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Related;
use Mage2\CustomProductLink\Model\Product;
use Magento\Ui\Component\Form\Fieldset;
class UpdateModifyMeta extends Related
{
/**
* After plugin to add parts product link config to product page
*
* @param Related $subject
* @param array $result
* @return array
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function afterModifyMeta(Related $subject, array $result): array
{
$result[Related::GROUP_RELATED]['children'][$this->scopePrefix . Product::PARTS_LINK_CODE] = $this->getPartsFieldset();
$result[Related::GROUP_RELATED]['arguments']['data']['config']['label'] =
__('Related Products, Up-Sells, Cross-Sells and Parts');
return $result;
}
/**
* Prepares config for the Parts products fieldset
*
* @return array
*/
protected function getPartsFieldset()
{
$content = __('Part products are customers parts related to a product.');
return [
'children' => [
'button_set' => $this->getButtonSet(
$content,
__('Add Part Products'),
$this->scopePrefix . Product::PARTS_LINK_CODE
),
'modal' => $this->getGenericModal(
__('Add Part Products'),
$this->scopePrefix . Product::PARTS_LINK_CODE
),
Product::PARTS_LINK_CODE => $this->getGrid($this->scopePrefix . Product::PARTS_LINK_CODE),
],
'arguments' => [
'data' => [
'config' => [
'additionalClasses' => 'admin__fieldset-section',
'label' => __('Part Products'),
'collapsible' => false,
'componentType' => Fieldset::NAME,
'dataScope' => '',
'sortOrder' => 100,
],
],
]
];
}
}
And that’s it, after creating all the above files, you need to run the following command:
bin/magento setup:upgrade
Then you can see a custom product link type UI grid like the following screenshot:

We hope this blog may be understandable and useful to you. You can email us at mage2developer@gmail.com if we missed anything or want to add any suggestions. We will respond to you as soon as possible. Happy to help 🙂

