This website uses cookies

Our website, platform and/or any sub domains use cookies to understand how you use our services, and to improve both your experience and our marketing relevance.

📣 Try the fastest hosting platform with pay-as-you-go pricing & 24/7 expert support! MIGRATE NOW →

How to Add Additional Options in Magento 2 Cart

Updated on December 23, 2021

5 Min Read
magento 2 custom options

Magento 2 has several well-known features that add great value to an online store. However, many store owners and administrators are always looking for new features that would distinguish their stores from the competition.

Recently, I was looking for a way to add custom input option to Magento 2 cart without affecting the core Magento functionalities (a good practice endorsed by Magento gurus including Ben Marks!)

I did a lot of Google searches and read a number of Stack Overflow threads (even started a thread myself). Finally, here is my solution of the big question of adding additional options in Magento 2 cart.

To add additional options in the frontend of the Magento 2 cart and to carry those values through ordering, invoices, and reordering process, you need two observer events.

First of all, let me briefly explain Observers in Magento.

Observers are special Magento classes that are executed whenever the event manager detects the event observers are configured to watch. The observer classes hold the instance of that event which is used for further pre-defined actions.

Bonus Tip: Almost 50% visitors will leave a website if it is slow. Don’t let your visitors leave you. Get best hosting for Magento 2 stores.

For the purpose of this article, assume that your Magento 2 store is installed directly in the public_html folder of the Magento hosting directory. This means that the store is accessible directly from the parent domain (yourdomain.com). Also, assume that the default Magento 2 theme is active and in use.

Managed Magento Hosting for an Instant Performance Boost

Experience blazing-fast & reliable Magento managed hosting, and cut all extra costs with the pay-as-you-go plans.

Following is the process of implementing the custom values in the Magento 2 cart.

Create a Simple Magento 2 Module

The best practice and the recommended code standard for Magento 2 is to create a module.

Here, I will use Cloudways as Module Namespace and Mymodule as Module Name.

First, create a registration.php file in /app/code/Cloudways/Mymodule/. Add the following code to the file.

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Cloudways_Mymodule',
    __DIR__
);

Now, create a module.xml file in /app/code/Cloudways/Mymodule/etc/ with the following content.

<?xml version="1.0"?>
	<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
	<module name="Cloudways_Mymodule" setup_version="1.0.0"></module>
</config>

Assign Observers to Events

Now, I will configure the observers in the events.xml file to watch certain events.

I will assign the observer CheckoutCartProductAddAfterObserver to the event checkout_cart_product_add_after and SalesModelServiceQuoteSubmitBeforeObserver to sales_model_service_quote_submit_before.

The file should be located at /app/code/Cloudways/Mymodule/etc/ with the following code.

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="checkout_cart_product_add_after">
        <observer name="cloudways_mymodule_checkout_cart_product_add_after" instance="Cloudways\Mymodule\Observer\CheckoutCartProductAddAfterObserver" />
    </event>
    <event name="sales_model_service_quote_submit_before">
        <observer name="cloudways_mymodule_sales_model_service_quote_submit_before" instance="Cloudways\Mymodule\Observer\SalesModelServiceQuoteSubmitBeforeObserver" />
    </event>
</config>

Create the Observers

I will require two observers for the job. Keep in mind that in order to create an observer, you must create an observer class file under the Observer directory of the module. The class should implement Magento\Framework\Event\ObserverInterface and define the observer execute the function.

To add custom option to quote, create CheckoutCartProductAddAfterObserver.php file in /app/code/Cloudways/Mymodule/Observer/ and add the following code in it.

<?php

namespace Cloudways\Mymodule\Observer;
use Magento\Framework\Event\Observer as EventObserver;
use Magento\Framework\Event\ObserverInterface;

