cancel
Showing results for 
Search instead for 
Did you mean: 

Help please creating a custom product type extended from bundle product type in magento 2?

SOLVED
   Did you know you can see the translated content as per your choice?

Translation is in progress. Please check again after few minutes.

Help please creating a custom product type extended from bundle product type in magento 2?

i've extended bundle product type of magento 2 named Box product, everything works excepts, add option is not appearing in extended new type... Here are the files, Please check..

Arslan/BoxType/etc/product_types.xml

<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Catalog:etc/product_types.xsd">
<type label="Box Product" modelInstance="Arslan\BoxType\Model\Product\Type\Box" name="box" composite='true' indexPriority="40" sortOrder="50">
    <priceModel instance="Arslan\BoxType\Model\Product\Price"/>
    <allowedSelectionTypes>
        <type name="simple" />
        <type name="virtual" />
    </allowedSelectionTypes>
    <customAttributes>
        <attribute name="refundable" value="true"/>
    </customAttributes>
</type>

now the model Arslan/BoxType/Model/Product/Type/Box.php

<?php
 namespace Arslan\BoxType\Model\Product\Type;

class Box extends \Magento\Bundle\Model\Product\Type
{

    const TYPE_ID = 'box';

   /**
   * {@inheritdoc}
   */
  public function deleteTypeSpecificData(\Magento\Catalog\Model\Product $product)
{
    // method intentionally empty
}
}

Now Arslan/BoxType/Setup/InstallData.php

<?php


 namespace Arslan\BoxType\Setup;

 use Magento\Framework\Setup\InstallDataInterface;
 use Magento\Framework\Setup\ModuleContextInterface;
 use Magento\Framework\Setup\ModuleDataSetupInterface;
 use Magento\Eav\Setup\EavSetup;
 use Magento\Eav\Setup\EavSetupFactory;

 class InstallData implements InstallDataInterface
 {

    private $eavSetupFactory;

/**
 * Constructor
 *
 * @param \Magento\Eav\Setup\EavSetupFactory $eavSetupFactory
 */
public function __construct(EavSetupFactory $eavSetupFactory)
{
    $this->eavSetupFactory = $eavSetupFactory;
}

/**
 * {@inheritdoc}
 */
public function install(
    ModuleDataSetupInterface $setup,
    ModuleContextInterface $context
) {
    $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);

    // associate these attributes with new product type
    $fieldList = [
        'price',
        'special_price',
        'special_from_date',
        'special_to_date',
        'minimal_price',
        'cost',
        'tier_price',
        'weight',
        'price_type',
        'sku_type',
        'weight_type',
        'price_view',
        'shipment_type',
    ];

    // make these attributes applicable to new product type
    foreach ($fieldList as $field) {
        $applyTo = explode(
            ',',
            $eavSetup->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $field, 'apply_to')
        );
        if (!in_array(\Arslan\BoxType\Model\Product\Type\Box::TYPE_ID, $applyTo)) {
            $applyTo[] = \Arslan\BoxType\Model\Product\Type\Box::TYPE_ID;
            $eavSetup->updateAttribute(
                \Magento\Catalog\Model\Product::ENTITY,
                $field,
                'apply_to',
                implode(',', $applyTo)
            );
        }
    }
}
}

Now The Missing Portion in my extended product type is "add options" Please see the screenshot

i72Zv

1 ACCEPTED SOLUTION

Accepted Solutions

Re: Help please creating a custom product type extended from bundle product type in magento 2?

Hi @arslantS 

 

You need to change modifier. For example (vendor/magento/module-bundle/etc/adminhtml/di.xml):

<virtualType name="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Pool">
    <arguments>
        <argument name="modifiers" xsi:type="array">
            <item name="box" xsi:type="array">
                <item name="class" xsi:type="string">Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\Composite</item>
                <item name="sortOrder" xsi:type="number">180</item>
            </item>
            <item name="bundle_stock_data" xsi:type="array">
                <item name="class" xsi:type="string">Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\StockData</item>
                <item name="sortOrder" xsi:type="number">190</item>
            </item>
        </argument>
    </arguments>
