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.

  • Did you ever figure this out? I am trying to figure out the exact same thing. – James Harrington Dec 3 '17 at 0:44

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
    ) {
        $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;

        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;

                $this->cart->addProduct($product, $productParams);

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

                ['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.',
                return $this->goBack(null, $groupedProduct);

        }catch (\Magento\Framework\Exception\LocalizedException $e) {
            if ($this->_checkoutSession->getUseNotice(true)) {
            } else {
                $messages = array_unique(explode("\n", $e->getMessage()));
                foreach ($messages as $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.'));
            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')


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" />


<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    <action name="groupedcustom/cart/addproducts">
        <section name="cart"/>

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"  />

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/


    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.

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.