class CheckoutCartProductAddAfterObserver implements ObserverInterface
{
    /**
     * @var \Magento\Framework\View\LayoutInterface
     */
    protected $_layout;
    /**
     * @var \Magento\Store\Model\StoreManagerInterface
     */
    protected $_storeManager;
    protected $_request;
    /**
     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
     * @param \Magento\Framework\View\LayoutInterface $layout
     */
    public function __construct(
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Magento\Framework\View\LayoutInterface $layout,
        \Magento\Framework\App\RequestInterface $request
    )
    {
        $this->_layout = $layout;
        $this->_storeManager = $storeManager;
        $this->_request = $request;
    }
    /**
     * Add order information into GA block to render on checkout success pages
     *
     * @param EventObserver $observer
     * @return void
     */
    public function execute(EventObserver $observer)
    {
        /* @var \Magento\Quote\Model\Quote\Item $item */
        $item = $observer->getQuoteItem();
        $additionalOptions = array();
        if ($additionalOption = $item->getOptionByCode('additional_options')){
            $additionalOptions = (array) unserialize($additionalOption->getValue());
        }
        $post = $this->_request->getParam('cloudways');
        if(is_array($post))
        {
            foreach($post as $key => $value)
            {
                if($key == '' || $value == '')
                {
                    continue;
                }
                $additionalOptions[] = [
                    'label' => $key,
                    'value' => $value
                ];
            }
        }
        if(count($additionalOptions) > 0)
        {
            $item->addOption(array(
                'code' => 'additional_options',
                'value' => serialize($additionalOptions)
            ));
        }
        /* To Do */
        // Edit Cart - May need to remove option and readd them
        // Pre-fill remarks on product edit pages
        // Check for comparability with custom option
    }
}

I will now create another observer that will copy option from quote_item to order_item.

Create another file SalesModelServiceQuoteSubmitBeforeObserver.php in /app/code/Cloudways/Mymodule/Observer/ with the following code:

<?php

namespace Cloudways\Mymodule\Observer;

use Magento\Framework\Event\Observer as EventObserver;
use Magento\Framework\Event\ObserverInterface;

