Email Templates from Database

Have you ever had the need to have more dynamic emails? We've had and it's usually the same request that comes from our clients and it goes something like this: "I would like to create an email template in our system, but I also want to be able to automatically replace {{name}} with our customer's full name, is that possible?".

The answer is, of course, yes, it's possible and this short tutorial will guide you through the process of creating a very simple engine for sending email templates stored in the database.

The similar functionality is very popular with mail services like Mailchimp, Mailgun etc. so it's something users expect to see in their apps as well.

First, let's create a table where we'll store our email templates.

<?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateEmailTemplatesTable extends Migration { public function up() { Schema::create('email_templates', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->string('subject'); $table->text('content'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('email_templates'); } }

The next step is to create a model EmailTemplate.php

<?php class EmailTemplate extends Eloquent { protected $table = 'email_templates'; }

Ok, now we have a model, the next thing is to write a test that will fail, and our job will be to make it pass

class EmailTemplateTest extends TestCase { public function testParseContent() { $template = new EmailTemplate; $template->content = "Hi {{firstname}} {{lastname}}"; $data = [ 'firstname' => 'John', 'lastname' => 'Doe' ]; $parsed = $template->parse($data); $this->assertEquals('Hi John Doe', $parsed); } }

In our EmailTemplate model, we'll add a parse method:

public function parse($data) { $parsed = preg_replace_callback('/{{(.*?)}}/', function ($matches) use ($data) { list($shortCode, $index) = $matches; if( isset($data[$index]) ) { return $data[$index]; } else { throw new Exception("Shortcode {$shortCode} not found in template id {$this->id}", 1); } }, $this->content); return $parsed; }

And the test will now pass. The parse method is our "engine" and it's very simple as you can see, all it does it parses the email template's content, check for any "shortcode" inside brackets and replaces that shortcode with the data we present to it.

We can also test if an exception will be thrown if a shortcode does not exist

public function testNonExistentShortCode() { $template = new EmailTemplate; $template->id = 1; $template->content = "Hi {{nonExistentVariable}}"; $data = []; $this->setExpectedException(Exception::class, 'Shortcode {{nonExistentVariable}} not found in template id 1'); $parsed = $template->parse($data); }

The one thing left here that we won't cover is creating a CRUD for the email_templates table with a WYSIWYG editor to edit those templates. We are guessing the readers of this tutorial know how to do that themselves.

Here's how we use it. Let's say we want to send a welcome email to the user with the following content:

Hi {firstname}, thanks for joining.

$template = EmailTemplate::where('name', 'welcome-email')->first(); Mail::send([], [], function($message) use ($template, $user) { $data = [ 'firstname' => $user->firstname ]; $message->to($user->email, $user->fullname) ->subject($template->subject) ->setBody($template->parse($data)); });

Granted this is a very simple way of doing email content parsing and in the upcoming tutorials we'll cover more advanced approach, but for 90% of cases, this simple parser will do its job.

Let us know what you think.

Please enable JavaScript to view the comments powered by Disqus.

Disqus