6

I am needing to add another related products attribute to associate multiple other products.

I have found a solution for Magento 1: http://inchoo.net/magento/how-to-add-custom-product-relations-in-magento/

However, I wishing to implement this in Magento 2. Any guidance is appreciated.

3

1 Answer 1

6

Magento 2.0.x version:

First of all, there are some notes:

--Replaced event catalog_product_prepare_save with controller product initialization helper that can be customized via plugins - Vendor/ProductRelation/Model/Product/Initialization/Helper/ProductLinks.php.

--Replaced event catalog_model_product_duplicate with composite Product\Copier model. However, we don't use this event.

--We need to override the \Magento\Catalog\Model\Product\Link\Proxy - can find it under var/generation.

Now, let's start to build our module:

Installer script

Create a setup script to add new product link type:

#app/code/Vendor/ProductRelation/Setup/UpgradeData.php

<?php

namespace Vendor\ProductRelation\Setup;

use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\UpgradeDataInterface;

class UpgradeData implements UpgradeDataInterface
{
    /**
     * Upgrades DB for a module
     *
     * @param ModuleDataSetupInterface $setup
     * @param ModuleContextInterface $context
     * @return void
     */
    public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {
        $setup->startSetup();

        //Add store_id to catalog_product_link
        $productLinkTable = 'catalog_product_link';
        $setup->getConnection()
            ->addColumn(
                    $setup->getTable($productLinkTable),
                    'store_id',
                    [
                        'type' => \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
                        'nullable' => false,
                        'default' => 0,
                        'comment' => 'Store Id'
                    ]
            );


        /**
         * Install product link types
         */
        $data = [
            ['link_type_id' => \Vendor\ProductRelation\Model\Catalog\Product\Link::LINK_TYPE_CUSTOMTYPE, 'code' => 'customtype']
        ];

        foreach ($data as $bind) {
            $setup->getConnection()
                ->insertForce($setup->getTable('catalog_product_link_type'), $bind);
        }

        /**
         * install product link attributes
         */
        $data = [
            [
                'link_type_id' => \Vendor\ProductRelation\Model\Catalog\Product\Link::LINK_TYPE_CUSTOMTYPE,
                'product_link_attribute_code' => 'position',
                'data_type' => 'int',
            ]
        ];

        $setup->getConnection()
            ->insertMultiple($setup->getTable('catalog_product_link_attribute'), $data);

        $setup->endSetup();
    }
}

Admin interface

Add new tab to product information

#app/code/Vendor/ProductRelation/view/adminhtml/layout/catalog_product_new.xml

<?xml version="1.0"?>

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="product_tabs">
            <block class="Vendor\ProductRelation\Block\Adminhtml\Catalog\Product\Edit\Tab" name="product.customtype">
                <arguments>
                    <argument name="label" xsi:type="string" translate="true">Custom Type</argument>
                    <argument name="url" xsi:type="url" path="catalog/product/customtype">
                        <param name="_current">1</param>
                    </argument>
                    <argument name="class" xsi:type="string">ajax</argument>
                    <argument name="group_code" xsi:type="string">advanced</argument>
                </arguments>
            </block>
            <action method="addTab">
                <argument name="name" xsi:type="string">product-customtype</argument>
                <argument name="block" xsi:type="string">product.customtype</argument>
            </action>
        </referenceBlock>
    </body>
</page>

In this layout, we need to define the url path path="catalog/product/customtype" to load the grid. We will create this controller later. - Vendor/ProductRelation/Controller/Adminhtml/Product/Customtype.php

The block for rendering the tab.

<?php

namespace Vendor\ProductRelation\Block\Adminhtml\Catalog\Product\Edit;

class Tab extends \Magento\Backend\Block\Widget\Tab
{
    /**
     * @param \Magento\Backend\Block\Template\Context $context
     * @param array $data
     */
    public function __construct(
        \Magento\Backend\Block\Template\Context $context,
        array $data = []
    ) {
        parent::__construct($context, $data);
    }
}

Creating the controllers:

--Get custom products grid and serializer block

#app/code/Vendor/ProductRelation/Controller/Adminhtml/Product/Customtype.php

<?php

namespace Vendor\ProductRelation\Controller\Adminhtml\Product;

