Magento 2 : Create a Category Attribute using Data Patches


In this tutorial, Today I will explain to how to create a category attribute using data patches in Magento 2. From Magento 2.3.x, Magento brings new features which is  data patches. The Data Patch is class that contains data notification instruction.

Previously, Magento 2 uses InstallData and UpgradeData file use add data in core table or custom table. From Magento 2.3 it will be replaced by Data Patch.

Let start step by step, on how to create a category attribute using data patches.

Step 1: Create module required files.

 First of all, Let’s assume that you have created simple module.

Step 2: Create category attribute 

Now, To create custom category attribute Create CategoryAttribute.php file at app/code/Nv/FeaturedCategories/Setup/Patch/Data/  and paste the below code :

<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
/**
 * Created By : Navnit Viradiya
 */
declare (strict_types = 1);
namespace Nv\FeaturedCategories\Setup\Patch\Data;
use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface;
use Magento\Eav\Setup\EavSetup;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;
/**
 * Class CreateCustomAttr for Create Custom Product Attribute using Data Patch.
 */
class CategoryAttribute implements DataPatchInterface {
    /**
     * ModuleDataSetupInterface
     *
     * @var ModuleDataSetupInterface
     */
    private $moduleDataSetup;
    /**
     * EavSetupFactory
     *
     * @var EavSetupFactory
     */
    private $eavSetupFactory;
    /**
     * @param ModuleDataSetupInterface $moduleDataSetup
     * @param EavSetupFactory          $eavSetupFactory
     */
    public function __construct(
        ModuleDataSetupInterface $moduleDataSetup,
        EavSetupFactory $eavSetupFactory
    ) {
        $this->moduleDataSetup = $moduleDataSetup;
        $this->eavSetupFactory = $eavSetupFactory;
    }
    /**
     * {@inheritdoc}
     */
    public function apply() {
        /** @var EavSetup $eavSetup */
        $eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]);
        $eavSetup->addAttribute(\Magento\Catalog\Model\Category::ENTITY, 'product_temp_attribute', [
            'type' => 'text',
            'label' => 'Category Temp Atrribute',
            'input' => 'text',
            'default' => 0,
            'sort_order' => 5,
            'global' => ScopedAttributeInterface::SCOPE_STORE,
            'group' => 'General Information',
            'visible_on_front' => true
        ]);
    }
    /**
     * {@inheritdoc}
     */
    public static function getDependencies() {
        return [];
    }
    /**
     * {@inheritdoc}
     */
    public function getAliases() {
        return [];
    }
}

Step 3: Add field in category form

As you may know, in the latest Magento versions, the category form for the admin panel is created via configuration file (app/code/Magento/Catalog/view/adminhtml/ui_component/category_form.xml or vendor/magento/module-catalog/view/adminhtml/ui_component/category_form.xml), so we need to create the same configuration file for our module and add our attribute to the category form.

Create a file category_form.xml at app/code/Nv/FeaturedCategories/view/adminhtml/ui_component/  and paste the below code :

<?xml version="1.0" ?>
<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
	<fieldset name="general">
        <field name="product_temp_attribute">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="required" xsi:type="boolean">false</item>
                    <item name="validation" xsi:type="array">
                        <item name="required-entry" xsi:type="boolean">false</item>
                    </item>
                    <item name="sortOrder" xsi:type="number">333</item>
                    <item name="dataType" xsi:type="string">string</item>
                    <item name="formElement" xsi:type="string">input</item>
                    <item name="label" translate="true" xsi:type="string">Category Temp Atrribute</item>
                </item>
            </argument>
        </field>
	</fieldset>
</form>

 Step 4: In Last, Now just execute this below command :

php bin/magento s:up
php bin/magento s:s:d -f
php bin/magento c:c

Magento 2 : Add custom field to Shipping Address form In Checkout Page


How to add custom fields in checkout page in magento 2.

In this blog, we will see how to add custom fields in shipping form on magento checkout page. Let start step by step, on how to add a field on the shipping step.

  1. Create module required files.
  2. Add column to ‘quote’ and ‘sales_order’.
  3. Add the field to the shipping address form dynamically
  4. Create our own ‘shipping-save-processor/default.js
  5. Create extension attributes ShippingInformationInterface
  6. Create a Plugin for save the custom field value into quote
  7. Save the value into sales order

Step 1: Create module required files.

Here vendor name Nv and Module name is CheckoutCustomField

