3

I want to create a custom tab in back-end product edit module of Magento 2.1. Like this

http://i.stack.imgur.com/ih8Wb.png

I tried this and tried to know how Magento adds these tabs. But didn't find a solution.

Please help me find the correct solution.

2
3

Magento 2.1 :- You can use this code for simple custom tab add in product edit page. Create Own module and put below code in Vendor/Module/view/adminhtml/layout/catalog_product_new.xml

<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_form">
            <block class="Vendor\Module\Block\Adminhtml\Product\Edit\Tab\Welcome" name="product.welcome" as="custom-tab" >
                <arguments>
                    <argument name="config" xsi:type="array">
                        <item name="label" xsi:type="string" translate="true">Product Welcome</item>
                        <item name="collapsible" xsi:type="boolean">true</item>
                        <item name="opened" xsi:type="boolean">true</item>
                        <item name="sortOrder" xsi:type="string">2</item>
                        <item name="canShow" xsi:type="boolean">true</item>
                        <item name="componentType" xsi:type="string">fieldset</item>
                    </argument>
                </arguments>
            </block>
        </referenceBlock>
    </body>
</page>

In block Vendor/Module/Block/Adminhtml/Product/Edit/Tab/Welcome.php put below code.

<?php

namespace Vendor\Module\Block\Adminhtml\Product\Edit\Tab;
use Magento\Backend\Block\Template\Context;
use Magento\Framework\Registry;

class Welcome extends \Magento\Framework\View\Element\Template
{
    protected $_template = 'catalog/product/edit/welcome.phtml';

    protected $_coreRegistry = null;

    public function __construct(
        Context $context,
        Registry $registry,
        array $data = []
    )
    {
        $this->_coreRegistry = $registry;
        parent::__construct($context, $data);
    }

    public function getProduct()
    {
        return $this->_coreRegistry->registry('current_product');
    }
}

In templates Vendor/Module/view/adminhtml/templates/catalog/product/edit/welcome.phtml put below code.

<div class="welcome">
    <?php echo __('Welcome !'); ?>
</div>

Now Check back-end product module of Magento 2.1. It's working perfect.

3
2

Create the di.xml in adminhtml scope:

app/code/Vendor/Module/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">
    <virtualType name="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Pool">
        <arguments>
            <argument name="modifiers" xsi:type="array">
                <item name="mageworx-option-dependency" xsi:type="array">
                    <item name="class" xsi:type="string">Vendor\Module\Ui\DataProvider\Product\Form\Modifier\Name</item>
                    <item name="sortOrder" xsi:type="number">71</item>
                </item>
            </argument>
        </arguments>
    </virtualType>
</config>

Then add your Vendor\Module\Ui\DataProvider\Product\Form\Modifier\Name:

app/code/Vendor/Module/Ui/DataProvider/Product/Form/Modifier/Name.php

<?php
namespace Vendor\Module\Ui\DataProvider\Product\Form\Modifier;

use Magento\Catalog\Model\Locator\LocatorInterface;
use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier;
use Magento\Framework\Stdlib\ArrayManager;
use Magento\Framework\UrlInterface;
use Magento\Ui\Component\Container;
use Magento\Ui\Component\Form\Fieldset;

class Name extends AbstractModifier
{

    const SAMPLE_FIELDSET_NAME = 'custom_fieldset';
    const SAMPLE_FIELD_NAME = 'sample_field';

    /**
     * @var \Magento\Catalog\Model\Locator\LocatorInterface
     */
    protected $locator;

    /**
     * @var ArrayManager
     */
    protected $arrayManager;

    /**
     * @var UrlInterface
     */
    protected $urlBuilder;

    /**
     * @var array
     */
    protected $meta = [];

    /**
     * @param LocatorInterface $locator
     * @param ArrayManager $arrayManager
     * @param UrlInterface $urlBuilder
     */
    public function __construct(
        LocatorInterface $locator,
        ArrayManager $arrayManager,
        UrlInterface $urlBuilder
    ) {
        $this->locator = $locator;
        $this->arrayManager = $arrayManager;
        $this->urlBuilder = $urlBuilder;
    }