</virtualType>

If you open Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\Composite then check 

/**
 * {@inheritdoc}
 */
public function modifyMeta(array $meta)
{
    if ($this->locator->getProduct()->getTypeId() === Type::TYPE_CODE) {
        foreach ($this->modifiers as $bundleClass) {
            /** @var ModifierInterface $bundleModifier */
            $bundleModifier = $this->objectManager->get($bundleClass);
            if (!$bundleModifier instanceof ModifierInterface) {
                throw new \InvalidArgumentException(
                    'Type "' . $bundleClass . '" is not an instance of ' . ModifierInterface::class
                );
            }
            $meta = $bundleModifier->modifyMeta($meta);
        }
    }
    return $meta;
}

Here Type::TYPE_CODE is 'bundle', that's why you missing some option.

 

So create your own modifier and modify type code will working.

 

Create Arslan/BoxType/etc/adminhtml/di.xml 

<virtualType name="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Pool">
    <arguments>
        <argument name="modifiers" xsi:type="array">
            <item name="box" xsi:type="array">
                <item name="class" xsi:type="string">Arslan\BoxType\Ui\DataProvider\Product\Form\Modifier\Composite</item>
                <item name="sortOrder" xsi:type="number">180</item>
            </item>
            <item name="bundle_stock_data" xsi:type="array">
                <item name="class" xsi:type="string">Arslan\BoxType\Ui\DataProvider\Product\Form\Modifier\StockData</item>
                <item name="sortOrder" xsi:type="number">190</item>
            </item>
        </argument>
    </arguments>
</virtualType>
<type name="Arslan\BoxType\Ui\DataProvider\Product\Form\Modifier\Composite">
<arguments>
<argument name="modifiers" xsi:type="array">
<item name="bundleSku" xsi:type="string">Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundleSku</item>
<item name="bundlePrice" xsi:type="string">Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundlePrice</item>
<item name="bundleWeight" xsi:type="string">Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundleWeight</item>
<item name="bundleQuantity" xsi:type="string">Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundleQuantity</item>
<item name="bundleAdvancedPricing" xsi:type="string">Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundleAdvancedPricing</item>
<item name="bundlePanel" xsi:type="string">Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundlePanel</item>
<item name="bundleCustomOptions" xsi:type="string">Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundleCustomOptions</item>
</argument>
</arguments>
</type>

Create Arslan\BoxType\Ui\DataProvider\Product\Form\Modifier\Composite.php 

namespace Arslan\BoxType\Ui\DataProvider\Product\Form\Modifier;

use Magento\Catalog\Model\Locator\LocatorInterface;
use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier;
use Magento\Framework\ObjectManagerInterface;
use Magento\Ui\DataProvider\Modifier\ModifierInterface;
use Magento\Bundle\Api\ProductOptionRepositoryInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundlePanel;