app/code/Nv/CheckoutCustomField/etc/module.xml

<?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="Nv_CheckoutCustomField" setup_version="1.0.0">
    	<sequence>
            <module name="Magento_Catalog"/>
        </sequence>
    </module>	
</config>

app/code/Nv/CheckoutCustomField/registration.php

use \Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Nv_CheckoutCustomField', __DIR__);

Step 2: Add column to ‘quote’ and ‘sales_order’

I have already publish post for add column in magento native table using db_schema.

Step 3: Add the field to the shipping address form dynamically

First, we add a plugin for the LayoutProcessor: Create a file etc/frontend/di.xml

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Checkout\Block\Checkout\LayoutProcessor">
        <plugin name="add_custom_field_checkout_shipping_form" type="Nv\CheckoutCustomField\Plugin\Checkout\Block\LayoutProcessorPlugin" sortOrder="10"/>
    </type>
</config>

And the Plugin class: Plugin/Checkout/Block/LayoutProcessorPlugin.php

<?php
namespace Nv\CheckoutCustomField\Plugin\Checkout\Block;

class LayoutProcessorPlugin
{
    /**
     * @param \Magento\Checkout\Block\Checkout\LayoutProcessor $subject
     * @param array $jsLayout
     * @return array
     */
    public function afterProcess(
        \Magento\Checkout\Block\Checkout\LayoutProcessor $subject,
        array  $jsLayout
    ) {
 
        $jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']
        ['shippingAddress']['children']['shipping-address-fieldset']['children']['custom_field_text'] = [
            'component' => 'Magento_Ui/js/form/element/abstract',
            'config' => [
                'customScope' => 'shippingAddress.custom_attributes',
                'customEntry' => null,
                'template' => 'ui/form/field',
                'elementTmpl' => 'ui/form/element/input',
                'options' => [],
                'id' => 'custom-field-text'
            ],
            'dataScope' => 'shippingAddress.custom_attributes.custom_field_text',
            'label' => 'Custom Field Text',
            'provider' => 'checkoutProvider',
            'visible' => true,
			'validation' => [
				'required-entry' => true
			],
            'sortOrder' => 250,
            /*'customEntry' => null,*/
            'id' => 'custom-field-text'
        ];

        return $jsLayout;
    }
}

After creating these files and clear cache, a new field will show up in the shipping address form field: 

Step 4: Create our own ‘shipping-save-processor/default.js

I will rewrite the ‘module-checkout/view/frontend/web/js/model /shipping-save-processor/default.js’ with my own file view/frontend/web/js/shipping-save-processor.js.

define(
    [
        'jquery',
        'ko',
        'Magento_Checkout/js/model/quote',
        'Magento_Checkout/js/model/resource-url-manager',
        'mage/storage',
        'Magento_Checkout/js/model/payment-service',
        'Magento_Checkout/js/model/payment/method-converter',
        'Magento_Checkout/js/model/error-processor',
        'Magento_Checkout/js/model/full-screen-loader',
        'Magento_Checkout/js/action/select-billing-address'
    ],
    function (
        $,
        ko,
        quote,
        resourceUrlManager,
        storage,
        paymentService,
        methodConverter,
        errorProcessor,
        fullScreenLoader,
        selectBillingAddressAction
    ) {
        'use strict';

        return {
            saveShippingInformation: function () {
                var payload;

                if (!quote.billingAddress()) {
                    selectBillingAddressAction(quote.shippingAddress());
                }

                var customFieldText = $('[name="custom_attributes[custom_field_text]"]').val();

                payload = {
                    addressInformation: {
                        shipping_address: quote.shippingAddress(),
                        billing_address: quote.billingAddress(),
                        shipping_method_code: quote.shippingMethod().method_code,
                        shipping_carrier_code: quote.shippingMethod().carrier_code,
                        extension_attributes:{
                            custom_field_text: customFieldText                  
                        }
                    }
                };

                fullScreenLoader.startLoader();

                return storage.post(
                    resourceUrlManager.getUrlForSetShippingInformation(quote),
                    JSON.stringify(payload)
                ).done(
                    function (response) {
                        quote.setTotals(response.totals);
                        paymentService.setPaymentMethods(methodConverter(response.payment_methods));
                        fullScreenLoader.stopLoader();
                    }
                ).fail(
                    function (response) {
                        errorProcessor.process(response);
                        fullScreenLoader.stopLoader();
                    }
                );
            }
        };
    }
);