class Customtype extends \Magento\Catalog\Controller\Adminhtml\Product
{
    /**
     * @var \Magento\Framework\View\Result\LayoutFactory
     */
    protected $resultLayoutFactory;

    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Magento\Catalog\Controller\Adminhtml\Product\Builder $productBuilder,
        \Magento\Framework\View\Result\LayoutFactory $resultLayoutFactory
    )
    {
        parent::__construct($context, $productBuilder);
        $this->resultLayoutFactory = $resultLayoutFactory;
    }

    /**
     * Get products grid and serializer block
     *
     * @return \Magento\Framework\View\Result\Layout
     */
    public function execute()
    {
        $this->productBuilder->build($this->getRequest());
        $resultLayout = $this->resultLayoutFactory->create();
        $resultLayout->getLayout()->getBlock('catalog.product.edit.tab.customtype')
            ->setProductsCustomtype($this->getRequest()->getPost('products_customtype', null));
        return $resultLayout;
    }
}

--Get custom products grid

#app/code/Vendor/ProductRelation/Controller/Adminhtml/Product/CustomtypeGrid.php

<?php

namespace Vendor\ProductRelation\Controller\Adminhtml\Product;

class CustomtypeGrid extends Customtype
{

}

Create two layouts for two controllers above:

#app/code/Vendor/ProductRelation/view/adminhtml/layout/catalog_product_customtype.xml

<?xml version="1.0"?>

<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/layout_generic.xsd">
    <container name="root" label="Root">
        <block class="Vendor\ProductRelation\Block\Adminhtml\Catalog\Product\Edit\Tab\Customtype"
               name="catalog.product.edit.tab.customtype"/>
        <block class="Magento\Backend\Block\Widget\Grid\Serializer" name="customtype_grid_serializer">
            <arguments>
                <argument name="input_names" xsi:type="string">position</argument>
                <argument name="grid_block" xsi:type="string">catalog.product.edit.tab.customtype</argument>
                <argument name="callback" xsi:type="string">getSelectedCustomtypeProducts</argument>
                <argument name="input_element_name" xsi:type="string">links[customtype]</argument>
                <argument name="reload_param_name" xsi:type="string">products_customtype</argument>
            </arguments>
        </block>
    </container>
</layout>


#app/code/Vendor/ProductRelation/view/adminhtml/layout/catalog_product_customtypegrid.xml

<?xml version="1.0"?>

<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/layout_generic.xsd">
    <container name="root" label="Root">
        <block class="Vendor\ProductRelation\Block\Adminhtml\Catalog\Product\Edit\Tab\Customtype"
               name="catalog.product.edit.tab.customtype"/>
    </container>
</layout>

Get custom products grid block:

app/code/Vendor/ProductRelation/Block/Adminhtml/Catalog/Product/Edit/Tab/Customtype.php

Here.

If continuing to post more code lines here, this topic is too long. So, I will focus on main ideas.

One of the most important things is di.xml

#app/code/Vendor/ProductRelation/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">

    <preference for="Magento\Catalog\Model\Product"
                type="Vendor\ProductRelation\Model\Product"/>
    <preference for="Magento\Catalog\Model\Product\Link"
                type="Vendor\ProductRelation\Model\Catalog\Product\Link"/>
    <preference for="Magento\Catalog\Model\Product\Link\Proxy"
                type="Vendor\ProductRelation\Model\Catalog\Product\Link\Proxy"/>

    <type name="Magento\Catalog\Model\Product\Initialization\Helper\ProductLinks">
        <plugin sortOrder="4" name="afterInitializeLinks"
                type="Vendor\ProductRelation\Model\Product\Initialization\Helper\ProductLinks"/>
    </type>
</config>

As we can, the same logic on Magento 1, we will override Product and Product Link model. However, we also need to override the Product Link Proxy and used Plugin to override initializeLinks method.

There is another note. We can reuse the admin catalog routes:

#app/code/Vendor/ProductRelation/etc/adminhtml/routes.xml

<?xml version="1.0"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="admin">
        <route id="catalog">
            <module name="Vendor_ProductRelation" before="Magento_Catalog" />
        </route>
    </router>
</config>

Full source code for this extension is available from GitHub repository page.

Magento 2.1.x or later version

I will update later on this page or Github.

14
  • Thank you for the response. I had already started to try and implement my own module, however, I will be using yours to improve mine and finish it. I should mention that I am using Magento 2.1. I will update once I hopefully have it working.
    – adamhs
    Commented Sep 23, 2016 at 1:25
  • Yeah! @adamhs you can contribute to my module on Github - I'm going to make my module works on Magento 2.1 version also. Commented Sep 23, 2016 at 1:56
  • Hi Khoa, I have the products table in the adminhtml and am able to select the products. I have also updated the schema to have the customtype link. However, I am stuck in getting the products to save to the database. When I click Save on the product page it is not being added to database.
    – adamhs
    Commented Sep 23, 2016 at 4:54
  • You're working on 2.1 version? Commented Sep 23, 2016 at 4:56
  • Sorry should have mention. Yep 2.1
    – adamhs
    Commented Sep 23, 2016 at 4:59

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.