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.