Sending Custom Emails using Custom Module in Magento 2 (Updated)

Sending Custom Emails using Custom Module in Magento 2

Emails are an essential part of any e-commerce website. Magento 2 by default provides many emails templates. You can check all the available email templates from Magento 2 admin panel under Marketing > Communications > Email Templates. Click “Add New Template”. Under “Load default template” you can see the Template list.

We can override any of this default template as per our need. But what if we want to create our own unique template to sending custom emails from a custom module. Well, In this article we are going to create new sample module and in that module, we will create a form, on submit of that form we will send form information by sending a custom email. This email will use custom email template.

Module Setup

Before we start creating files for the modules. Let’s look at the module structure and important files at a glance. We will create a module named “Customemail”.
 

app/code/Codextblog/Customemail/etc/module.xml – This file is a module configuration file.

app/code/Codextblog/Customemail/registration.php – This file is module registration file that register our module in magento 2 system.

app/code/Codextblog/Customemail/etc/frontend/routes.xml – This file is responsible for frontend routing of our module

app/code/Codextblog/Customemail/Controller – This directory contains controller files

app/code/Codextblog/Customemail/Block – This directory contain block php file which is responsible for view logic.

app/code/Codextblog/Customemail/view/frontend/layout – This directory contain layout xml file.

app/code/Codextblog/Customemail/view/frontend/templates – This directory contain template file which render HTML.

app/code/Codextblog/Customemail/view/frontend/email – This directory contain email template which is use for sending custom email in our module

Getting Started: Development

Step 1: Create module.xml file under app/code/Codextblog/Customemail/etc directory

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/module.xsd">
    <module name="Codextblog_Customemail" setup_version="1.0.0"></module>
        <sequence>
            <module name="Magento_Backend"/>
             <module name="Magento_Sales"/>
            <module name="Magento_Quote"/>
            <module name="Magento_Checkout"/>>
        </sequence>
</config>

Step 2: Create registration.php file under app/code/Codextblog/Customemail/

<?php

\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Codextblog_Customemail',
    __DIR__
);

Step 3: Create frontend router file routes.xml under app/code/Codextblog/Customemail/etc/frontend

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../lib/internal/Magento/Framework/App/etc/routes.xsd">
    <router id="standard">
        <route id="customemail" frontName="customemail">
            <module name="Codextblog_Customemail" />
        </route>
    </router>
</config>

we have defined the frontend name of our module to “customemail”. Whenever we visit www.example.com/customemail/controllername It will look for controller file from our module.

Now Let’s create a controller file.

Step 4: Create Index.php file under app/code/Codextblog/Customemail/Controller/Index directory

<?php

namespace Codextblog\Customemail\Controller\Index;

class Index extends \Magento\Framework\App\Action\Action
{
    public function execute()
    {

        $this->_view->loadLayout();
        $this->_view->getLayout()->initMessages();
        $this->_view->renderLayout();
    }
}

Here we have created Index controller hence, our frontend URL will be like www.example.com/customemail/index/

Now let’s create block and frontend view files.

Step 5: Create Index.php block file under app/code/Codextblog/Customemail/Block/Index directory

<?php

namespace Codextblog\Customemail\Block\Index;


class Index extends \Magento\Framework\View\Element\Template {

    public function __construct(\Magento\Catalog\Block\Product\Context $context, array $data = []) {

        parent::__construct($context, $data);

    }


    protected function _prepareLayout()
    {
        return parent::_prepareLayout();
    }

}

Step 6: Create customemail_index_index.xml layout fiel under app/code/Codextblog/Customemail/view/frontend/layout directory

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">

	<head>
        <title>Custom Contact Form</title>
    </head> 

    <body>

		<referenceContainer name="content">
            <block class="Codextblog\Customemail\Block\Index\Index" name="customemail_index_index" template="Codextblog_Customemail::customemail_index_index.phtml"/>
        </referenceContainer>

	</body>
</page>

Step 7: Create template file customemail_index_index.phtml under app/code/Codextblog/Customemail/view/templates directory. In this file we are define html form that post data to our controller.

