cancel
Showing results for 
Search instead for 
Did you mean: 

How to add custom field in order_sales table and used it ?

How to add custom field in order_sales table and used it ?

Hello, i need to add new field in order_sale tables.

I create a initialSchema and add field in database (office_id). But i don't use this filed in order create, no matter where it is created.

 

Please tell me how to use this field, on order save. Google's results are very confusing .

 

Please do not answer me with things like EAV or attributes.

 

This should be the table and should be used there. I have a specific question, I hope for get a clear and precise answer.

2 REPLIES 2

Re: How to add custom field in order_sales table and used it ?

Hello @vanssata,

 

 Let me give you an example, assuming that you’ve already defined a custom module with a registration.php and a module.xml file, then you are ready to add the code from the steps below.

To start with, define the custom attribute in a catalog_attributes.xml within a quote_item group.

In this example, a dropdown_attribute attribute (of type dropdown) will be added to Magento.

 

file: app/code/Vendor/Module/etc/catalog_attributes.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Catalog:etc/catalog_attributes.xsd">
    <group name="quote_item">
        <attribute name="dropdown_attribute"/>
    </group>
</config>

 

Add the InstallData.php class to add the custom attribute programmatically. In this example, the attribute created will be added to the Default attribute set, within the General group.

 

Take note that the attribute is also added as a column to the quote_item and sales_order_item tables via $quoteSetup and $salesSetup respectively.

 

file: app/code/Vendor/Module/Setup/InstallData.php

<?php
namespace [Vendor]\[Module]\Setup;
 
use Magento\Eav\Setup\EavSetup;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\DB\Ddl\Table;
use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Sales\Setup\SalesSetupFactory;
use Magento\Quote\Setup\QuoteSetupFactory;
 
/**
 * @codeCoverageIgnore
 */
class InstallData implements InstallDataInterface
{
    /**
     * EAV setup factory
     *
     * @var EavSetupFactory
     */
    private $eavSetupFactory;
 
    /**
     * @var QuoteSetupFactory
     */
    private $quoteSetupFactory;
 
    /**
     * @var SalesSetup
     */
    private $salesSetupFactory;
 
    /**
     * InstallData constructor.
     * @param EavSetupFactory $eavSetupFactory
     * @param QuoteSetupFactory $quoteSetupFactory
     */
    public function __construct(
        EavSetupFactory $eavSetupFactory,
        QuoteSetupFactory $quoteSetupFactory,
        SalesSetupFactory $salesSetupFactory
    )
    {
        $this->eavSetupFactory = $eavSetupFactory;
        $this->quoteSetupFactory = $quoteSetupFactory;
        $this->salesSetupFactory = $salesSetupFactory;
    }
 
    public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {
        /** @var EavSetup $eavSetup */
        $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);
 
        /** @var QuoteSetup $quoteSetup */
        $quoteSetup = $this->quoteSetupFactory->create(['setup' => $setup]);
 
        /** @var SalesSetup $salesSetup */
        $salesSetup = $this->salesSetupFactory->create(['setup' => $setup]);
 
        /**
         * Add attributes to the eav/attribute
         */
        $eavSetup->addAttribute(
            \Magento\Catalog\Model\Product::ENTITY,
            'dropdown_attribute',
            [
                'type'                    => 'int',
                'label'                   => 'Dropdown Attribute',
                'input'                   => 'select',
                'global'                  => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL,
                'visible'                 => true,
                'required'                => false,
                'user_defined'            => true,
                'default'                 => '',
                'searchable'              => false,
                'filterable'              => false,
                'comparable'              => false,
                'visible_on_front'        => false,
                'used_in_product_listing' => false,
                'unique'                  => false,
                'option'                  => [
                    'values' => [
                        'Option 1',
                        'Option 2',
                        'Option 3'
                    ],
                ]
            ]
        );
 
        $attributeSetId = $eavSetup->getDefaultAttributeSetId('catalog_product');
        $eavSetup->addAttributeToSet(
            'catalog_product',
            $attributeSetId,
            'General',
            'dropdown_attribute'
        );
 
        $attributeOptions = [
            'type'     => Table::TYPE_TEXT,
            'visible'  => true,
            'required' => false
        ];
        $quoteSetup->addAttribute('quote_item', 'dropdown_attribute', $attributeOptions);
        $salesSetup->addAttribute('order_item', 'dropdown_attribute', $attributeOptions);
    }
}

 

