Magento – Magento2 add new field in bundle item (in option selection)

bundle-productmagento2

i'am new with magento!

I want to add an image-upload-field to a bundle-product in Magento2. I need this field in the selection-items of the options of the bundle items (see the following image for better understanding):

enter image description here

Adding a field in the database via my own module was no problem. Now I need to add the field in the adminhtml-template and the selection-model but I don't know how. I tried to change the original files of Magento to check whether I have found the correct files to change/overwrite with my own module but the changes have no effect. I think I need to change this files:

  • \vendor\magento\module-bundle\view\adminhtml\templates\product\edit\bundle\option\selection.phtml
  • \vendor\magento\module-bundle\Block\Adminhtml\Catalog\Product\Edit\Tab\Bundle\Option\Selection.php

I don't know why my changes doesn't affect the output-form (I added something like "abc" in the phtml-template, cleared the whole magento-cache and also the browser-cache). I thought I can extend the template like in this post (but changing the selection-files instead of the option-files): https://stackoverflow.com/questions/24097689/magento-add-another-new-field-in-bundle-item

What am I doing wrong? Do I edit the wrong files?

[edit]

Ok, now i have get the field inside the BundlePanel. The files above were the wrong place to add this field. Ok fine. The right place is:

\Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundlePanel.php

Via Plugin in my own new module, i have added some new field "custom_text".

Me\HelloWorld\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">
    <type name="Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundlePanel">
        <plugin name="bundle_panel_newfield" type="Me\HelloWorld\Plugin\Bundle\Ui\DataProvider\Product\Form\Modifier\BundlePanel" sortOrder="1"/>
    </type>
</config>

Me\HelloWorld\Plugin\Bundle\Ui\DataProvider\Product\Form\Modifier\BundlePanel.php

<?php
namespace Me\HelloWorld\Plugin\Bundle\Ui\DataProvider\Product\Form\Modifier;

use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier;
use Magento\Ui\Component\Form;

class BundlePanel
{
    public function afterModifyMeta(
        \Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundlePanel $subject,
        $meta
    ) { 
        $meta['bundle-items']['children']['bundle_options']['children']['record']['children']['product_bundle_container']['children']['bundle_selections']['children']['record']['children']['custom_text'] = $this->getSelectionCustomText();

        return $meta;
    }

     /**
     * Get selection custom field structure
     *
     * @return array
     */
    protected function getSelectionCustomText()
    {
        return [
            'arguments' => [
                'data' => [
                    'config' => [
                        'componentType' => Form\Field::NAME,
                        'dataType' => Form\Element\DataType\Text::NAME,
                        'formElement' => Form\Element\Input::NAME,
                        'label' => __('Custom Text'),
                        'dataScope' => 'custom_text',
                        'sortOrder' => 120,
                    ],
                ],
            ],
        ];
    }
}

With the meta array i define and order the new field in the panel.

But now i have two new problems:

First Problem:
You can see in the image below that the plugin places the field correct before my basket image, but not the tableheader. For the th element the option sortOrder will be ignored and so the wrong columns are attached to the wrong titles. Why? Bug?

enter image description here

Second Problem:
After saving (database contains matching column "custom_text" in table "catalog_product_bundle_selection") the input value will not processed to the database and some manually database value will not appear in the BundlePanel. What is missing?

Thanks a lot!

Best Answer

Here is my solution:

app/code/MyVendor/MyModule/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">
    <type name="Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundleCustomOptions">
        <plugin
            sortOrder="100"
            name="MyVendorMyModuleBundleModifierCustomOptions"
            type="MyVendor\MyModule\Plugin\Ui\DataProvider\Product\Form\Modifier\BundleCustomOptions"/>
    </type>
    <type name="Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\Composite">
        <plugin
            sortOrder="100"
            name="MyVendorMyModuleModifierComposite"
            type="MyVendor\MyModule\Plugin\Ui\DataProvider\Product\Form\Modifier\Composite"/>
    </type>
    <preference for="Magento\Bundle\Model\LinkManagement" type="MyVendor\MyModule\Preference\Model\LinkManagement" />
</config>

app/code/MyVendor/MyModule/Plugin/Ui/DataProvider/Product/Form/Modifier/Composite.php

namespace MyVendor\MyModule\Plugin\Ui\DataProvider\Product\Form\Modifier;

use Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\Composite as BundleComposite;
use Magento\Catalog\Model\Locator\LocatorInterface;
use Magento\Bundle\Model\Product\Type;
use Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundlePanel;
use Magento\Bundle\Model\Selection;

class Composite
{

    // ...

    protected $locator;
    protected $selection;

    /**
     * @param LocatorInterface $locator
     * @param Selection $selection
     */
    public function __construct(
        LocatorInterface $locator,
        Selection $selection
    ) {
        $this->locator = $locator;
        $this->selection = $selection;
    }

