How to "add to cart" a product with custom input field and save it to Database?
To accomplish this you could use Magento built-in "additional_options" functionality so that you don't have to edit email template, admin order view, customer order view (etc) to display your custom options.
Github : https://github.com/srenon/Cloudways_Mymodule
/app/code/MagePal/CustomItemAddToCart/etc/events.xml
<?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="magepal_Customitemaddtocart_checkout_cart_product_add_after" instance="MagePal\CustomItemAddToCart\Observer\CheckoutCartProductAddAfterObserver" />
</event>
<event name="sales_model_service_quote_submit_before">
<observer name="magepal_Customitemaddtocart_sales_model_service_quote_submit_before" instance="MagePal\CustomItemAddToCart\Observer\SalesModelServiceQuoteSubmitBeforeObserver" />
</event>
</config>
Add Option to Quote
/app/code/MagePal/CustomItemAddToCart/Observer/CheckoutCartProductAddAfterObserver.php
<?php
namespace MagePal\CustomItemAddToCart\Observer;
use Magento\Framework\Event\Observer as EventObserver;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\App\RequestInterface;
class CheckoutCartProductAddAfterObserver implements ObserverInterface
{
protected $_request;
/**
* @param RequestInterface $request
*/
public function __construct(RequestInterface $request){
$this->_request = $request;
}
/**
* @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('magepal');
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 read them
// Pre-fill remarks on product edit pages
/* Issues */
// Create new cart item with identical option values will add a new line item, instead of increment the previous item qty
}
}
Method #1 - Copying Option from quote_item to order_item using Observer See Magento 2 fieldset.xml; copy fields from quote to order
/app/code/MagePal/CustomItemAddToCart/Observer/SalesModelServiceQuoteSubmitBeforeObserver.php
<?php
namespace MagePal\CustomItemAddToCart\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 an 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')) {
//Todo
// - 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();
//As of Magento ~2.2, the unserialize() was discontinued in favor of json_encode.
// See https://devdocs.magento.com/guides/v2.4/ext-best-practices/tutorials/serialized-to-json-data-upgrade.html
$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;
}
}
Method #2 - Copying Option from quote_item to order_item using Plugin
/app/code/MagePal/CustomItemAddToCart/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\Quote\Model\Quote\Item\ToOrderItem">
<plugin name="magepal_Customitemaddtocart_Sales_Quote_Item_ToOrderItem" type="MagePal\CustomItemAddToCart\Plugin\QuoteItemToOrderItemPlugin" />
</type>
</config>
/app/code/MagePal/CustomItemAddToCart/Plugin/QuoteItemToOrderItemPlugin.php
<?php
namespace MagePal\CustomItemAddToCart\Plugin;
class QuoteItemToOrderItemPlugin
{
public function aroundConvert(\Magento\Quote\Model\Quote\Item\ToOrderItem $subject, callable $proceed, $quoteItem, $data)
{
// get order item
$orderItem = $proceed($quoteItem, $data);
if(!$orderItem->getParentItemId() && $orderItem->getProductType() == \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE){
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;
}
//As of Magento ~2.2, the unserialize() was discontinued in favor of json_encode.
// See https://devdocs.magento.com/guides/v2.4/ext-best-practices/tutorials/serialized-to-json-data-upgrade.html
if(count($additionalOptions) > 0){
$options = $orderItem->getProductOptions();
$options['additional_options'] = unserialize($additionalOptions->getValue());
$orderItem->setProductOptions($options);
}
}
}
return $orderItem;
}
}
Base off of Magento1 - Quote/order product item attribute based on user input