    public function modifyData(array $data)
    {
        return array_replace_recursive(
            $data,
            [
                $this->locator->getProduct()->getId() => [
                    static::DATA_SOURCE_DEFAULT => [
                        static::SAMPLE_FIELD_NAME => $this->locator->getProduct()->getData(static::SAMPLE_FIELD_NAME),
                    ]
                ]
            ]
        );
    }

    public function modifyMeta(array $meta)
    {
        $this->meta = $meta;
        $this->addFieldset();

        return $this->meta;
    }

    protected function addFieldset()
    {
        $this->meta = array_replace_recursive(
            $this->meta,
            [
                static::SAMPLE_FIELDSET_NAME => [
                    'arguments' => [
                        'data' => [
                            'config' => [
                                'label' => __('Sample Fieldset'),
                                'componentType' => Fieldset::NAME,
                                'dataScope' => 'data.product',
                                'collapsible' => true,
                                'sortOrder' => 10,
                            ],
                        ],
                    ],
                    'children' => [
                        'header_container' => $this->getHeaderContainerConfig(10),
                        // Add children here
                    ],
                ],
            ]
        );

        return $this;
    }

    /**
     * Get config for header container
     *
     * @param int $sortOrder
     * @return array
     */
    protected function getHeaderContainerConfig($sortOrder)
    {
        return [
            'arguments' => [
                'data' => [
                    'config' => [
                        'label' => null,
                        'formElement' => Container::NAME,
                        'componentType' => Container::NAME,
                        'sortOrder' => $sortOrder,
                        'content' => __('Sample content.'),
                    ],
                ],
            ],
            'children' => [
                'sample_container' => $this->getSampleContainer(10),
            ],
        ];
    }

    protected function getSampleContainer($sortOrder)
    {
        return [
            'arguments' => [
                'data' => [
                    'config' => [
                        'label' => null,
                        'formElement' => Fieldset::NAME,
                        'componentType' => Fieldset::NAME,
                        'sortOrder' => $sortOrder,
                        'additionalClasses' => 'admin__field-wide',
                    ],
                ],
            ],
            'children' => [
                static::SAMPLE_FIELD_NAME => $this->getSampleFieldConfig(10)
            ],
        ];
    }

    protected function getSampleFieldConfig($sortOrder)
    {
        return [
            'arguments' => [
                'data' => [
                    'config' => [
                        'label' => __('Sample Field'),
                        'componentType' => \Magento\Ui\Component\Form\Field::NAME,
                        'formElement' => \Magento\Ui\Component\Form\Element\Input::NAME,
                        'dataScope' => static::SAMPLE_FIELD_NAME,
                        'dataType' => \Magento\Ui\Component\Form\Element\DataType\Text::NAME,
                        'sortOrder' => $sortOrder,
                        'validation' => [
                            'letters-with-basic-punc' => true,
                        ],
                        'required' => true,
                    ],
                ],
            ],
            'children' => [],
        ];
    }
}

Your new empty fieldset with example field:

example fieldset with field

Validation is working:

validation is working

When saving a product your data will be automatically applied to it:

save debug

See: \Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper::initializeFromData(). Thus if the product has the respective field, then your value will be saved. Another way you need to use an observer or some kind of plugins (before the product saving process).

You can take more examples from:

vendor/magento/module-catalog/Ui/DataProvider/Product/Form/Modifier

To get the current product you can use the $this->locator->getProduct().

4
  • I didn't understand what is Name field in Dependency.php? and do i need to create a separate name class?
    – HungryDB
    Aug 16 '16 at 8:51
  • @HungryDB Name - is a modifier class name (Dependency.php should be the Name.php, it's a typo, sorry). So you need to create app/code/Vendor/Module/Ui/DataProvider/Product/Form/Modifier/Name.php with a namespace Vendor\Module\Ui\DataProvider\Product\Form\Modifier and a class name Name. Aug 16 '16 at 8:56
  • @HungryDB I've updated the code (updates modifyData method; adds constants). I can share a full example from the my module, but there a lot of code (more than 1k lines only in modifier). Aug 16 '16 at 9:05
  • @SiarheyUchukhlebau, Thanks for your great guide... how can I have two columns under sample fieldset?
    – Zinat
    Nov 2 '16 at 8:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.