    public function afterModifyData(BundleComposite $subject, array $data)
    {
        $product = $this->locator->getProduct();
        $modelId = $product->getId();
        $isBundleProduct = $product->getTypeId() === Type::TYPE_CODE;
        if ($isBundleProduct && $modelId) {
            foreach ($data[$modelId][BundlePanel::CODE_BUNDLE_OPTIONS][BundlePanel::CODE_BUNDLE_OPTIONS] as &$option) {
                foreach ($option['bundle_selections'] as &$selection) {
                    $this->selection->load($selection['selection_id']);
                    $selection['custom_field'] = $this->selection->getCustomField();
                }
            }
        }

        return $data;
    }

    // ...

}

app/code/MyVendor/MyModule/Plugin/Ui/DataProvider/Product/Form/Modifier/BundleCustomOptions.php

namespace MyVendor\MyModule\Plugin\Ui\DataProvider\Product\Form\Modifier;

use Magento\Ui\Component\Form\Field;
use Magento\Ui\Component\Form\Element\Select;
use Magento\Ui\Component\Form\Element\DataType\Text;
use Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundleCustomOptions as MagentoBundleCustomOptions;

class BundleCustomOptions
{

    // ...

    const FIELD_CUSTOM_FIELD_OPTION_NAME = 'custom_field';

    public function afterModifyMeta(MagentoBundleCustomOptions $subject, array $meta)
    {
        if (isset($meta['bundle-items']['children']['bundle_options']['children']['record']['children']['product_bundle_container']['children']['bundle_selections']['children']['record']['children'])) {


            $meta['bundle-items']['children']['bundle_options']['children']['record']['children']['product_bundle_container']['children']['bundle_selections']['children']['record']['children'][static::FIELD_CUSTOM_FIELD_OPTION_NAME] = $this->getCuststomFieldOptionFieldConfig(125);


            // Reorder table headings

            $action_delete = $meta['bundle-items']['children']['bundle_options']['children']['record']['children']['product_bundle_container']['children']['bundle_selections']['children']['record']['children']['action_delete'];
            unset($meta['bundle-items']['children']['bundle_options']['children']['record']['children']['product_bundle_container']['children']['bundle_selections']['children']['record']['children']['action_delete']);
            $meta['bundle-items']['children']['bundle_options']['children']['record']['children']['product_bundle_container']['children']['bundle_selections']['children']['record']['children']['action_delete'] = $action_delete;

            // There should be more convenient way to reorder table headings

        }

        return $meta;
    }

    protected function getCuststomFieldOptionFieldConfig($sortOrder)
    {
        return [
            'arguments' => [
                'data' => [
                    'config' => [
                        'label' => __('Custom field'),
                        'componentType' => Field::NAME,
                        'formElement' => Select::NAME,
                        'dataScope' => static::FIELD_CUSTOM_FIELD_OPTION_NAME,
                        'dataType' => Text::NAME,
                        'sortOrder' => $sortOrder,
                        'default' => 'default',
                        'options' => [
                            [
                                'label' => __('Option 1'),
                                'value' => 'option_1',
                            ],
                            [
                                'label' => __('Option 2'),
                                'value' => 'option_2',
                            ],
                            [
                                'label' => __('Option 3'),
                                'value' => 'option_3',
                            ],
                        ],
                    ],
                ],
            ],
        ];
    }

    // ...

}

app/code/MyVendor/MyModule/Preference/Model/LinkManagement

namespace MyVendor\MyModule\Preference\Model;

class LinkManagement extends \Magento\Bundle\Model\LinkManagement
{

    // ....

    /**
     * @param \Magento\Bundle\Model\Selection $selectionModel
     * @param \Magento\Bundle\Api\Data\LinkInterface $productLink
     * @param string $linkedProductId
     * @param string $parentProductId
     * @return \Magento\Bundle\Model\Selection
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
     * @SuppressWarnings(PHPMD.NPathComplexity)
     */
    protected function mapProductLinkToSelectionModel(
        \Magento\Bundle\Model\Selection $selectionModel,
        \Magento\Bundle\Api\Data\LinkInterface $productLink,
        $linkedProductId,
        $parentProductId
    ) {

        $selectionModel = parent::mapProductLinkToSelectionModel($selectionModel, $productLink, $linkedProductId, $parentProductId);
        if ($productLink->getCustomField() !== null) {
            $selectionModel->setCustomField($productLink->getCustomField());
        }

        return $selectionModel;
    }

    // ....
}

And don't forget to add custom_field field to DB table {prefix}_catalog_product_bundle_selection. Best way is via app/code/MyVendor/MyModule/Setup/UpgradeSchema.php

Notice I've used preference way for LinkManagement class override for deeper modification of bundle save process. For a question mentioned use case there would be better the plugin way.

Related Topic