The next step is to ensure that the product dropdown attribute is saved in the quote when the sales_quote_item_set_product event is fired.

Add an observer to this event via the events.xml file.

 

file: app/code/Vendor/Module/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="sales_quote_item_set_product">
        <observer name="set_item_dropdown_attribute" instance="[Vendor]\[Module]\Observer\SetItemDropdownAttribute" />
    </event>
</config>

 

Now add the SetItemCustomAttribute.php observer. This will simply set the product’s dropdown attribute option, which can be retrieved using $product->getAttributeText('dropdown_attribute'), to the quote item using setDropdownAttribute()

 

 

file: app/code/Vendor/Module/Observer/SetItemCustomAttribute.php

 

<?php
namespace [Vendor]\[Module]\Observer;
 
use Magento\Framework\Event\ObserverInterface;
 
class SetItemDropdownAttribute implements ObserverInterface
{
    /**
     * @param \Magento\Framework\Event\Observer $observer
     * @return void
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    public function execute(\Magento\Framework\Event\Observer $observer)
    {
        $quoteItem = $observer->getQuoteItem();
        $product = $observer->getProduct();
        $quoteItem->setDropdownAttribute($product->getAttributeText('dropdown_attribute'));
    }
}

 

The last step should be (see explanation below…) to add a fieldset.xml file that defines the attribute to be copied over from the quote to order object when an order is placed. This is similar to what happens in Magento 1 using the module’s config.xml file.

 

 

file: app/code/Vendor/Module/etc/fieldset.xml

 

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Object/etc/fieldset.xsd">
    <scope id="global">
        <fieldset id="quote_convert_item">
            <field name="dropdown_attribute">
                <aspect name="to_order_item" />
            </field>
        </fieldset>
    </scope>
</config>

 

If you now enable the module, clear the cache and run the database upgrade via the commands provided, you should notice that the dropdown_attribute attribute has been added to Magento’s eav_attribute table in the database, and also as a column to the quote_item and sales_order_item tables.

Now assign an existing product a value from the dropdown_attribute attribute within the admin, and when adding the product to your basket, you should notice that the dropdown_attribute column in the quote_item table is now populated for the item entry!

Finally, place an order which will convert the quote item data to an order item entry in the sales_order_item table. Whilst the default column data from quote_item is copied across, the dropdown_attribute column is not.

It is not clear whether this is intentional from Magento, or whether it is more likely a bug, but for some reason the fieldset.xml configuration in custom modules doesn’t take into account the conversion of the quote item to order item.

Therefore to fix this problem, create a plugin that will function around the convert() method of the Magento\Quote\Model\Quote\Item\ToOrderItem class. This is the class responsible for converting the quote data to order data.

Start by defining the plugin within your module’s di.xml file.

file: app/code/Vendor/Module/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="dropdown_attribute_quote_to_order_item" type="[Vendor]\[Module]\Plugin\DropdownAttributeQuoteToOrderItem"/>
    </type>
</config>

 

Then add the aroundConvert() plugin.

 

file: app/code/Vendor/Module/Plugin/DropdownAttributeQuoteToOrderItem.php

 

<?php
namespace [Vendor]\[Module]\Plugin;
 
class DropdownAttributeQuoteToOrderItem
{
    public function aroundConvert(
        \Magento\Quote\Model\Quote\Item\ToOrderItem $subject,
        \Closure $proceed,
        \Magento\Quote\Model\Quote\Item\AbstractItem $item,
        $additional = []
    ) {
        /** @var $orderItem \Magento\Sales\Model\Order\Item */
        $orderItem = $proceed($item, $additional);
        $orderItem->setDropdownAttribute($item->getDropdownAttribute());
        return $orderItem;
    }
}

Now when you place an order, the quote item’s dropdown_attribute value will copy across to the sales_order_item table.

Hopefully, the plugin step will not be needed in the future when Magento fix the conversion issue. However, for now, the plugin solution is sufficient.

--
If my answer is useful, please Accept as Solution & give Kudos

Re: How to add custom field in order_sales table and used it ?

Thanks, this is a very useful answer.
I can use  solution in another case in same project.

But on this case  I need change this field only in Model, like Model in normal MVC architecture.

This field change only programitacly.