Magento 2 is a strong platform where you can build custom features, and one common requirement is sending email notifications when specific events occur—such as order placement. In this blog post,
In this blog, we’ll explore how to create a custom module that sends email notifications in Magento 2 to a specific set of recipients whenever an order is placed.
ON THIS PAGE
Key Features of the Module
- Ability to enable or disable the feature via admin configuration.
- Specify multiple recipient email addresses.
- Use a custom email template to send order details.
- Observe Magento events to trigger email notifications.
Create A Custom Email Notification for New Order Placement in Magento 2
1. Module Registration
Create a registration.php file and a module.xml file to define and register the module:
app/code/Vendor/OrderEmail/registration.php
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'Vendor_OrderEmail',
__DIR__
);
app/code/Vendor/OrderEmail/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="Vendor_OrderEmail" setup_version="1.0.0">
</module>
</config>
2. Admin Configuration
Define system configuration fields using system.xml to allow admins to enable or disable the feature and specify recipient email addresses:
app/code/Vendor/OrderEmail/etc/adminhtml/system.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
<system>
<tab id="klizer" translate="label" sortOrder="400">
<label>Klizer</label>
</tab>
<section id="order_email" translate="label" type="text" sortOrder="1" showInDefault="1"
showInWebsite="1" showInStore="1">
<label>Order Email</label>
<tab>klizer</tab>
<resource>Vendor_OrderEmail::orderemail_resource</resource>
<group id="orderemail_gorup_general" translate="label" type="text" sortOrder="1" showInDefault="1"
showInWebsite="1" showInStore="1">
<label>Order Email</label>
<field id="orderemail_status" translate="label" type="select" sortOrder="1" showInDefault="1"
showInWebsite="1" showInStore="1">
<label>Enabled</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="orderemail_send_to" showInDefault="1" showInWebsite="1" showInStore="1" translate="label" sortOrder="10" type="textarea">
<label>Order Email Send to (Comma-Separated)</label>
<comment>Enter multiple email addresses separated by commas</comment>
</field>
</group>
</section>
</system>
</config>
3. Helper Class
Create a helper to fetch configuration values:
app/code/Vendor/OrderEmail/Helper/Data.php
<?php
namespace Vendor\OrderEmail\Helper;
use \Magento\Framework\App\Helper\AbstractHelper;
use Magento\Framework\App\Helper\Context;
class Data extends AbstractHelper
{
/**
* @var \Magento\Store\Model\StoreManagerInterface
*/
protected $_storeManager;
/**
* @var \Magento\Framework\App\Config\ScopeConfigInterface
*/
protected $scopeConfig;
/**
* @var \Magento\Framework\Pricing\PriceCurrencyInterface
*/
protected $_priceCurrency;
/**
* Data constructor.
* @param Context $context
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
* @param \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency
*/
public function __construct(Context $context, \Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
\Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency)
{
$this->_storeManager = $storeManager;
$this->scopeConfig = $scopeConfig;
$this->_priceCurrency = $priceCurrency;
parent::__construct($context);
}
/**
* @return mixed
*/
public function getOrderEmailStatus()
{
$orderemailstatus = $this->scopeConfig->getValue(
'order_email/orderemail_gorup_general/orderemail_status',
\Magento\Store\Model\ScopeInterface::SCOPE_STORE
);
return $orderemailstatus;
}
/**
* @return mixed
*/
public function getOrderEmail()
{
$emails = $this->scopeConfig->getValue(
'order_email/orderemail_gorup_general/orderemail_send_to',
\Magento\Store\Model\ScopeInterface::SCOPE_STORE
);
if ($emails) {
return array_map('trim', explode(',', $emails)); // Split by comma and trim spaces
}
return [];
}
}
4. Email Template
Declare the email template in email_templates.xml:
app/code/Vendor/OrderEmail/etc/email_templates.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../Email/etc/email_templates.xsd">
<template id="'send_custom_order_email'" label="Contact Form" file="order_new.html" type="html"
module="Vendor_OrderEmail" area="frontend"/>
</config>
Define the email template in order_new.html:
app/code/Vendor/OrderEmail/view/frontend/email/order_new.html
<!--
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<!--@subject {{trans "New Order Received"}} @-->
<!--@vars {
"var formattedBillingAddress|raw":"Billing Address",
"var order_data.email_customer_note|escape|nl2br":"Email Order Note",
"var order.increment_id":"Order Id",
"layout handle=\"sales_email_order_items\" order=$order area=\"frontend\"":"Order Items Grid",
"var payment_html|raw":"Payment Details",
"var formattedShippingAddress|raw":"Shipping Address",
"var order.shipping_description":"Shipping Description",
"var shipping_msg":"Shipping message",
"var created_at_formatted":"Order Created At (datetime)",
"var store.frontend_name":"Store Frontend Name",
"var store_phone":"Store Phone",
"var store_email":"Store Email",
"var store_hours":"Store Hours",
"var this.getUrl($store,'customer/account/',[_nosid:1])":"Customer Account URL",
"var order_data.is_not_virtual":"Order Type",
"var order":"Order",
"var order_id": "Order DB Id",
"var order_data.customer_name":"Customer Name"
} @-->
{{template config_path="design/email/header_template"}}
<table>
<tr class="email-intro">
<td>
<p>{{trans "A new Order has been placed on %store_name." store_name=$store.frontend_name}}</p>
<p class="greeting">{{trans "%customer_name," customer_name=$order_data.customer_name}}</p>
<p>
{{trans "Thank you for your order from %store_name." store_name=$store.frontend_name}}
{{trans "Once your package ships we will send you a tracking number."}}
{{trans 'You can check the status of your order by <a href="%account_url">logging into your account</a>.' account_url=$this.getUrl($store,'customer/account/',[_nosid:1]) |raw}}
</p>
<!-- Use default order email template code to add order detail --!>
</td>
</tr>
{{layout handle="sales_email_order_items" order_id=$order_id area="frontend"}}
</td>
</tr>
</table>
{{template config_path="design/email/footer_template"}}
5. Event Observer
Listen to the checkout_onepage_controller_success_action event to trigger the email notification.
app/code/Vendor/OrderEmail/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="checkout_onepage_controller_success_action">
<observer name="order_email" instance="Vendor\OrderEmail\Observer\OrderEmail" />
</event>
</config>
6. Observer Class
The observer processes the event and sends the email:
app/code/Vendor/OrderEmail/Observer/OrderEmail.php
<?php
namespace Vendor\OrderEmail\Observer;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\Mail\Template\TransportBuilder;
use Magento\Framework\Translate\Inline\StateInterface;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Payment\Helper\Data as PaymentHelper;
use Magento\Sales\Model\Order\Address\Renderer as AddressRenderer;
use Psr\Log\LoggerInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Klizer\OrderEmail\Helper\Data as OrderEmailHelper;
class OrderEmail implements ObserverInterface
{
/**
* @var TransportBuilder
*/
protected $transportBuilder;
/**
* @var StoreManagerInterface
*/
protected $storeManager;
/**
* @var StateInterface
*/
protected $inlineTranslation;
/**
* @var ScopeConfigInterface
*/
protected $scopeConfig;
/**
* @var OrderRepositoryInterface
*/
protected $orderRepository;
/**
* @var PaymentHelper
*/
protected $paymentHelper;
/**
* @var AddressRenderer
*/
protected $addressRenderer;
/**
* @var SearchCriteriaBuilder
*/
protected $searchCriteriaBuilder;
/**
* @var OrderEmailHelper
*/
protected $orderEmailHelper;
/**
* @var LoggerInterface
*/
private $logger;
/**
* OrderEmail constructor.
* @param TransportBuilder $transportBuilder
* @param StoreManagerInterface $storeManager
* @param StateInterface $state
* @param ScopeConfigInterface $scopeConfig
* @param OrderRepositoryInterface $orderRepository
* @param PaymentHelper $paymentHelper
* @param AddressRenderer $addressRenderer
* @param SearchCriteriaBuilder $searchCriteriaBuilder
* @param OrderEmailHelper $orderEmailHelper
* @param LoggerInterface $logger
*/
public function __construct(
TransportBuilder $transportBuilder,
StoreManagerInterface $storeManager,
StateInterface $state,
ScopeConfigInterface $scopeConfig,
OrderRepositoryInterface $orderRepository,
PaymentHelper $paymentHelper,
AddressRenderer $addressRenderer,
SearchCriteriaBuilder $searchCriteriaBuilder,
OrderEmailHelper $orderEmailHelper,
LoggerInterface $logger
)
{
$this->transportBuilder = $transportBuilder;
$this->storeManager = $storeManager;
$this->inlineTranslation = $state;
$this->scopeConfig = $scopeConfig;
$this->orderRepository = $orderRepository;
$this->paymentHelper = $paymentHelper;
$this->addressRenderer = $addressRenderer;
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
$this->orderEmailHelper = $orderEmailHelper;
$this->logger = $logger;
}
/**
* @param Observer $observer
*/
public function execute(Observer $observer)
{
$orderEmailstatus = $this->orderEmailHelper->getOrderEmailStatus();
if ($orderEmailstatus == 1) { // Check if the status is enabled
try {
$orderId = $observer->getEvent()->getOrder()->getId();
$order = $this->orderRepository->get($orderId);
$billingAddress = $order->getBillingAddress();
$shippingAddress = $order->getShippingAddress();
// Fetch company name from the company table
$companyName = '';
if ($order->getCustomerId()) {
$companyName = $this->getCompanyNameByCustomerId($order->getCustomerId());
}
$templateVars = [
'order' => $order,
'order_id' => $order->getId(),
'billing' => $billingAddress ? $billingAddress->getData() : [],
'shipping' => $shippingAddress ? $shippingAddress->getData() : [],
'store' => $order->getStore(),
'increment_id' => $order->getIncrementId(),
'created_at_formatted' => $order->getCreatedAtFormatted(2),
'formattedShippingAddress' => $this->getFormattedShippingAddress($order),
'formattedBillingAddress' => $this->getFormattedBillingAddress($order),
'payment_html' => $order->getPayment() ? $this->getPaymentHtml($order) : '',
'customer_email' => $order->getCustomerEmail(),
'customer_name' => $order->getCustomerFirstname() . ' ' . $order->getCustomerLastname(),
'order_data' => [
'customer_name' => $order->getCustomerName(),
'is_not_virtual' => $order->getIsNotVirtual(),
'email_customer_note' => $order->getEmailCustomerNote(),
'frontend_status_label' => $order->getFrontendStatusLabel()
]
];
$this->inlineTranslation->suspend();
$templateOptions = [
'area' => \Magento\Framework\App\Area::AREA_FRONTEND,
'store' => $this->storeManager->getStore()->getId(),
];
$orderEmailAddresses = $this->orderEmailHelper->getOrderEmail();
if (!empty($orderEmailAddresses)) {
foreach ($orderEmailAddresses as $email) {
try {
$transport = $this->transportBuilder
->setTemplateIdentifier('send_custom_order_email')
->setTemplateOptions($templateOptions)
->setTemplateVars($templateVars)
->setFromByScope($this->getStoreConfig('sales_email/order/identity'))
->addTo($email)
->getTransport();
$transport->sendMessage();
} catch (\Exception $e) {
$this->logger->error('Mail Not Sent to ' . $email . ': ' . $e->getMessage());
}
}
}
$this->inlineTranslation->resume();
} catch (\Exception $e) {
$this->logger->critical('Mail Not Sent: ' . $e->getMessage());
}
} else {
return; // Skip email sending if status is not enabled
}
}
/**
* @param string $storeConfigName
* @param null $storeId
* @return mixed
*/
protected function getStoreConfig(string $storeConfigName, $storeId = null)
{
return $this->scopeConfig->getValue(
$storeConfigName,
\Magento\Store\Model\ScopeInterface::SCOPE_STORE,
$storeId
);
}
/**
* @param $order
* @return string|null
*/
protected function getFormattedBillingAddress($order)
{
return $this->addressRenderer->format($order->getBillingAddress(), 'html');
}
/**
* @param $order
* @return string|null
*/
protected function getFormattedShippingAddress($order)
{
return $this->addressRenderer->format($order->getShippingAddress(), 'html');
}
/**
* @param \Magento\Sales\Model\Order $order
* @return string
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
protected function getPaymentHtml(\Magento\Sales\Model\Order $order)
{
return $this->paymentHelper->getInfoBlockHtml(
$order->getPayment(),
$this->storeManager->getStore()->getId()
);
}
}
Configuring the New Order Email Notification in Magento 2
Follow the steps below to configure the new order email notification settings in the Magento admin panel:
Step 1:
- Navigate to Stores → Configuration.
- Go to Klizer → Order Email → Order Email.
Step 2:
- Enable the Order Email by setting it to Yes.
Step 3:
- Enter the email address(es) to receive emails about new orders.
- If providing multiple email addresses, separate them with commas.
Step 4:
- Click the Save button to save the configuration
- After placing an order from the front end, the configured email address receives a notification email.
Custom Email Notification for New Order Placement in Magento 2
This custom Magento 2 module demonstrates how to leverage events and email templates to build a powerful email notification system. With proper configuration, robust error handling, and logging, this module ensures a smooth experience for store administrators.
Contact Klizer to learn how our Magento development experts can help you create a custom email notification for new order placement in Magento 2.