/**
* Class Bundle customizes Bundle product creation flow
*/
class Composite extends AbstractModifier
{
/**
* @var LocatorInterface
*/
protected $locator;

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

/**
* Object Manager
*
* @var ObjectManagerInterface
*/
protected $objectManager;

/**
* @var ProductOptionRepositoryInterface
*/
protected $optionsRepository;

/**
* @var ProductRepositoryInterface
*/
protected $productRepository;

/**
* @param LocatorInterface $locator
* @param ObjectManagerInterface $objectManager
* @param ProductOptionRepositoryInterface $optionsRepository
* @param ProductRepositoryInterface $productRepository
* @param array $modifiers
*/
public function __construct(
LocatorInterface $locator,
ObjectManagerInterface $objectManager,
ProductOptionRepositoryInterface $optionsRepository,
ProductRepositoryInterface $productRepository,
array $modifiers = []
) {
$this->locator = $locator;
$this->objectManager = $objectManager;
$this->optionsRepository = $optionsRepository;
$this->productRepository = $productRepository;
$this->modifiers = $modifiers;
}

/**
* {@inheritdoc}
*/
public function modifyMeta(array $meta)
{
if ($this->locator->getProduct()->getTypeId() === 'box') {
foreach ($this->modifiers as $bundleClass) {
/** @var ModifierInterface $bundleModifier */
$bundleModifier = $this->objectManager->get($bundleClass);
if (!$bundleModifier instanceof ModifierInterface) {
throw new \InvalidArgumentException(
'Type "' . $bundleClass . '" is not an instance of ' . ModifierInterface::class
);
}
$meta = $bundleModifier->modifyMeta($meta);
}
}
return $meta;
}

/**
* {@inheritdoc}
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
*/
public function modifyData(array $data)
{
/** @var \Magento\Catalog\Api\Data\ProductInterface $product */
$product = $this->locator->getProduct();
$modelId = $product->getId();
$isBundleProduct = $product->getTypeId() === 'box';
if ($isBundleProduct && $modelId) {
$data[$modelId][BundlePanel::CODE_BUNDLE_OPTIONS][BundlePanel::CODE_BUNDLE_OPTIONS] = [];
/** @var \Magento\Bundle\Api\Data\OptionInterface $option */
foreach ($this->optionsRepository->getList($product->getSku()) as $option) {
$selections = [];
/** @var \Magento\Bundle\Api\Data\LinkInterface $productLink */
foreach ($option->getProductLinks() as $productLink) {
$linkedProduct = $this->productRepository->get($productLink->getSku());
$integerQty = 1;
if ($linkedProduct->getExtensionAttributes()->getStockItem()) {
if ($linkedProduct->getExtensionAttributes()->getStockItem()->getIsQtyDecimal()) {
$integerQty = 0;
}
}
$selections[] = [
'selection_id' => $productLink->getId(),
'option_id' => $productLink->getOptionId(),
'product_id' => $linkedProduct->getId(),
'name' => $linkedProduct->getName(),
'sku' => $linkedProduct->getSku(),
'is_default' => ($productLink->getIsDefault()) ? '1' : '0',
'selection_price_value' => $productLink->getPrice(),
'selection_price_type' => $productLink->getPriceType(),
'selection_qty' => $integerQty ? (int)$productLink->getQty() : $productLink->getQty(),
'selection_can_change_qty' => $productLink->getCanChangeQuantity(),
'selection_qty_is_integer' => (bool)$integerQty,
'position' => $productLink->getPosition(),
'delete' => '',
];
}
$data[$modelId][BundlePanel::CODE_BUNDLE_OPTIONS][BundlePanel::CODE_BUNDLE_OPTIONS][] = [
'position' => $option->getPosition(),
'option_id' => $option->getOptionId(),
'title' => $option->getTitle(),
'default_title' => $option->getDefaultTitle(),
'type' => $option->getType(),
'required' => ($option->getRequired()) ? '1' : '0',
'bundle_selections' => $selections,
];
}
}

return $data;
}
}

 

-----
If Issue Solved, Click Kudos and Accept As solutions.
Sohel Rana, 7x Magento 2, 2x Magento 1 Certified

View solution in original post

6 REPLIES 6

Re: Help please creating a custom product type extended from bundle product type in magento 2?

Hi @arslantS 

 

You need to change modifier. For example (vendor/magento/module-bundle/etc/adminhtml/di.xml):

<virtualType name="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Pool">
    <arguments>
        <argument name="modifiers" xsi:type="array">
            <item name="box" xsi:type="array">
                <item name="class" xsi:type="string">Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\Composite</item>
                <item name="sortOrder" xsi:type="number">180</item>
            </item>
            <item name="bundle_stock_data" xsi:type="array">
                <item name="class" xsi:type="string">Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\StockData</item>
                <item name="sortOrder" xsi:type="number">190</item>
            </item>
        </argument>
    </arguments>
</virtualType>

If you open Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\Composite then check 

/**
 * {@inheritdoc}
 */
public function modifyMeta(array $meta)
{
    if ($this->locator->getProduct()->getTypeId() === Type::TYPE_CODE) {
        foreach ($this->modifiers as $bundleClass) {
            /** @var ModifierInterface $bundleModifier */
            $bundleModifier = $this->objectManager->get($bundleClass);
            if (!$bundleModifier instanceof ModifierInterface) {
                throw new \InvalidArgumentException(
                    'Type "' . $bundleClass . '" is not an instance of ' . ModifierInterface::class
                );
            }
            $meta = $bundleModifier->modifyMeta($meta);
        }
    }
    return $meta;
}