class SalesModelServiceQuoteSubmitBeforeObserver implements ObserverInterface
{
    private $quoteItems = [];
    private $quote = null;
    private $order = null;
    /**
     * Add order information into GA block to render on checkout success pages
     *
     * @param EventObserver $observer
     * @return void
     */
    public function execute(EventObserver $observer)
    {
        $this->quote = $observer->getQuote();
        $this->order = $observer->getOrder();
        // can not find a equivalent event for sales_convert_quote_item_to_order_item
        /* @var  \Magento\Sales\Model\Order\Item $orderItem */
        foreach($this->order->getItems() as $orderItem)
        {
            if(!$orderItem->getParentItemId() && $orderItem->getProductType() == \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
            {

                if($quoteItem = $this->getQuoteItemById($orderItem->getQuoteItemId())){
                    if ($additionalOptionsQuote = $quoteItem->getOptionByCode('additional_options'))
                    {
                        //To do
                        // - check to make sure element are not added twice
                        // - $additionalOptionsQuote - may not be an array
                        if($additionalOptionsOrder = $orderItem->getProductOptionByCode('additional_options'))
                        {
                            $additionalOptions = array_merge($additionalOptionsQuote, $additionalOptionsOrder);
                        }
                        else
                        {
                            $additionalOptions = $additionalOptionsQuote;
                        }
                        if(count($additionalOptions) > 0)
                        {
                            $options = $orderItem->getProductOptions();
                            $options['additional_options'] = unserialize($additionalOptions->getValue());
                            $orderItem->setProductOptions($options);
                        }

                    }
                }
            }
        }
    }
    private function getQuoteItemById($id)
    {
        if(empty($this->quoteItems))
        {
            /* @var  \Magento\Quote\Model\Quote\Item $item */
            foreach($this->quote->getItems() as $item)
            {
                //filter out config/bundle etc product
                if(!$item->getParentItemId() && $item->getProductType() == \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
                {
                    $this->quoteItems[$item->getId()] = $item;
                }
            }
        }
        if(array_key_exists($id, $this->quoteItems))
        {
            return $this->quoteItems[$id];
        }
        return null;
    }
}

Add Input Field to Magento 2 Cart

Last but not the least, I will add a custom input field to Magento 2 Cart template. For this, it is important to know the location from where Magento 2 is rendering the default cart on the product page. This is the location of the template:

Magento/Catalog/view/frontend/templates/catalog/product/view/addtocart.phtml

Copy the addtocart.phtml file to the module directory at /app/code/Cloudways/Mymodule/view/frontend/templates/catalog/product/view/ and paste the following code in it:

<?php
/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */

// @codingStandardsIgnoreFile

/** @var $block \Magento\Catalog\Block\Product\View */
?>
<?php $_product = $block->getProduct(); ?>
<?php $buttonTitle = __('Add to Cart'); ?>
<?php if ($_product->isSaleable()): ?>
<div class="box-tocart">
    <div class="fieldset">
        <?php if ($block->shouldRenderQuantity()): ?>
        <div class="field qty">
            <label class="label" for="qty"><span><?php /* @escapeNotVerified */ echo __('Qty') ?></span></label>
            <div class="control">
                <input type="number"
                       name="qty"
                       id="qty"
                       maxlength="12"
                       value="<?php /* @escapeNotVerified */ echo $block->getProductDefaultQty() * 1 ?>"
                       title="<?php /* @escapeNotVerified */ echo __('Qty') ?>" class="input-text qty"
                       data-validate="<?php echo $block->escapeHtml(json_encode($block->getQuantityValidators())) ?>"
                       />
            </div>
        </div>
        <!-- Custom Input Field -->
        <div>
            <input
                type="text"
                name="cloudways[remarks]"
                id="remarks"
                maxlength="255"
                placeholder="Remarks"
            />
        </div>
        <!-- Custom Input Field -->
        <br>
        <?php endif; ?>
        <div class="actions">
            <button type="submit"
                    title="<?php /* @escapeNotVerified */ echo $buttonTitle ?>"
                    class="action primary tocart"
                    id="product-addtocart-button">
                <span><?php /* @escapeNotVerified */ echo $buttonTitle ?></span>
            </button>
            <?php echo $block->getChildHtml('', true) ?>
        </div>
    </div>
</div>
<?php endif; ?>
<script type="text/x-magento-init">
    {
        "#product_addtocart_form": {
            "Magento_Catalog/product/view/validation": {
                "radioCheckboxClosest": ".nested"
            }
        }
    }
</script>
<?php if (!$block->isRedirectToCartEnabled()) : ?>
<script type="text/x-magento-init">
    {
        "#product_addtocart_form": {
            "catalogAddToCart": {
                "bindSubmit": false
            }
        }
    }
</script>
<?php endif; ?>

And that’s all.

Now, the last thing to do is to change the default addtocart.phtml template to a custom template. At this point, Magento 2 does not know that I want to use my own addtocart.phtml file. To remedy this, I will modify the path using the Magento 2 layout.

Create a new file catalog_product_view.xml in /app/code/Cloudways/Mymodule/view/frontend/layout/ and add the following code:

<?xml version="1.0"?>
<page layout="1column" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="product.info.addtocart">
            <action method="setTemplate">
                <argument name="template" xsi:type="string">Cloudways_Mymodule::catalog/product/view/addtocart.phtml</argument>
            </action>
        </referenceBlock>
        <referenceBlock name="product.info.addtocart.additional">
            <action method="setTemplate">
                <argument name="template" xsi:type="string">Cloudways_Mymodule::catalog/product/view/addtocart.phtml</argument>
            </action>
        </referenceBlock>
    </body>
</page>

Finally, the custom module that will add additional options to Magento 2 Cart and forward the custom information items across orders, invoices, and reorder processes is ready.

Now, I will enable and activate the module through the following CLI command.

rm -rf var/di var/generation var/cache/* var/log/* var/page_cache/*
php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento cache:clean
php bin/magento cache:flush
php bin/magento indexer:reindex

The final output will look like this:

Magento 2 Cart Output

Conclusion

I hope that by now you have a clear idea about the concept of Observers in Magento 2. I created a custom module that added a further option to the cart. If you have any questions about the process or would like to add to the discussion, please leave a comment below, and don’t forget to share the article with your fellow Magento users!

Share your opinion in the comment section. COMMENT NOW

Share This Article

Abdur Rahman

Abdur Rahman is the Magento whizz at Cloudways. He is growth ambitious, and aims to learn & share information about Ecommerce & Magento Development through practice and experimentation. He loves to travel and explore new ideas whenever he finds time. Get in touch with him at [email protected]

×

Get Our Newsletter
Be the first to get the latest updates and tutorials.

Thankyou for Subscribing Us!

×

Webinar: How to Get 100% Scores on Core Web Vitals

Join Joe Williams & Aleksandar Savkovic on 29th of March, 2021.

Do you like what you read?

Get the Latest Updates

Share Your Feedback

Please insert Content

Thank you for your feedback!

Do you like what you read?

Get the Latest Updates

Share Your Feedback

Please insert Content

Thank you for your feedback!

Want to Experience the Cloudways Platform in Its Full Glory?

Take a FREE guided tour of Cloudways and see for yourself how easily you can manage your server & apps on the leading cloud-hosting platform.

Start my tour

CYBER WEEK SAVINGS

  • 0

    Days

  • 0

    Hours

  • 0

    Mints

  • 0

    Sec

GET OFFER

For 4 Months &
40 Free Migrations

For 4 Months &
40 Free Migrations

Upgrade Now