Throughout the lifespan of Laravel and more specifically, Laravel 9.35, Taylor Otwell (Creator of Laravel) decided to add in a new way to send emails. This was in the form of “Envelopes” which has a really nice syntax. Their were no breaking changes to how you sent emails previously either. This guide will show you how to create Envelopes, and send them using the Mail Object within Laravel. I will assume you have a knowledge of setting up a Laravel project for this guide.
Setting up a Test Mailbox
There are lots of options out there for testing the sending and receiving of emails. The one I commonly use is Mailtrap.io but there are many other options available. To name a couple you have Amazon SES and Mailgun. As I use mailtrap, I will be using it for the remainder of the guide.
Before we are able to start sending emails, we need to configure our test email environment to work with Laravel. This will be done in our .env
file. Thankfully, Mailtrap provides you with all the necessary credentials out of the box. We just need to select “Laravel 9+” from the drop down. Note: To get your unfiltered password you would click on “Show Credentials”
After we have our credentials, we need to update our .env
in our project so we can tell Laravel where to send the emails. This is rather quick as you will mostly need to copy and paste above credentials. Once you have, it should look something like the snippet below, without a masked password.
# Other Environment Variables
MAIL_MAILER=smtp
MAIL_HOST=sandbox.smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=cc870e48aec7b8
MAIL_PASSWORD=de********98
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS="demo@example.com"
MAIL_FROM_NAME="${APP_NAME}"
# Even more Environment Variables
With this done you will need to quickly run php artisan optimize:clear
so that the new values can be identified by Laravel config. Now we can get on with the fun stuff.
Making a simple form to test emails
For this guide, I am going to be using a simple form. It’ll use tailwind as that is what comes out of the box with a fresh Laravel Breeze Installation nowadays. Below is the HTML for the form we are going to be using.
<form class="w-1/3" action="{{ route('email.send') }}" method="post">
@csrf
<div class="mb-4">
<label class="block text-gray-700 text-sm font-bold mb-2">Name</label>
<input name="name" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight" type="text">
</div>
<div class="mb-6">
<label class="block text-gray-700 text-sm font-bold mb-2">Email Address</label>
<input name="email" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight" type="email">
</div>
<div class="mb-6">
<label class="block text-gray-700 text-sm font-bold mb-2">Message</label>
<textarea name="message" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight h-20"></textarea>
</div>
<div class="flex items-center justify-between">
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" type="submit">Send Message</button>
</div>
</form>
The complete form should look something like the below screenshot if done correctly.
Adding Controller & Route to submit form
Now we have our form, you can see that we want to submit the form using a POST request to route('email.send')
. We are going to need to setup a controller and a route to accept this form. Firstly, we will start with the controller.
The Controller
Within your IDE console, or terminal, you will want to run the following artisan command php artisan make:controller EmailController
to generate a default stub in your app/Http/Controllers
directory.
Inside this controller lets make a simple function called send()
. Now we have the basic controller setup we need a route to interact with this function and controller.
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Mail\NewMessageReceivedEmail;
use Exception;
use Illuminate\Support\Facades\Mail;
class EmailController extends Controller
{
public function send( Request $request )
{
// Email Send Code Here
}
}
The Route
Open your routes/web.php
file as you are going to want to add the following route. In production, please ensure you have secured this behind the correct auth middleware if necessary.
Route::post('/email/send', [ EmailController::class, 'send' ])->name('email.send');
Once we have added the route, we need to ensure Laravel knows it exists, so once again we are going to run the php artisan optimize:clear
command in our console. This will clear the previous route caching and tell Laravel to look at the file again and cache anything new.
Putting it all together
With the Route & Controller now made, we can focus on the actual Controller code and Envelope so we can send an Email. To make the envelope you are going to need to run one more artisan command in your console/terminal, and that is php artisan make:mail NewMessageReceivedEmail
. We now have all the files we need to send an email and all the setup mostly complete.
Now, In your controller you are going to want to validate and try to send your email. The send()
function code should look something like below snippet. Note; You can always refactor the validation into a form request if you wish.
Final send()
method code
public function send( Request $request )
{
$request->validate([
'email' => 'required|email',
'name' => 'required',
'message' => 'required'
]);
$data = [
'name' => $request->name,
'email' => $request->email,
'message' => $request->message
];
try {
Mail::to(config('mail.from.address'))->send(new NewMessageReceivedEmail($data));
} catch (Exception $e) {
return redirect()->back()->with([
'success' => false,
'message' => 'Email not sent!. Reason: ' . $e->getMessage()
]);
}
return redirect()->back()->with([
'success' => true,
'message' => 'Email sent successfully!'
]);
}
To explain what is happening in this controller, I will break it down.
- We are validating the values coming in from the form you submitted. This ensures no erroneous data is sent to your server, protecting you from malicious attempts at SQL Injection. ALWAYS validate any incoming data, even from sources you trust!
- We are compiling the data into a small
$data
array. This makes it easier to pass to the Mail Object. - Instantiating the Mail Object and injecting our new email file we made with the Artisan command previously. Note how we are doing this in a try/catch block. This is so we can easily understand errors should they happen. If this works, this is where our Envelope Magic works.
The final envelope code
The final part of this is setting up the EnvelopeYou will notice we sent the $data
array to the NewMessageReceivedEmail
Object. This is so we can access the data inside the envelope.
If you open the file NewMessageReceivedEmail.php
you will see a bunch of boiler plate that we are going to add too. Your file should look similar to the below envelope code. To make your $data
values available, you can inject them using the constructor.
class NewMessageReceivedEmail extends Mailable
{
use SerializesModels;
/**
* Create a new message instance.
*/
public function __construct( protected $data )
{
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
from: $this->data['email'],
to: config('mail.from.address'),
replyTo: $this->data['email'],
subject: 'New Message Received Email',
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'emails.new-message-received',
with: [
'data' => $this->data
]
);
}
}
The final parts of this is setting the envelope
values. For a simple email like this, you likely only need from
, to
, replyTo
and subject
. These values are set in the envelope()
method using named arguements. As these are named arguments, the order you specify these does not matter.
Finally, we want to ensure that the content()
method points to a view that can be sent via email, and you want to make the data available by leveraging the with
variable passing along the data you sent to the envelope.
The view that is sent in the case of this simple email is below.
<div>
<p>Hi, you have a new message from {{ $data['name'] }}.</p>
<p>Message: {{ $data['message'] }}</p>
<p>Thank you</p>
</div>
Sending your first Envelope
With everything setup, you should now be able to complete the form we made at the start of this guide, therefore, lets go to our form and fill it in.
Now, all we need to do is click on the ‘Send Message’ button and let all the code we have written do it’s magic. If the email is sent successfully, you should be able to check your mailtrap account and see the email.
NOTE: All form submissions, simple, complex or otherwise should always be protected with a form of reCaptcha. A previous post I made here will explain how to implement Googles V3 Recaptcha
Closing Words
Sending emails in Laravel after the initial setup is really a beautiful experience. It’s simple, quick and easy to understand. If you found this guide useful, I provide mostly daily tips over on my X/Twitter Account and a simple follow would be most appreciated.
If you have any issues with this, my X/Twitter DM’s are always open and I am happy to help if you are struggling along the way!
[…] This guide is going to refactor the controller based validation that we see in my guide for from a few days ago; Laravel 10: Sending Emails with Envelopes […]
[…] second exciting feature is email. Previously, you may have seen an article I wrote about how to use mailtrap.io as a developer inbox to test emails to ensure formatting was correct. Well, Herd pro now has mail functionality that is […]
Comments are closed.