Here Type::TYPE_CODE is 'bundle', that's why you missing some option.

 

So create your own modifier and modify type code will working.

 

Create Arslan/BoxType/etc/adminhtml/di.xml 

<virtualType name="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Pool">
    <arguments>
        <argument name="modifiers" xsi:type="array">
            <item name="box" xsi:type="array">
                <item name="class" xsi:type="string">Arslan\BoxType\Ui\DataProvider\Product\Form\Modifier\Composite</item>
                <item name="sortOrder" xsi:type="number">180</item>
            </item>
            <item name="bundle_stock_data" xsi:type="array">
                <item name="class" xsi:type="string">Arslan\BoxType\Ui\DataProvider\Product\Form\Modifier\StockData</item>
                <item name="sortOrder" xsi:type="number">190</item>
            </item>
        </argument>
    </arguments>
</virtualType>
<type name="Arslan\BoxType\Ui\DataProvider\Product\Form\Modifier\Composite">
<arguments>
<argument name="modifiers" xsi:type="array">
<item name="bundleSku" xsi:type="string">Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundleSku</item>
<item name="bundlePrice" xsi:type="string">Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundlePrice</item>
<item name="bundleWeight" xsi:type="string">Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundleWeight</item>
<item name="bundleQuantity" xsi:type="string">Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundleQuantity</item>
<item name="bundleAdvancedPricing" xsi:type="string">Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundleAdvancedPricing</item>
<item name="bundlePanel" xsi:type="string">Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundlePanel</item>
<item name="bundleCustomOptions" xsi:type="string">Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundleCustomOptions</item>
</argument>
</arguments>
</type>

Create Arslan\BoxType\Ui\DataProvider\Product\Form\Modifier\Composite.php 

namespace Arslan\BoxType\Ui\DataProvider\Product\Form\Modifier;

use Magento\Catalog\Model\Locator\LocatorInterface;
use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier;
use Magento\Framework\ObjectManagerInterface;
use Magento\Ui\DataProvider\Modifier\ModifierInterface;
use Magento\Bundle\Api\ProductOptionRepositoryInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Bundle\Ui\DataProvider\Product\Form\Modifier\BundlePanel;

