1

I am writing an extension where I need to place an order with the items already in the user's cart (added programmatically). I have been successful with this, however my orders lose custom options added via $_product->addCustomOption().

$addOptns['my_custom_option'] = 3;
foreach ($addOptns as $key => $value) {
    $_product->addCustomOption( $key, $value);
}
$params = array(
    'product' => 123, //product Id
    'qty'   => 100    //quantity of product
);
$this->_cart->addProduct(
    $_product,
    $params
);
$this->_cart->save();
$quote = $this->_cart->getQuote();

...[shipping and payment config]...

$order_id = $this->cartManagementInterface->placeOrder($quote->getId());

When I try to receive my_custom_option from the order that is placed, I get null.

var_dump($item->getCustomOptions()); // NULL

Any guess what is wrong with my approach?

Note: This is not a 'custom option' like those created in the admin UI nor do I expect it to be, I use these custom options for other customized processes and they are not directly user selectable.

2
+50

Order object does not take custom options from quote but you can force it with event

sales_model_service_quote_submit_before

check this tutorial: https://www.cloudways.com/blog/add-additional-options-in-magento-2/. it covers your case completely

  • Thanks for this, it took some significant changes to work for my specific case but seems to work. I'm still surprised there isn't something more direct. – jamil Feb 2 '18 at 23:48
  • Yes the sales_model_service_quote_submit_before is not working, is there have any other event for accomplish the task – senthil May 11 '18 at 10:00
4

You can achieve by a plugin in Magento 2.2.*

First of all, we need to create an observer file and one Magento event file to implement this functionality.

<?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="catalog_product_load_after">
        <observer name="set_additional_options" instance="CompanyName\ModuleName\Model\SetAdditionalOptions" />
    </event>    
</config>

Once you have created this file, now you need to Create another file and named as: CompanyName\ModuleName/Model/SetAdditionalOptions.php

<?php
namespace CompanyName\ModuleName\Model;

use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\Serialize\Serializer\Json;

class SetAdditionalOptions implements ObserverInterface
{
    protected $_request;    
    public function __construct(RequestInterface $request, Json $serializer = null) 
    {
        $this->_request = $request;
        $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()
            ->get(\Magento\Framework\Serialize\Serializer\Json::class);
    }

    /**
     * @param \Magento\Framework\Event\Observer $observer
     */
    public function execute(\Magento\Framework\Event\Observer $observer)
    {
        // Check and set information according to your need
        $product = $observer->getProduct();                    
        if ($this->_request->getFullActionName() == 'checkout_cart_add') { //checking when product is adding to cart
            $product = $observer->getProduct();
            $additionalOptions = [];
            $additionalOptions[] = array(
                'label' => "Release Date", //Custom option label
                'value' => $product->getReleaseDate(), //Custom option value
            );                        
            $product->addCustomOption('additional_options', $this->serializer->serialize($additionalOptions));
        }
    }

}

Now, We need to create a plugin for the retrieve custom option from cart to order.

First We need to create 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="unique_name" type="CompanyName\ModuleName\Model\Plugin\Quote\ToOrderItem" sortOrder="1" />
    </type>
</config>

Once you have created this file, now you need to Create another file and named as: CompanyName\ModuleName\Model\Plugin\Quote\ToOrderItem.php

<?php
namespace ZCompanyName\ModuleName\Model\Plugin\Quote;

use Magento\Quote\Model\Quote\Item\ToOrderItem as QuoteToOrderItem;
use Magento\Framework\Serialize\Serializer\Json;
class ToOrderItem
{    
    public function __construct(Json $serializer = null) 
    {        
        $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()
            ->get(\Magento\Framework\Serialize\Serializer\Json::class);
    }

    public function aroundConvert(QuoteToOrderItem $subject,
        \Closure $proceed,
        $item,
        $data = []
    ) {
        // Get Order Item
        $orderItem = $proceed($item, $data);               

        $additionalOptions = $item->getOptionByCode('additional_options');        
        // Check if there is any additional options in Quote Item                    
        if (count($additionalOptions) > 0) {
            // Get Order Item's other options
            $options = $orderItem->getProductOptions();
            // Set additional options to Order Item
            $options['additional_options'] = $this->serializer->unserialize($additionalOptions->getValue());
            $orderItem->setProductOptions($options);
        }

        return $orderItem;
    }
}

  • Hi @dhairya Its working for me above version 2.2.*, you save my day bro! – Rajan Soni May 3 '18 at 5:56
  • I have follow this step but i am not getting any result – Jinesh Apr 30 at 9:39
  • Custom option is showing on cart and checkout page but I am not able to place order after using plugin. Getting error: {"message":"A server error stopped your order from being placed. Please try to place your order again."} Can you please help me? – Vinod Kumar Oct 4 at 5:53
0

Retrieve custom options of products present any order

$orderObject = $objectManager->get('\Magento\Sales\Model\Order'); 

// load by order id
$orderId = 1; // YOUR ORDER ID
$order = $orderObject->load($orderId);

// load by order increment id
// $incrementId = '000000001'; // YOUR ORDER INCREMENT ID
// $order = $orderObject->loadByIncrementId($incrementId);

$items = $order->getAllVisibleItems(); // get all items aren't marked as deleted and that do not have parent item; for e.g. here associated simple products of a configurable products are not fetched

// Order items can also be fetched with the following functions
// $items = $order->getAllItems(); // get all items that are not marked as deleted
// $items = $order->getItems(); // get all items

foreach ($items as $item) {
    $options = $item->getProductOptions();        
    if (isset($options['options']) && !empty($options['options'])) {        
        foreach ($options['options'] as $option) {
            echo 'Title: ' . $option['label'] . '<br />';
            echo 'ID: ' . $option['option_id'] . '<br />';
            echo 'Type: ' . $option['option_type'] . '<br />';
            echo 'Value: ' . $option['option_value'] . '<br />' . '<br />';
        }
    }
}

However, using ObjectManager directly for that is not a great solution. You can still use the code with dependency injection as the below:

  • Open the block class Yourcompany_HelloWorld, then inject the object of \Magento\Sales\Model\Order in the constructor of my module’s block class.

app/code/Yourcompany/HelloWorld/Block/HelloWorld.php

<?php
namespace Yourcompany\HelloWorld\Block;
class HelloWorld extends \Magento\Framework\View\Element\Template
{
    protected $_orderModel;    

    public function __construct(
        \Magento\Backend\Block\Template\Context $context,
        \Magento\Sales\Model\Order $orderModel,
        array $data = []
        )
        {        
            $this->_orderModel = $orderModel;
            parent::__construct($context, $data);
        }

        public function getOrderItems($orderId) 
        {
            $order = $this->_orderModel->load($orderId);
            return $order->getAllVisibleItems();        
        }
}
?>

Like that, you can use the function in the .phtml file.

$orderId = 1; // YOUR ORDER ID
$items = $block->getOrderItems($orderId);

foreach ($items as $item) {
    $options = $item->getProductOptions();        
    if (isset($options['options']) && !empty($options['options'])) {        
        foreach ($options['options'] as $option) {
            echo 'Title: ' . $option['label'] . '<br />';
            echo 'ID: ' . $option['option_id'] . '<br />';
            echo 'Type: ' . $option['option_type'] . '<br />';
            echo 'Value: ' . $option['option_value'] . '<br />' . '<br />';
        }
    }
}

That is all things you will use to retrieve value product custom option cart order in Magento 2.

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.