Hello Friends !!
In this blog, We will discuss about magento theme layout customizations related points.
So, Firstly we try to understand how we can create a custom theme in Magento 2. You can refer the our Custom Theme in Magento 2 blog to create a custom theme.
While creating a custom theme, We should always use best practices for theme development to avoid conflicts and issues with our theme after we update or upgrade our Magento instance.
You can refer our Best approach to creating a theme in Magento 2 blog to avoid conflict issues.
Now back to our topic, for layout customizations in Magento theme, We will cover the below topics:-
1. Override the Core Magento layout
In the below example, we are overriding Magento_Catalog module’s layout/catalog_category_view.xml layout to our custom theme to remove breadcumbs from category view page.
To achieve this, We create a same layout file (catalog_category_view.xml) inside the below theme directory:-
app/design/frontend/Webkul/customtheme/Magento_Catalog/layout/
<?xml version="1.0"?> <!-- /** * Webkul Software. * * @category Webkul * @package Webkul_CustomTheme * @author Webkul Software Private Limited * @copyright Webkul Software Private Limited (https://webkul.com) * @license https://store.webkul.com/license.html */ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="breadcrumbs" remove="true"/> </body> </page>
Here is the result of the mentioned code. Breadcumbs has been removed from the category view page.
2. Override the core Magento Style
In the below example, we are overriding Magento_Catalog module’s web/css/_module.less style file to our custom theme to change the colour of add to cart button.
To achieve this, We create a same style file (_module.less) inside the below theme directory:-
app/design/frontend/Webkul/customtheme/Magento_Catalog/Web/css/
@import 'source/_extend.less'; .actions-primary{ background-color: red; border:1px solid red; color:#fff; }
Here is the result of the mentioned code.
Image 1:- Without overriding the css.
Image 2:- With overriding the css. Add to cart button colour has been changed.
3. Override Core Magento Template
In the below example, we are overriding Magento_Catalog module’s templates/product/list.phtml template file to our custom theme to replace add to cart text with purchase now.
To achieve this, We create a same template file (list.phtml) inside the below directory:-
app/design/frontend/Webkul/customtheme/Magento_Catalog/templates/product/
<?php /** * Webkul Software. * * @category Webkul * @package Webkul_CustomTheme * @author Webkul Software Private Limited * @copyright Webkul Software Private Limited (https://webkul.com) * @license https://store.webkul.com/license.html */ use Magento\Framework\App\Action\Action; /** * Product list template * * @var $block \Magento\Catalog\Block\Product\ListProduct * @var \Magento\Framework\Escaper $escaper * @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer */ ?> <?php $_productCollection = $block->getLoadedProductCollection(); /** @var \Magento\Catalog\Helper\Output $_helper */ $_helper = $block->getData('outputHelper'); ?> <?php if (!$_productCollection->count()): ?> <div class="message info empty"> <div><?= $escaper->escapeHtml(__('We can\'t find products matching the selection.')) ?></div> </div> <?php else: ?> <?= $block->getToolbarHtml() ?> <?= $block->getAdditionalHtml() ?> <?php if ($block->getMode() === 'grid') { $viewMode = 'grid'; $imageDisplayArea = 'category_page_grid'; $showDescription = false; $templateType = \Magento\Catalog\Block\Product\ReviewRendererInterface::SHORT_VIEW; } else { $viewMode = 'list'; $imageDisplayArea = 'category_page_list'; $showDescription = true; $templateType = \Magento\Catalog\Block\Product\ReviewRendererInterface::FULL_VIEW; } /** * Position for actions regarding image size changing in vde if needed */ $pos = $block->getPositioned(); ?> <div class="products wrapper <?= /* @noEscape */ $viewMode ?> products-<?= /* @noEscape */ $viewMode ?>"> <ol class="products list items product-items"> <?php /** @var $_product \Magento\Catalog\Model\Product */ ?> <?php foreach ($_productCollection as $_product): ?> <li class="item product product-item"> <div class="product-item-info" id="product-item-info_<?= /* @noEscape */ $_product->getId() ?>" data-container="product-<?= /* @noEscape */ $viewMode ?>"> <?php $productImage = $block->getImage($_product, $imageDisplayArea); if ($pos != null) { $position = 'left:' . $productImage->getWidth() . 'px;' . 'top:' . $productImage->getHeight() . 'px;'; } ?> <?php // Product Image ?> <a href="<?= $escaper->escapeUrl($_product->getProductUrl()) ?>" class="product photo product-item-photo" tabindex="-1"> <?= $productImage->toHtml() ?> </a> <div class="product details product-item-details"> <?php $_productNameStripped = $block->stripTags($_product->getName(), null, true); ?> <strong class="product name product-item-name"> <a class="product-item-link" href="<?= $escaper->escapeUrl($_product->getProductUrl()) ?>"> <?=/* @noEscape */ $_helper->productAttribute($_product, $_product->getName(), 'name')?> </a> </strong> <?= $block->getReviewsSummaryHtml($_product, $templateType) ?> <?= /* @noEscape */ $block->getProductPrice($_product) ?> <?= $block->getProductDetailsHtml($_product) ?> <div class="product-item-inner"> <div class="product actions product-item-actions"> <div class="actions-primary"> <?php if ($_product->isSaleable()):?> <?php $postParams = $block->getAddToCartPostParams($_product); ?> <form data-role="tocart-form" data-product-sku="<?= $escaper->escapeHtml($_product->getSku()) ?>" action="<?= $escaper->escapeUrl($postParams['action']) ?>" data-mage-init='{"catalogAddToCart": {}}' method="post"> <?php $options = $block->getData('viewModel')->getOptionsData($_product); ?> <?php foreach ($options as $optionItem): ?> <input type="hidden" name="<?= $escaper->escapeHtml($optionItem['name']) ?>" value="<?= $escaper->escapeHtml($optionItem['value']) ?>"> <?php endforeach; ?> <input type="hidden" name="product" value="<?= /* @noEscape */ $postParams['data']['product'] ?>"> <input type="hidden" name="<?= /* @noEscape */ Action::PARAM_NAME_URL_ENCODED ?>" value="<?= /* @noEscape */ $postParams['data'][Action::PARAM_NAME_URL_ENCODED] ?>"> <?= $block->getBlockHtml('formkey') ?> <button type="submit" title="<?= $escaper->escapeHtmlAttr(__('Purchase Now')) ?>" class="action tocart primary" disabled> <span><?= $escaper->escapeHtml(__('Purchase Now')) ?></span> </button> </form> <?php else:?> <?php if ($_product->isAvailable()):?> <div class="stock available"> <span><?= $escaper->escapeHtml(__('In stock')) ?></span></div> <?php else:?> <div class="stock unavailable"> <span><?= $escaper->escapeHtml(__('Out of stock')) ?></span></div> <?php endif; ?> <?php endif; ?> </div> <?= ($pos && strpos($pos, $viewMode . '-primary')) ? /* @noEscape */ $secureRenderer->renderStyleAsTag( $position, 'product-item-info_' . $_product->getId() . ' div.actions-primary' ) : '' ?> <div data-role="add-to-links" class="actions-secondary"> <?php if ($addToBlock = $block->getChildBlock('addto')): ?> <?= $addToBlock->setProduct($_product)->getChildHtml() ?> <?php endif; ?> </div> <?= ($pos && strpos($pos, $viewMode . '-secondary')) ? /* @noEscape */ $secureRenderer->renderStyleAsTag( $position, 'product-item-info_' . $_product->getId() . ' div.actions-secondary' ) : '' ?> </div> <?php if ($showDescription): ?> <div class="product description product-item-description"> <?= /* @noEscape */ $_helper->productAttribute( $_product, $_product->getShortDescription(), 'short_description' ) ?> <a href="<?= $escaper->escapeUrl($_product->getProductUrl()) ?>" title="<?= /* @noEscape */ $_productNameStripped ?>" class="action more"><?= $escaper->escapeHtml(__('Learn More')) ?></a> </div> <?php endif; ?> </div> </div> </div> <?= ($pos && strpos($pos, $viewMode . '-actions')) ? /* @noEscape */ $secureRenderer->renderStyleAsTag( $position, 'product-item-info_' . $_product->getId() . ' div.product-item-actions' ) : '' ?> </li> <?php endforeach; ?> </ol> </div> <?= $block->getChildBlock('toolbar')->setIsBottom(true)->toHtml() ?> <?php // phpcs:ignore Magento2.Legacy.PhtmlTemplate ?> <?php endif; ?>
Here is the result of the mentioned code. Add to cart text has been replaced with Purchase Now.
4. Override Core Magento js
In the below example, we are overriding Magento_Checkout module’s web/js/view/payment/default.js file to our custom theme to add confirmation to place order button on click action.
To achieve this, We create a same js file (default.js) inside the below directory:-
app/design/frontend/Webkul/customtheme/Magento_Checkout/web/js/view/payment/
/** * Webkul Software. * * @category Webkul * @package Webkul_CustomTheme * @author Webkul Software Private Limited * @copyright Webkul Software Private Limited (https://webkul.com) * @license https://store.webkul.com/license.html */ define([ 'ko', 'jquery', 'uiComponent', 'Magento_Checkout/js/action/place-order', 'Magento_Checkout/js/action/select-payment-method', 'Magento_Checkout/js/model/quote', 'Magento_Customer/js/model/customer', 'Magento_Checkout/js/model/payment-service', 'Magento_Checkout/js/checkout-data', 'Magento_Checkout/js/model/checkout-data-resolver', 'uiRegistry', 'Magento_Checkout/js/model/payment/additional-validators', 'Magento_Ui/js/model/messages', 'uiLayout', 'Magento_Checkout/js/action/redirect-on-success', 'Magento_Ui/js/modal/confirm' ], function ( ko, $, Component, placeOrderAction, selectPaymentMethodAction, quote, customer, paymentService, checkoutData, checkoutDataResolver, registry, additionalValidators, Messages, layout, redirectOnSuccessAction, confirmation ) { 'use strict'; return Component.extend({ redirectAfterPlaceOrder: true, isPlaceOrderActionAllowed: ko.observable(quote.billingAddress() != null), /** * After place order callback */ afterPlaceOrder: function () { // Override this function and put after place order logic here }, /** * Initialize view. * * @return {exports} */ initialize: function () { var billingAddressCode, billingAddressData, defaultAddressData; this._super().initChildren(); quote.billingAddress.subscribe(function (address) { this.isPlaceOrderActionAllowed(address !== null); }, this); checkoutDataResolver.resolveBillingAddress(); billingAddressCode = 'billingAddress' + this.getCode(); registry.async('checkoutProvider')(function (checkoutProvider) { defaultAddressData = checkoutProvider.get(billingAddressCode); if (defaultAddressData === undefined) { // Skip if payment does not have a billing address form return; } billingAddressData = checkoutData.getBillingAddressFromData(); if (billingAddressData) { checkoutProvider.set( billingAddressCode, $.extend(true, {}, defaultAddressData, billingAddressData) ); } checkoutProvider.on(billingAddressCode, function (providerBillingAddressData) { checkoutData.setBillingAddressFromData(providerBillingAddressData); }, billingAddressCode); }); return this; }, /** * Initialize child elements * * @returns {Component} Chainable. */ initChildren: function () { this.messageContainer = new Messages(); this.createMessagesComponent(); return this; }, /** * Create child message renderer component * * @returns {Component} Chainable. */ createMessagesComponent: function () { var messagesComponent = { parent: this.name, name: this.name + '.messages', displayArea: 'messages', component: 'Magento_Ui/js/view/messages', config: { messageContainer: this.messageContainer } }; layout([messagesComponent]); return this; }, /** * Place order. */ placeOrder: function (data, event) { var self = this; confirmation({ title: 'Order Confirmation', content: 'Do you want to place this order?', actions: { confirm: function () { if (event) { event.preventDefault(); } if (self.validate() && additionalValidators.validate() && self.isPlaceOrderActionAllowed() === true ) { self.isPlaceOrderActionAllowed(false); self.getPlaceOrderDeferredObject() .done( function () { self.afterPlaceOrder(); if (self.redirectAfterPlaceOrder) { redirectOnSuccessAction.execute(); } } ).always( function () { self.isPlaceOrderActionAllowed(true); } ); return true; } }, cancel: function () { return false; } } }); return false; }, /** * @return {*} */ getPlaceOrderDeferredObject: function () { return $.when( placeOrderAction(this.getData(), this.messageContainer) ); }, /** * @return {Boolean} */ selectPaymentMethod: function () { selectPaymentMethodAction(this.getData()); checkoutData.setSelectedPaymentMethod(this.item.method); return true; }, isChecked: ko.computed(function () { return quote.paymentMethod() ? quote.paymentMethod().method : null; }), isRadioButtonVisible: ko.computed(function () { return paymentService.getAvailablePaymentMethods().length !== 1; }), /** * Get payment method data */ getData: function () { return { 'method': this.item.method, 'po_number': null, 'additional_data': null }; }, /** * Get payment method type. */ getTitle: function () { return this.item.title; }, /** * Get payment method code. */ getCode: function () { return this.item.method; }, /** * @return {Boolean} */ validate: function () { return true; }, /** * @return {String} */ getBillingAddressFormName: function () { return 'billing-address-form-' + this.item.method; }, /** * Dispose billing address subscriptions */ disposeSubscriptions: function () { // dispose all active subscriptions var billingAddressCode = 'billingAddress' + this.getCode(); registry.async('checkoutProvider')(function (checkoutProvider) { checkoutProvider.off(billingAddressCode); }); } }); });
Here is the result of the mentioned code. The confirmation has been added on place order button click.
That’s all about the Magento Theme : Layout customizations. Hope this will be helpful.
If you have any questions please comment below, and we will try to respond to you.
Thanks! 🙂
Be the first to comment.