<form action="<?php echo $block->getBaseUrl().'customemail/index/post/';?>" name="customemaildata" method="post" id="contactForm-1" data-hasrequired="<?php echo __('* Required Fields') ?>" data-mage-init='{"validation":{}}'>
	<fieldset class="fieldset">
			<div class="field email required">
                <label class="label" for="email"><span><?php  echo __('Name') ?></span></label>
                <div class="control">
                    <input name="name" id="name" title="<?php echo __('Name') ?>" class="input-text" type="text" data-validate="{required:true}"/>
                </div>
            </div>
        	<div class="field email required">
                <label class="label" for="email"><span><?php  echo __('Email') ?></span></label>
                <div class="control">
                    <input name="email" id="email" title="<?php echo __('Email') ?>" class="input-text" type="email" data-validate="{required:true, 'validate-email':true}"/>
                </div>
            </div>                        
	</fieldset>
	<div class="actions-toolbar">
        <div class="primary">
            <input type="hidden" name="hideit" id="hideit" value="" />
            <button type="submit" title="<?php echo __('Submit') ?>" class="action submit primary">
                <span><?php echo __('Submit') ?></span>
            </button>
        </div>
    </div>
</form>

Step 8: Our form is submitting data to post action so let’s create Post.php under app/code/Codextblog/Customemail/Controller directory

<?php
namespace Codextblog\Customemail\Controller\Index;

use Zend\Log\Filter\Timestamp;
use Magento\Store\Model\StoreManagerInterface;

class Post extends \Magento\Framework\App\Action\Action
{
    const XML_PATH_EMAIL_RECIPIENT_NAME = 'trans_email/ident_support/name';
    const XML_PATH_EMAIL_RECIPIENT_EMAIL = 'trans_email/ident_support/email';
    
    protected $_inlineTranslation;
    protected $_transportBuilder;
    protected $_scopeConfig;
    protected $_logLoggerInterface;
    protected $storeManager;
    
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation,
        \Magento\Framework\Mail\Template\TransportBuilder $transportBuilder,
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
        \Psr\Log\LoggerInterface $loggerInterface,
        StoreManagerInterface $storeManager
        array $data = []
        
        )
    {
        $this->_inlineTranslation = $inlineTranslation;
        $this->_transportBuilder = $transportBuilder;
        $this->_scopeConfig = $scopeConfig;
        $this->_logLoggerInterface = $loggerInterface;
        $this->messageManager = $context->getMessageManager();
        $this->storeManager = $storeManager;
        
        parent::__construct($context);
        
        
    }
    
    public function execute()
    {
        $post = $this->getRequest()->getPost();
        try
        {
            // Send Mail
            $this->_inlineTranslation->suspend();
                        
            $sender = [
                'name' => $post['name'],
                'email' => $post['email']
            ];
            
            $sentToEmail = $this->_scopeConfig ->getValue('trans_email/ident_general/email',\Magento\Store\Model\ScopeInterface::SCOPE_STORE);
            
            $sentToName = $this->_scopeConfig ->getValue('trans_email/ident_general/name',\Magento\Store\Model\ScopeInterface::SCOPE_STORE);
            
            
            $transport = $this->_transportBuilder
            ->setTemplateIdentifier('customemail_email_template')
            ->setTemplateOptions(
                [
                    'area' => 'frontend',
                    'store' => $this->storeManager->getStore()->getId()
                ]
                )
                ->setTemplateVars([
                    'name'  => $post['name'],
                    'email'  => $post['email']
                ])
                ->setFromByScope($sender)
                ->addTo($sentToEmail,$sentToName)
                //->addTo('owner@example.com','owner')
                ->getTransport();
                
                $transport->sendMessage();
                
                $this->_inlineTranslation->resume();
                $this->messageManager->addSuccess('Email sent successfully');
                $this->_redirect('customemail/index/index');
                
        } catch(\Exception $e){
            $this->messageManager->addError($e->getMessage());
            $this->_logLoggerInterface->debug($e->getMessage());
            exit;
        }
        
        
        
    }
}

Under execute method we have written email send code using a “transportBuilder” object which we have injected in our constructor method. Using this object we are calling several methods to set require email parameters. The first method “setTemplateIdentifier” set email template identifier to “customemail_email_template” which is our custom email template identifier which we will define in next step. Second method “setTemplateOptions” set options ‘frontend’ and current store code. If we would send email from backend module then we set ‘admin’. Third method “setTemplateVars” set the variables which we can call in our email template. Further, we have called “setFrom” and “addTo” methods which are sets receipt and sender emails.

Step 9: Declare email template in email_templates.xml file Under app/code/Codextblog/Customemail/etc directory

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Email:etc/email_templates.xsd">

	<template id="customemail_email_template" label="Email Form" file="customemail.html" type="html" module="Codextblog_Customemail" area="frontend"/>
	
</config>

Here we have declared our email template “customemail.html” with id “customemail_email_template”. This id need to pass in “setTemplateIdentifier” function. In Step 7, we have done the same. Now let’s create actual email template HTML file.

Step 10: Create customemail.html under app/code/Codextblog/Customemail/view/frontend/email directory

<!--@subject Sending email from my custom module @-->

