In this blog we’ll see how to add new step in checkout page and save data in database in Magento 2.
First You Need to Create Custom module then follow this step. I have shared a step-by-step guide to add new step on checkout page.
Step 1: Create register.php
app/code/Bluethinkinc/CustomCheckoutstep/registration.php
1 2 3 4 5 6 7 8 9 |
<?php /** * Copyright © mukesh singh All rights reserved. * See COPYING.txt for license details. */ use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Bluethinkinc_CustomCheckoutstep', __DIR__); ?> |
Step 2: Create module.xml
app/code/Bluethinkinc/CustomCheckoutstep/etc/module.xml
1 2 3 4 5 6 7 8 |
<?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="Bluethinkinc_CustomCheckoutstep"> <sequence> <module name="Magento_Checkout"/> </sequence> </module> </config> |
Step 3: Create db_schema.xml
app/code/Bluethinkinc/CustomCheckoutstep/etc/db_schema.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?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="text" name="cname" nullable="true" comment="Customer Name"/> <column xsi:type="text" name="clname" nullable="true" comment="Customer Last Name"/> <column xsi:type="text" name="cnumber" nullable="true" comment="Customer Number"/> </table> <table name="sales_order" resource="default" engine="innodb"> <column xsi:type="text" name="cname" nullable="true" comment="Customer Name"/> <column xsi:type="text" name="clname" nullable="true" comment="Customer Last Name"/> <column xsi:type="text" name="cnumber" nullable="true" comment="Customer Number"/> </table> </schema> |
Step 4: Create events.xml
app/code/Bluethinkinc/CustomCheckoutstep/etc/events.xml
1 2 3 4 5 6 |
<?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_model_service_quote_submit_before"> <observer name="custom_checkout_step_to_order" instance="Bluethinkinc\CustomCheckoutstep\Observer\SaveToOrder" /> </event> </config> |
Step 5: Create routes.xml
app/code/Bluethinkinc/CustomCheckoutstep/etc/frontend/routes.xml
1 2 3 4 5 6 7 8 |
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> <router id="standard"> <route id="newstep" frontName="newstep"> <module name="Bluethinkinc_CustomCheckoutstep" /> </route> </router> </config> |
Step 6: Create Newstep.php
app/code/Bluethinkinc/CustomCheckoutstep/Block/Adminhtml/Newstep.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<?php namespace Bluethinkinc\CustomCheckoutstep\Block\Adminhtml; use Magento\Framework\AppInterface; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Sales\Api\Data\OrderInterface; class Newstep extends \Magento\Backend\Block\Template { public $orderRepository; public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\Sales\Api\OrderRepositoryInterface $orderRepository, array $data = [] ) { $this->orderRepository = $orderRepository; parent::__construct($context, $data); } public function getOrder() { try { $orderId = $this->getRequest()->getParam('order_id'); return $this->orderRepository->get($orderId); } catch (NoSuchEntityException $e) { return false; } } } ?> |
Step 7: Create Save.php
app/code/Bluethinkinc/CustomCheckoutstep/Controller/Quote/Save.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
<?php namespace Bluethinkinc\CustomCheckoutstep\Controller\Quote; class Save extends \Magento\Framework\App\Action\Action { protected $quoteIdMaskFactory; protected $quoteRepository; public function __construct( \Magento\Framework\App\Action\Context $context, \Magento\Quote\Model\QuoteIdMaskFactory $quoteIdMaskFactory, \Magento\Quote\Api\CartRepositoryInterface $quoteRepository ) { parent::__construct($context); $this->quoteRepository = $quoteRepository; $this->quoteIdMaskFactory = $quoteIdMaskFactory; } /** * @return \Magento\Framework\Controller\Result\Raw */ public function execute() { $post = $this->getRequest()->getPostValue(); if ($post) { $cartId = $post['cartId']; $cname = $post['cname']; $clname = $post['clname']; $cnumber = $post['cnumber']; $loggin = $post['is_customer']; if ($loggin === 'false') { $cartId = $this->quoteIdMaskFactory->create()->load($cartId, 'masked_id')->getQuoteId(); } $quote = $this->quoteRepository->getActive($cartId); if (!$quote->getItemsCount()) { throw new NoSuchEntityException(__('Cart %1 doesn\'t contain products', $cartId)); } $quote->setData('cname', $cname); $quote->setData('clname', $clname); $quote->setData('cnumber', $cnumber); $this->quoteRepository->save($quote); } } } ?> |
Step 8: Create SaveToOrder.php
app/code/Bluethinkinc/CustomCheckoutstep/Observer/SaveToOrder.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?php namespace Bluethinkinc\CustomCheckoutstep\Observer; class SaveToOrder implements \Magento\Framework\Event\ObserverInterface { public function execute(\Magento\Framework\Event\Observer $observer) { $event = $observer->getEvent(); $quote = $event->getQuote(); $order = $event->getOrder(); $order->setData('cname', $quote->getData('cname')); $order->setData('clname', $quote->getData('clname')); $order->setData('cnumber', $quote->getData('cnumber')); } } ?> |
Step 9: Create sales_order_view.xml
app/code/Bluethinkinc/CustomCheckoutstep/view/adminhtml/layout/sales_order_view.xml
1 2 3 4 5 6 7 8 9 10 11 12 |
<?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceContainer name="content"> <referenceContainer name="order_additional_info"> <block class="Bluethinkinc\CustomCheckoutstep\Block\Adminhtml\Newstep" name="customfield_order_newstep" template="Bluethinkinc_CustomCheckoutstep::newstep.phtml" /> </referenceContainer> </referenceContainer> </body> </page> |
Step 10: Create newstep.phtml
app/code/Bluethinkinc/CustomCheckoutstep/view/adminhtml/templates/newstep.phtml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<?php $order = $block->getOrder(); ?> <?php if ($order) : ?> <section class="admin__page-section order-additional-information"> <div class="admin__page-section-title"> <strong class="title"><?php echo /* @escapeNotVerified */ __('New Step Information'); ?></strong> </div> <div id="<?php echo $block->getHtmlId() ?>" class="admin__page-section-content order-additional-information-container"> <div class="admin__page-section-item order-information"> <div class="admin__page-section-item-content"> <table class="admin__table-secondary order-information-table"> <tr> <th>Customer Name</th> <td><?php echo $order->getCname(); ?></td> </tr> <tr> <th>Customer Last Name</th> <td><?php echo $order->getClname(); ?></td> </tr> <tr> <th>Customer Number</th> <td><?php echo $order->getCnumber(); ?></td> </tr> </table> </div> </div> </div> </section> <?php endif; ?> |
Step 11: Create checkout_index_index.xml
app/code/Bluethinkinc/CustomCheckoutstep/view/frontend/layout/checkout_index_index.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="checkout.root"> <arguments> <argument name="jsLayout" xsi:type="array"> <item name="components" xsi:type="array"> <item name="checkout" xsi:type="array"> <item name="children" xsi:type="array"> <item name="steps" xsi:type="array"> <item name="children" xsi:type="array"> <!-- The new step you add for last step --> <item name="reviewstep" xsi:type="array"> <item name="component" xsi:type="string">Bluethinkinc_CustomCheckoutstep/js/customnew-step</item> <item name="sortOrder" xsi:type="string">1</item> <item name="children" xsi:type="array"> <!--add here child component declaration for your step--> </item> </item> </item> </item> </item> </item> </item> </argument> </arguments> </referenceBlock> </body> </page> |
Step 12: Create sales_order_view.xml
app/code/Bluethinkinc/CustomCheckoutstep/view/frontend/layout/sales_order_view.xml
1 2 3 4 5 6 7 8 |
<?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceContainer name="content"> <block class="Magento\Sales\Block\Order\Info" template="Bluethinkinc_CustomCheckoutstep::newstep.phtml"></block> </referenceContainer> </body> </page> |
Step 13: Create newstep.phtml
app/code/Bluethinkinc/CustomCheckoutstep/view/frontend/templates/newstep.phtml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ ?> <?php /** @var $block \Magento\Sales\Block\Order\Info */ ?> <?php $_order = $block->getOrder() ?> <div class="block block-order-details-view"> <div class="block-title"> <strong><?= $block->escapeHtml(__('New Step Information')) ?></strong> </div> <div class="block-content"> <div class="box box-order-shipping-address"> <strong class="box-title"><span><?= $block->escapeHtml(__('Customer Name')) ?></span></strong> <div class="box-content"> <address><?= $_order->getCname() ?></address> </div> </div> <div class="box box-order-shipping-method"> <strong class="box-title"> <span><?= $block->escapeHtml(__('Customer Last Name')) ?></span> </strong> <div class="box-content"> <?= $_order->getClname() ?> </div> </div> <div class="box box-order-shipping-method"> <strong class="box-title"> <span><?= $block->escapeHtml(__('Customer Number')) ?></span> </strong> <div class="box-content"> <?= $_order->getCnumber() ?> </div> </div> </div> </div> |
Step 14: Create customnew-step.js
app/code/Bluethinkinc/CustomCheckoutstep/view/frontend/web/js/customnew-step.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
define([ 'ko', 'uiComponent', 'underscore', 'Magento_Checkout/js/model/step-navigator', 'Magento_Checkout/js/model/quote', 'Magento_Customer/js/model/customer', 'Magento_Checkout/js/model/url-builder', 'mage/url', 'Magento_Checkout/js/model/error-processor', 'uiRegistry', 'mage/translate' ], function (ko, Component, _, stepNavigator,quote,customer,urlBuilder,urlFormatter,errorProcessor, registry,$t) { 'use strict'; /** * mystep - is the name of the component's .html template, * Bluethinkinc_CustomCheckoutstep - is the name of your module directory. */ return Component.extend({ defaults: { template: 'Bluethinkinc_CustomCheckoutstep/mystep' }, // add here your logic to display step, isVisible: ko.observable(true), /** * @returns {*} */ initialize: function () { this._super(); // register your step stepNavigator.registerStep( // step code will be used as step content id in the component template 'contact_code', // step alias null, // step title value 'Contact', // observable property with logic when display step or hide step this.isVisible, _.bind(this.navigate, this), /** * sort order value * 'sort order value' < 10: step displays before shipping step; * 10 < 'sort order value' < 20 : step displays between shipping and payment step * 'sort order value' > 20 : step displays after payment step */ 8 ); return this; }, /** * The navigate() method is responsible for navigation between checkout steps * during checkout. You can add custom logic, for example some conditions * for switching to your custom step * When the user navigates to the custom step via url anchor or back button we_must show step manually here */ navigate: function () { this.isVisible(true); }, /** * @returns void */ navigateToNextStep: function () { var quoteId = quote.getQuoteId(); var isCustomer = customer.isLoggedIn(); var url = urlFormatter.build('newstep/quote/save'); var cname=jQuery('#cname').val(); var clname=jQuery('#clname').val(); var cnumber=jQuery('#cnumber').val(); jQuery(".mage-error").remove(); if (cname=='') { var cferror=$t('Please Enter Customer First Name.'); jQuery('#cname').after('<div class="mage-error">'+cferror+'</div>'); } if (clname=='') { var clerror=$t('Please Enter Customer Last Name.'); jQuery('#clname').after('<div class="mage-error">'+clerror+'</div>'); } if (cnumber=='') { var cnerror=$t('Please Enter Customer Number.'); jQuery('#cnumber').after('<div class="mage-error">'+cnerror+'</div>'); } if(cnumber=='') { return false; }else if(clname=='') { return false; }else if(cname=='') { return false; } if (cname) { var payload = { 'cartId': quoteId, 'cname': cname, 'clname': clname, 'cnumber': cnumber, 'is_customer': isCustomer }; if (!payload.cname) { return true; } var result = true; jQuery.ajax({ url: url, data: payload, dataType: 'text', type: 'POST', }).done( function (response) { result = true; } ).fail( function (response) { result = false; errorProcessor.process(response); } ); } stepNavigator.next(); } }); }); |
Step 15: Create mystep.html
app/code/Bluethinkinc/CustomCheckoutstep/view/frontend/web/template/mystep.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<!--The 'step_code' value from the .js file should be used--> <li id="contact_code" data-bind="fadeVisible: isVisible"> <div class="step-title" data-bind="i18n: 'Contact'" data-role="title"></div> <div id="checkout-step-title" class="step-content" data-role="content"> <form data-bind="submit: navigateToNextStep" novalidate="novalidate"> <div class="actions-toolbar"> <div class="primary"> <br> <input type="text" id='cname' placeholder="First Name" class="form-control"/> <br> <br> <input type="text" id='clname' placeholder="Last Name" class="form-control"/> <br> <br> <input type="number" id='cnumber' placeholder="Contact Number" class="form-control"/> <br> <br> <button data-role="opc-continue" type="submit" class="button action continue primary"> <span><!-- ko i18n: 'Next'--><!-- /ko --></span> </button> </div> </div> </form> </div> </li> |
Step 16: After create the module if you run the command as
php bin/magento module:status
You should see the module is disable now:
List of disabled modules: Bluethinkinc_ CustomCheckoutstep
enable the module right now, let run the command as:
php bin/magento module:enable Bluethinkinc_ CustomCheckoutstep
After Enable Module Then Run this Command
sudo php bin/magento setup:upgrade
sudo php bin/magento setup:di:compile
sudo php bin/magento setup:static-content:deploy -f
sudo php bin/magento c:f
sudo chmod -R 777 var/ pub/static generated/
Now, please go checkout page and see the result.
Checkout page showing New Step and form on frontend.
Customer My account order detail page section showing new step value.
Backend order detail page showing new step value.
Mukesh Singh
2024-01-02