/**
* Class Bundle customizes Bundle product creation flow
*/
class Composite extends AbstractModifier
{
/**
* @var LocatorInterface
*/
protected $locator;

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

/**
* Object Manager
*
* @var ObjectManagerInterface
*/
protected $objectManager;

/**
* @var ProductOptionRepositoryInterface
*/
protected $optionsRepository;

/**
* @var ProductRepositoryInterface
*/
protected $productRepository;

/**
* @param LocatorInterface $locator
* @param ObjectManagerInterface $objectManager
* @param ProductOptionRepositoryInterface $optionsRepository
* @param ProductRepositoryInterface $productRepository
* @param array $modifiers
*/
public function __construct(
LocatorInterface $locator,
ObjectManagerInterface $objectManager,
ProductOptionRepositoryInterface $optionsRepository,
ProductRepositoryInterface $productRepository,
array $modifiers = []
) {
$this->locator = $locator;
$this->objectManager = $objectManager;
$this->optionsRepository = $optionsRepository;
$this->productRepository = $productRepository;
$this->modifiers = $modifiers;
}

/**
* {@inheritdoc}
*/
public function modifyMeta(array $meta)
{
if ($this->locator->getProduct()->getTypeId() === 'box') {
foreach ($this->modifiers as $bundleClass) {
/** @var ModifierInterface $bundleModifier */
$bundleModifier = $this->objectManager->get($bundleClass);
if (!$bundleModifier instanceof ModifierInterface) {
throw new \InvalidArgumentException(
'Type "' . $bundleClass . '" is not an instance of ' . ModifierInterface::class
);
}
$meta = $bundleModifier->modifyMeta($meta);
}
}
return $meta;
}

/**
* {@inheritdoc}
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
*/
public function modifyData(array $data)
{
/** @var \Magento\Catalog\Api\Data\ProductInterface $product */
$product = $this->locator->getProduct();
$modelId = $product->getId();
$isBundleProduct = $product->getTypeId() === 'box';
if ($isBundleProduct && $modelId) {
$data[$modelId][BundlePanel::CODE_BUNDLE_OPTIONS][BundlePanel::CODE_BUNDLE_OPTIONS] = [];
/** @var \Magento\Bundle\Api\Data\OptionInterface $option */
foreach ($this->optionsRepository->getList($product->getSku()) as $option) {
$selections = [];
/** @var \Magento\Bundle\Api\Data\LinkInterface $productLink */
foreach ($option->getProductLinks() as $productLink) {
$linkedProduct = $this->productRepository->get($productLink->getSku());
$integerQty = 1;
if ($linkedProduct->getExtensionAttributes()->getStockItem()) {
if ($linkedProduct->getExtensionAttributes()->getStockItem()->getIsQtyDecimal()) {
$integerQty = 0;
}
}
$selections[] = [
'selection_id' => $productLink->getId(),
'option_id' => $productLink->getOptionId(),
'product_id' => $linkedProduct->getId(),
'name' => $linkedProduct->getName(),
'sku' => $linkedProduct->getSku(),
'is_default' => ($productLink->getIsDefault()) ? '1' : '0',
'selection_price_value' => $productLink->getPrice(),
'selection_price_type' => $productLink->getPriceType(),
'selection_qty' => $integerQty ? (int)$productLink->getQty() : $productLink->getQty(),
'selection_can_change_qty' => $productLink->getCanChangeQuantity(),
'selection_qty_is_integer' => (bool)$integerQty,
'position' => $productLink->getPosition(),
'delete' => '',
];
}
$data[$modelId][BundlePanel::CODE_BUNDLE_OPTIONS][BundlePanel::CODE_BUNDLE_OPTIONS][] = [
'position' => $option->getPosition(),
'option_id' => $option->getOptionId(),
'title' => $option->getTitle(),
'default_title' => $option->getDefaultTitle(),
'type' => $option->getType(),
'required' => ($option->getRequired()) ? '1' : '0',
'bundle_selections' => $selections,
];
}
}

return $data;
}
}

 

-----
If Issue Solved, Click Kudos and Accept As solutions.
Sohel Rana, 7x Magento 2, 2x Magento 1 Certified

Re: Help please creating a custom product type extended from bundle product type in magento 2?

Hi, the add option button appears, but The product didn't save.
"the attribute "price" must be set" i solved by updating di.xml
Backend working fine..

Now frontend display of new product type is not working same as bundle product type..
which files i need to update for frontend display of New product type..

Re: Help please creating a custom product type extended from bundle product type in magento 2?

thanks, we need to override etc/di.xml too... & it works perfectly.. 

Re: Help please creating a custom product type extended from bundle product type in magento 2?

The product didn't save."the attribute "price" must be set"

How do you fix this issue ? can you provide me code to fix this. thanks Smiley Very Happy

Re: Help please creating a custom product type extended from bundle product type in magento 2?

Hi there @arslantS and @Sohel  great post, this really help me a lot!

But I'm stuck in this issue that you mentioned:
The "Price" attribute value is empty. Set the attribute and try again.

Could you help me and share the changes you've made in etc/di.xml

I've tryed to make some changes in etc/di.xml, but nothing works to allowed me to save my custom product type extending bundle type.. 

One more doubt, please @Sohel could you guide me how can I create a custom frontend template for this custom product type.. we need that for this custom product type we have a different layout page, totally different as default bundle product page, but with all funcionalities as default bundle.

Just one more doubt, can I create a custom product extending bundle product type and sell this products by recurring paymments, like weekly, montly or annualy?

Re: Help please creating a custom product type extended from bundle product type in magento 2?

Hi @fabox237 

Did you find out the solution to this problem? Can you share how?

Thanks in advance.