And create a ‘view/frontend/requirejs-config.js’ file, let Magento use our own javascript instead of the default.

var config = {
    "map": {
        "*": {
            'Magento_Checkout/js/model/shipping-save-processor/default': 'Nv_CheckoutCustomField/js/model/shipping-save-processor/default'
        }
    }
};

step 5: Create extension attributes ShippingInformationInterface

Create a file etc/extension_attributes.xml

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd">
    <extension_attributes for="Magento\Checkout\Api\Data\ShippingInformationInterface">
        <attribute code="custom_field_text" type="string" />
    </extension_attributes>
</config>

Step 6: Create a Plugin for save the custom field value into quote

Create a ‘etc/di.xml’ for adding additional plugin to save the value to quote.

<?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\Checkout\Model\ShippingInformationManagement">
        <plugin name="save_custom_field_to_quote_table" type="Nv\CheckoutCustomField\Plugin\Checkout\Model\ShippingInformationManagement" sortOrder="1" />
    </type>
</config>

And Create Plugin class: Plugin/Checkout/Model/ShippingInformationManagement.php

<?php
namespace Nv\CheckoutCustomField\Plugin\Checkout\Model;

use Magento\Quote\Model\QuoteRepository;

class ShippingInformationManagement
{
    protected $quoteRepository;

    public function __construct(QuoteRepository $quoteRepository) {
        $this->quoteRepository = $quoteRepository;
    }

    public function beforeSaveAddressInformation(
        \Magento\Checkout\Model\ShippingInformationManagement $subject,
        $cartId,
        \Magento\Checkout\Api\Data\ShippingInformationInterface $addressInformation
    ) {

        if(!$extAttributes = $addressInformation->getExtensionAttributes())
        {
            return;
        }

        $quote = $this->quoteRepository->getActive($cartId);

        $quote->setCustomFieldText($extAttributes->getCustomFieldText());
    }
}

Step 7: Save the value into sales order

We are going to use ‘sales_model_service_quote_submit_before’ event to save the value into sales_order tables.
So need to create etc/event.xml

<?xml version="1.0" encoding="UTF-8"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="sales_model_service_quote_submit_before">
        <observer name="custom_fields_sales_address_save" instance="Nv\CheckoutCustomField\Observer\SaveCustomFieldsInOrder" />
    </event>
</config>

And the Observer class: Observer/SaveCustomFieldsInOrder.php

<?php
namespace Nv\CheckoutCustomField\Observer;

class SaveCustomFieldsInOrder implements \Magento\Framework\Event\ObserverInterface
{
    public function execute(\Magento\Framework\Event\Observer $observer) {
        $order = $observer->getEvent()->getOrder();
        $quote = $observer->getEvent()->getQuote();

        $order->setData('custom_field_text', $quote->getCustomFieldText());

        return $this;
    }
}

Thats it, we are done. Hopefully we have covered all sections related to add field in shipping form. If we missed something please comment below and we will add it.

How to add a new field in ‘quote’ and ‘sales_order’ table magento 2 using db_schema.xml?


You can add a new column in quote and sales_order table using db_schema.xml (For Magento 2.3 and Upper version.)

Step: 1
Create file db_schema.xml under your module etc directory. 

Step: 2
Add bellow code in db_schema.xml
Change field name ‘custom_field_text’ to your field name.

<?xml version="1.0"?>
<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
    <table name="quote" resource="default" engine="innodb">
        <column xsi:type="varchar" name="custom_field_text" nullable="true" length="40" comment="Custom Field Text"/>
    </table>
    <table name="sales_order" resource="default" engine="innodb">
        <column xsi:type="varchar" name="custom_field_text" nullable="true" length="40" comment="Custom Field Text"/>
    </table>
</schema>

Step: 3
Run command from Magento root instance,
php bin/magento setup:upgrade 
Check the  ‘quote’ and ‘sales_order’ table  in Database and one new field ‘custom_field_text’ is display.

List of WordPress filters and hooks


You can get list of all registered filters and hooks in wordpress using global variables $wp_filter and $wp_actions. Please see following example.
Example 1

global $wp_filter , $wp_actions;
print_r( $wp_actions );
print_r( $wp_filter );

If you want to get list of a specific filter then use as follows example.
Example 2

gloabl $wp_filter;
$spec_filter = 'the_content';
print_r( $wp_filter[ $spec_filter ] );