{{template config_path="design/email/header_template"}}
<table>
	 <tr class="email-intro">
		 <td>
		 Email: {{var email}}
		 </td>
		 <td>
		 Name: {{var name}}
		 </td>
	 </tr>
 </table>

{{template config_path="design/email/footer_template"}}

Want to test this mail in your localhost? follow this link.
If this post helps you, then please like us on Facebook and follow us on Twitter.

Want to ask a question or leave a comment?

Leave a Comment

(62 Comments)

  • Akshay

    When I’m running this Module I got a blank page of custom email so how can I get form

  • Vishal

    How can we append styles to custom email template, i tried added custom classes but all classes disappear when they passed to email and when we receive that email.

  • matteo

    how can i add attachment to email?

  • lucas

    I’m having the main.DEBUG error: Unable to send mail: Unknown error [] [], and in post.php it points out that in the StoreManagerInterface $ storeManager constructor
    array $ data = [] is wrong. Has anyone had this problem?

  • lucas

    Is it necessary to activate php mail ()?

    • Chirag

      Yes

  • Jonathan Dacosta

    How can I send attachments in this form?

  • Rajan Panchal

    Can you Please help me to fix the issue of [2021-01-12 14:36:25] main.CRITICAL: Invalid template file: ‘Codextblog_Customemail::customemail_index_index.phtml’ in module: ‘Codextblog_Customemail’ block’s name: ‘customemail_index_index’ [] [].
    Also i am not able to showing the Form.
    Can you let me know what is the issue i need to fix this on the urgent bases.
    Please help me!!

    • Chirag

      Make sure the template path mentioned in the layout file is correct.

  • Soumita

    Hello I followed this tutorial. I am able to access the form and submit it. I am also getting the email sent successfully message but didn’t receive any email. Did you use the general email from stores->settings->store addresses->general contact to receive the email ? Any help will be appreciated. Thanks in advance.

    • Chirag

      Yes your are right. It’s sending email to general contact.

  • Mubashir Ali

    How to implement this module? I mean, how to make it work or what triggers this module?

  • Lee

    Thankyou for this tutorial, I have managed to get the form on screen but when I submit I get a blank screen on customemail/index/post/ page? I’m testing on my localhost

    • Chirag

      Have you checked Magento log files? May be there is a exception or php error due to that you are getting blank page. If not found in magento log then check apache log.

    • Lee

      This is now working (ish), I can send the form, I get the email sent successfully message but it’s sending to owner@example.com and not the store email address that all emails should send to?

      • Chirag

        This can be changed. Check the sendToEmail variable where I have set the store email address. You can change that line as per your need.

        • Lee

          Thankyou, I have done that and it nows sends to my mailbox. Thankyou for your help

        • Lee

          When I try to add new fields the form does not send, do you have an email I can send you with what I’ve done to see if I’m doing something wrong?

      • Anil

        Is SMTP configuration needed for this?

        • Chirag

          No. But if your Magento not sending any system mail then you should try SMTP

  • Pritesh Saxena

    Don’t know why but customemail/index/post/ this controller shows 302 status code in Network Magento 2 ?

  • Pritesh Saxena

    I have created your module and it shows the form when on sending the i didn’t receive any mail in my mailbox.
    I have used send-grid for sending the male.
    Have you any idea how to get it working ?

    • Pritesh Saxena

      Don’t know why bit customemail/index/post/ that controller shows 302 status code in Network will you please help ?

      • Chirag

        Please check the controller name and its router in routes.xml

    • Chirag

      No. I have tested this code with default magento and its working. Not tested with sendgrid.

  • neeraj

    Hi email field value not send add multiple field send email all field value but email field value balnk in mail

    • Chirag

      Can you please clarify what you want to achieve? Your comment is not able to be understood.

      • neeraj

        add multiple field in form after submit send all field value in mail but email field value empty send

        • Chirag

          You have to debug Post.php controller to check whether you are getting email field value in post or not.

  • Rashi Goyal

    Thanks for sharing. Nice tutorial

    But I think that folder templates must be inside view >> frontend folder instead of view folder.

  • Rashi Goyal

    Thanks for sharing. Nice tutorial

    But I think that file in step 8 i.e. Post.php must be inside app/code/Codextblog/Customemail/Controller/Index directory. Also folder templates must be inside view > frontend instead of view folder.

  • manoj bisht

    Thanks brother..It helped me a lot.. I have used this one in product detailed page for ‘report wrong product’ module..Thanks again

  • Ashish

    I am not getting success messages or error messages on this form.

    • Chirag

      Please check log file.

  • sandeep

    When i have create this module and enable the modulle and open the link mystore.com/customemailindex/ the page is show blank no content and error is show please help me to solve this issue and the email template is show in the admin

    • Chirag

      Please check the error log of that page and tell little bit about the error to find the issue.

  • chandan kumar

    Form is not showing i have followed all the step from begining but not showing form

  • Andy Allen

    Why are you suspending inlineTranslation?

    this->_inlineTranslation->suspend();

  • Manisha

    Very nice tutorial for custom email template..It’s working perfectly.

  • ashish

    hello i am getting this error Email template ‘customemail_email_template’ is not defined. when i am sending values to admin

  • Alex

    I’ve managed to get it working!!!
    The thing was that Post.php file should be under “app/code/Codextblog/Customemail/Controller/Index” directory, not in the “app/code/Codextblog/Customemail/Controller”.
    Thanks a lot for your helpful tutorial again!

    • Chirag

      Glad to hear you finally got it working. You should very careful while creating module. One small typo or wrong directory can also lead to big error that is very hard to find

      I personally suggest all the developer to create module from beginning when you encounter big unknown errors. That will make you realize any missing file or typo mistake.

  • Alex

    Finally got it working after creating all files from the very beginning, form is showing up at url my.domain.com/customemail/index, but after submitting redirects to 404-page, maybe you have any guess what could it be?

  • Alex

    First of all, thank you for the very detailed tutorial!
    But after accomplishing all the step, compiling setup and having rechecked everything twice, I still having an error when trying to load the page: my.domain.com/customemail/index/

    Here is the error message:
    Exception #0 (Magento\Framework\Config\Dom\ValidationException): Element ‘block’, attribute ‘class’: [facet ‘pattern’] The value ‘NRG\customemail\Block\Index\Index’ is not accepted by the pattern ‘[A-Z][_a-zA-Z\d]*(\\[A-Z][_a-zA-Z\d]*)*’.
    Line: 804

    Element ‘block’, attribute ‘class’: ‘NRG\customemail\Block\Index\Index’ is not a valid value of the atomic type ‘blockClassType’.
    Line: 804

    Would be grateful if you can give some idea why the error occurs…

  • larp

    Error filtering template: Invalid template file: ‘Codextblog_Customemail::customemail_index_index.phtml’ in module: ‘Codextblog_Customemail’ block’s name: ‘index\index_0’.. I am new to magento please help me.

    • Chirag Dodia

      I have just modified the path of email template’s file. Please correct the file path in your code and check again.

  • larp

    No display form..
    I paste this in Page I want to display it.

    but no form is display.

    • Chirag Dodia

      Is there any specific error are you facing on form page? Please check each and every files path and make sure it is correct.

  • larp

    Hello i followed completely your tutorial, now, how to display the custom contact us?

    I paste this on page where I want to display it but nothing happens.

    No Form is displaying.

  • Minaxi

    Hello
    I m using this above code but it shows me error like this
    Uncaught TypeError: Argument 2 passed to VimirLab\HelpDesk\Controller\Index\Save::__construct() must implement interface Magento\Framework\Translate\Inline\StateInterface, none given, called

    • Chirag Dodia

      Please make sure you have added the dependency of \Magento\Framework\Translate\Inline\StateInterface in your construct method same like shows in this post. If you have already included the dependency you just need to run below three command
      php bin/magento setup:upgrade
      php bin/magento setup:di:compile
      php bin/magento setup:static-content:deploy

      After running these command please clear your cache and check.

  • Ajith Kumar

    Thank you for your guidance. It perfectly works for me in magento 2 running in localhost. Keep Doing Your Work..Good Luck Dude.

  • Juliano

    Hello.
    I have followed your post thanks by the way. But Like LadyKea I received the email but not errror however no content either not even harded coded content in customemail.html

    • larp

      Hello use getPostValue() instead of getPost() in Post.php. Hope it helps!

  • TC Buddy

    Hi, I followed your post but I end up with a 404 error, new to magento, plz assist or could you provide a download link to this code that you have tested?

    • LadyKea

      Please help, followed along but form does nothing, no message, no errors just nothing.

      • larp

        Try to upload it on live server. The codes are working. I just modified some codes.

        • pragnesh

          what change you done i have same error plz help me!!!

  • All the comments are goes into moderation before approval. Irrelevant comment with links directly goes to spam. Your email address will not be published.

    Was this post helpful? Please support Us!

    Follow us on twitter or Like us on facebook.

     


    To Avoid Spam Downloads, We Want Your Email

    We will send you download link right
    away. Please submit form below.
    SEND ME DOWNLOAD LINK
    Close

    Increase Your
    Magento 2
    Knowledge

    Get Weekly Tutorial
    to your Inbox
    Subscribe Me
    close-link
    Subscribe Here