Magento2 Grouped Products – Add Product with Custom Options to Cart

grouped-productsmagento2

I have a grouped product composed of simple products with custom options. I am displaying the custom options in a dropdown. The trouble I am having is that on submission of the form ("Add to Cart") the custom options are not being recognized. I have listed part of the form post below to illustrate. Will the add action handle this or do I need to write my own controller action to do this?

Form Post to ~/checkout/cart/add/

product:1182
selected_configurable_option:
related_product:
form_key:f1TDY3pWTMTYZUWC
options[865]:11558
super_group[628]:1
super_group[1281]:0

Any help is appreciated.

Best Answer

I did indeed figure this out. But is has been a while since I wrote it so my memory is a little foggy.

I created a new module with a new controller. I believe basically copied the Magento AddToCart controller and modified it.

My controller looks like this


namespace CoName\Groupedcustomproducts\Controller\Cart;

use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Checkout\Model\Cart as CustomerCart;
use Magento\Framework\Exception\NoSuchEntityException;

/**
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
 */

class AddProducts extends \Magento\Checkout\Controller\Cart
{


    protected $productRepository;

    /**
     * @param \Magento\Framework\App\Action\Context $context
     * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
     * @param \Magento\Checkout\Model\Session $checkoutSession
     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
     * @param \Magento\Framework\Data\Form\FormKey\Validator $formKeyValidator
     * @param CustomerCart $cart
     * @param ProductRepositoryInterface $productRepository
     * @codeCoverageIgnore
     */
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
        \Magento\Checkout\Model\Session $checkoutSession,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Magento\Framework\Data\Form\FormKey\Validator $formKeyValidator,
        CustomerCart $cart,
        ProductRepositoryInterface $productRepository
    ) {
        parent::__construct(
            $context,
            $scopeConfig,
            $checkoutSession,
            $storeManager,
            $formKeyValidator,
            $cart
        );
        $this->productRepository = $productRepository;
    }

    /**
     * Initialize product instance from request data
     *
     * @return \Magento\Catalog\Model\Product|false
     */
    protected function _initProduct($productId)
    {
        //$productId = (int)$this->getRequest()->getParam('product');
        if ($productId) {
            $storeId = $this->_objectManager->get('Magento\Store\Model\StoreManagerInterface')->getStore()->getId();
            try {
                return $this->productRepository->getById($productId, false, $storeId);
            } catch (NoSuchEntityException $e) {
                return false;
            }
        }
        return false;
    }

    public function execute()
    {
        if (!$this->_formKeyValidator->validate($this->getRequest())) {
            return $this->resultRedirectFactory->create()->setPath('*/*/');
        }

        $params = $this->getRequest()->getParams();
        $super_group = $params['super_group'];
        $orderedProducts = [];

        foreach($super_group as $pid => $qty){
            if(intval($qty) > 0){
                $orderedProducts[$pid] = $qty;
            }
        }


        //var_dump($params);
        //return;
        try {

            foreach($orderedProducts as $pid => $qty){
                $product = $this->_initProduct($pid);

                if (!$product) {
                    return $this->goBack();
                }

                $productParams = ['form_key' => $params['form_key']];

                $filter = new \Zend_Filter_LocalizedToNormalized(
                    ['locale' => $this->_objectManager->get('Magento\Framework\Locale\ResolverInterface')->getLocale()]
                );
                $productParams['qty'] = $filter->filter($qty);

                if (isset($params['options'][$pid])) {
                    $productParams['options'] = [];

                    foreach($params['options'][$pid] as $optId => $optVal){
                        $productParams['options'][$optId] = $optVal;
                    }
                }

                //var_dump($productParams);
                $this->cart->addProduct($product, $productParams);
            }
            $this->cart->save();

            $groupedProduct = $this->_initProduct($params['product']);

            $this->_eventManager->dispatch(
                'checkout_cart_add_product_complete',
                ['product' => $groupedProduct, 'request' => $this->getRequest(), 'response' => $this->getResponse()]
            );

            //$related = $this->getRequest()->getParam('related_product');


            if (!$this->_checkoutSession->getNoCartRedirect(true)) {
                if (!$this->cart->getQuote()->getHasError()) {
                    $message = __(
                        'You added %1 to your shopping cart.',
                        $product->getName()
                    );
                    $this->messageManager->addSuccessMessage($message);
                }
                return $this->goBack(null, $groupedProduct);
            }

        }catch (\Magento\Framework\Exception\LocalizedException $e) {
            if ($this->_checkoutSession->getUseNotice(true)) {
                $this->messageManager->addNotice(
                    $this->_objectManager->get('Magento\Framework\Escaper')->escapeHtml($e->getMessage())
                );
            } else {
                $messages = array_unique(explode("\n", $e->getMessage()));
                foreach ($messages as $message) {
                    $this->messageManager->addError(
                        $this->_objectManager->get('Magento\Framework\Escaper')->escapeHtml($message)
                    );
                }
            }

            $url = $this->_checkoutSession->getRedirectUrl(true);

            if (!$url) {
                $cartUrl = $this->_objectManager->get('Magento\Checkout\Helper\Cart')->getCartUrl();
                $url = $this->_redirect->getRedirectUrl($cartUrl);
            }

            return $this->goBack($url);

        } catch (\Exception $e) {
            $this->messageManager->addException($e, __('We can\'t add this item to your shopping cart right now.'));
            $this->_objectManager->get('Psr\Log\LoggerInterface')->critical($e);
            return $this->goBack();
        }
    }

    /**
     * Resolve response
     *
     * @param string $backUrl
     * @param \Magento\Catalog\Model\Product $product
     * @return $this|\Magento\Framework\Controller\Result\Redirect
     */
    protected function goBack($backUrl = null, $product = null)
    {
        if (!$this->getRequest()->isAjax()) {
            return parent::_goBack($backUrl);
        }

        $result = [];

        if ($backUrl || $backUrl = $this->getBackUrl()) {
            $result['backUrl'] = $backUrl;
        } else {
            if ($product && !$product->getIsSalable()) {
                $result['product'] = [
                    'statusText' => __('Out of stock')
                ];
            }
        }

        $this->getResponse()->representJson(
            $this->_objectManager->get('Magento\Framework\Json\Helper\Data')->jsonEncode($result)
        );
    }
}

Create the Route and Action in Magento routes.xml

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="standard">
        <route id="groupedcustom" frontName="groupedcustom">
            <module name="CoName_Groupedcustomproducts" />
        </route>
    </router>
</config>

sections.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Customer:etc/sections.xsd">
    <action name="groupedcustom/cart/addproducts">
        <section name="cart"/>
    </action>
</config>

I had to override the existing group product model and not really sure why. I basically just copied it over to my extension. I wont list it here because it is not really different but here is the DI config.

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
    <preference for="Magento\GroupedProduct\Model\Product\Type\Grouped" type="CoName\Groupedcustomproducts\Model\Product\Type\Grouped"  />
</config>

The final piece was to modify the add to cart functionality to use my new controller. I just did this in my theme. In my theme I added a grouped-product.js file under Magento_Catalog/web/js/

jQuery(function($){

    var $form = $('.product-add-form form');
    var oldAction = $form.attr('action');
    var newAction = oldAction.replace('checkout/cart/add', 'groupedcustom/cart/addproducts/');
    $form.attr('action', newAction);

});

It probably would have been better to do the last part in the template rather then the client-side. But I seem to recall trying to modify the action particularly painful. Doing through JavaScript was pretty painless and fast.