Oh Dear! monitors your entire website, not just the homepage. You'll get a notification as soon as your website is down, a monthly uptime report, a warning a few days before your SSL certificate expires and much more! Start your free 10 day trial now!

Mailcoach v2 has been released with support for custom HTML editors and multiple mailers

Original – by Freek Van der Herten – 9 minute read

A couple of months ago, my team released Mailcoach, a self-hosted solution to send out newsletters. It sends out mail via services like Amazon SES, Mailgun, Sendgrid, and Postmark. It can optionally track opens and clicks. When your email list grows, this is a much more cost-effective solution when compared to a service like Mailchimp.

Mailcoach can be used as a premium Laravel package or as a stand-alone app. When installed into a Laravel app, it can be greatly customized. The Mailcoach stand alone app can be used without knowing how to program.

Today we're releasing v2 of Mailcoach. It offers support for Laravel 7, html editors, and multiple mailers, together with a bunch of quality of life improvements. In this blog post, I'd like to walk you through these features and show some technical details.

If you already bought Mailcoach, you'll be happy to know that this is a free upgrade.

Introducing Mailcoach

If you're not familiar with Mailcoach yet, you can watch this video to see the core features in action.

Mailcoach intro from Spatie on Vimeo.

Support for custom HTML editors

By default, Mailcoach uses a plain text area field to edit campaigns and tables. One of the most requested features after launching Mailcoach v1, was support for an HTML editor.

Unlayer, an easy to use HTML editor

In Mailcoach v2 we've added the ability to install editors via add on packages, so you can bring any editor you like. We've created two add-on packages ourselves. The first one brings the free version of Unlayer, a powerful WYSIWYG HTML editor to Mailcoach.

image of unlayer

When using this editor, you don't need any knowledge of HTML to compose your campaign. It also can handle image uploads. Any uploads will be handled by our laravel-medialibrary package, but users don't need to care about that, it all happens behind the scenes.

Installing the Unlayer editor in Mailcoach is easy. You just have to require the add on package.

composer require spatie/laravel-mailcoach-unlayer

Next, you'll have to run the migration provided by the package, add the route macro, and set the editor key in the mailcoach.app config file to \Spatie\MailcoachUnlayer\UnlayerEditor::class. If you're familiar with adding packages to a Laravel app, this shouldn't take you long.

Monaco, a powerful code editor

Monaco is a powerful code editor created by Microsoft. It provides code highlighting, auto-completion, and much more.

image of monaco editor

Installing this package is dead simple too. Just require the package...

composer require spatie/laravel-mailcoach-monaco

Install the assets...

php artisan vendor:publish --tag mailcoach-monaco-assets --force

Set the editor in the mailcoach.app config file...

'editor' => \Spatie\MailcoachMonaco\MonacoEditor::class,

And you're good to go!

Optionally, you can customize the looks of Monaco by specifying some options in the monaco key of the mailcoach.php config file.

'monaco' => [
    'theme' => 'vs-light', // vs-light or vs-dark
    'fontFamily' => 'Jetbrains Mono',
    'fontLigatures' => true,
    'fontWeight' => 400,
    'fontSize' => '16', // No units
    'lineHeight' => '24' // No units
],

When using Mailcoach as a stand-alone app, you can configure these options on the editor screen.

image of monaco editor settings

How editors are implemented in Mailcoach

By default, Mailcoach displays a plain text area field to edit campaigns and templates. Let's take a look at how that is implemented.

image of textarea

In Mailcoach, you must specify the class name of an editor in the editor config key of the mailcoach.app config file. By default, Spatie\Mailcoach\Support\Editor\TextEditor::class is used. Let's take a look at that class.

namespace Spatie\Mailcoach\Support\Editor;

use Spatie\Mailcoach\Models\Concerns\HasHtmlContent;

class TextEditor implements Editor
{
    public function render(HasHtmlContent $model): string
    {
        return view('mailcoach::app.campaigns.draft.textEditor', [
            'html' => $model->getHtml(),
        ])->render();
    }
}

As you can see, it's quite simple. We just call getHtml on the $model (which can be a campaign or a template)`, give that to a view and return the rendered view.

Here's the context of that textEditor view.

<div>
    <x-html-field label="Body (HTML)" name="html" :value="old('html', $html)"></x-html-field>
</div>

<div class="form-buttons">
    <button id="save" type="submit" class="button">
        <x-icon-label icon="fa-code" text="Save content"/>
    </button>

    <button type="button" class="link-icon" data-modal-trigger="preview">
        <x-icon-label icon="fa-eye" text="Preview"/>
    </button>
    <x-modal title="Preview" name="preview" large>
        <iframe class="absolute" width="100%" height="100%" data-html-preview-target></iframe>
    </x-modal>
</div>

<x-replacer-help-texts />

Using the new Blade components introduced in Laravel 7, we render the x-HTML-field (which is a textarea), and some buttons to submit and preview the content.

Now we only have the rendered editor, let's see how it is rendered in a form. This is the content of the content.blade.view, which renders the entire view you see in the screenshot above.

@extends('mailcoach::app.campaigns.draft.layouts.edit', [
    'campaign' => $campaign,
    'titlePrefix' => 'HTML',
])

@section('breadcrumbs')
    <li>
        <a href="{{ route('mailcoach.campaigns.settings', $campaign) }}">
            <span class="breadcrumb">{{ $campaign->name }}</span>
        </a>
    </li>
    <li><span class="breadcrumb">Content</span></li>
@endsection

@section('campaign')
    <form
        class="form-grid"
        action="{{ route('mailcoach.campaigns.updateContent', $campaign) }}"
        method="POST"
        data-dirty-check
    >
        @csrf
        @method('PUT')
        {!! app(config('mailcoach.editor'))->render($campaign) !!}
    </form>
@endsection

The important line is the one with app(config('mailcoach.editor'))->render($campaign). As you can see, we simply get the class name of the configured editor, instantiate it, and render it.

This all makes creating support for another editor is rather straightforward. You should have to create a class, let it implement Spatie\Mailcoach\Models\Concerns\HasHtmlContent interface, and specify its fully qualified class name to the config. And that is exactly what our add on packages are doing. Here's the code of the MonacoEditor class in the laravel-mailcoach-monaco package.

namespace Spatie\MailcoachMonaco;

use Spatie\Mailcoach\Models\Concerns\HasHtmlContent;
use Spatie\Mailcoach\Support\Editor\Editor;

class MonacoEditor implements Editor
{
    public function render(HasHtmlContent $model): string
    {
        return view('mailcoach-monaco::editor', [
            'html' => $model->getHtml(),
        ])->render();
    }
}

The JavaScript side of the setup happens in the mailcoach-monaco::editor view. An interesting note is that we use the @push blade directive to add a script to the endHead stack of the Mailcoach layout view.

Using multiple mailers

Some email providers have very strict rules on sending mails. They require to keep a low bounce rate at all times. Confirmation emails have a higher chance of bouncing because they are sent to unverified email addresses.

Mailcoach v1 was built on Laravel v6. In that version of the framework, you could only define one mailer. In Laravel v7 support for multiple mailers was introduced. Mailcoach makes use of that feature to allow confirmation mails to be sent using a different mailer.

In the mailcoach.php config file, there are a couple of values that govern which mailer should be used.

return [

    /*
     * The mailer used by Mailcoach for password resets and summary emails.
     * Mailcoach will use the default Laravel mailer if this is not set.
     */
    'mailer' => null,

    /*
     * The default mailer used by Mailcoach for sending campaigns.
     */
    'campaign_mailer' => null,

    /*
     * The default mailer used by Mailcoach for confirmation and welcome mails.
     */
    'transactional_mailer' => null,
    
    // ...
];

On the settings screen of an email list, you can also specify which mailer should be used for that particular list.

Building Mailcoach

While building Mailcoach, I've recorded a video course on how to use Mailcoach, and how it was built. You can use these techniques to improve your projects as well. I've made a couple of videos available for free.

When creating a function, it's a good idea to keep the number of arguments as low as possible. In this video, you'll learn a technique on how to do this.

Most developers have probably have seen complex conditionals in legacy code. In this video, I show you how to refactor those.

Want to see more videos like this? Then head over to the video course page on Mailcoach.

Even if you don't require a self-hosted email solution, you could still benefit from purchasing it. Mailcoach is a labor of love, and our team polished it a lot. You can probably learn some nice things and pick up some tricks by reading the source code. The test suite of Mailcoach has been opensource, you can view it in this repo on GitHub.

In closing

Mailcoach v2 has a few smaller new features too, such as adding a delay for a welcome mail, some new customization options when sending mail, support for the latest version of laravel-medialibrary, support for Postmark ...

I'd like to thank my colleague Rias, who has done a lot getting v2 ready.

If you want to know more about Mailcoach, check out the extensive documentation. If you like what you see, consider getting your license.

Here are some more blog posts on Mailcoach

Mailcoach is probably the best package I've worked on. I use it myself to send my own newsletter. I hope you'll like it too!

Stay up to date with all things Laravel, PHP, and JavaScript.

Follow me on Twitter. I regularly tweet out programming tips, and what I myself have learned in ongoing projects.

Every two weeks I send out a newsletter containing lots of interesting stuff for the modern PHP developer.

Expect quick tips & tricks, interesting tutorials, opinions and packages. Because I work with Laravel every day there is an emphasis on that framework.

Rest assured that I will only use your email address to send you the newsletter and will not use it for any other purposes.

Simon liked on 14th March 2020
Hacktoshi liked on 14th March 2020
Daniele Esposito liked on 14th March 2020
Emmanuel Oduro liked on 13th March 2020
José Cage liked on 13th March 2020
Morgan Breden retweeted on 13th March 2020
Morgan Breden liked on 13th March 2020
Spatie retweeted on 13th March 2020
Joey Kudish liked on 13th March 2020
Gabriel Chuan 🐯 liked on 13th March 2020
Dyos Deangey 📶 liked on 13th March 2020
Runar Jørgensen liked on 13th March 2020
William Mandai liked on 13th March 2020
Peter Fox liked on 13th March 2020
Raphael Cunha liked on 13th March 2020
Tom Philpotts liked on 13th March 2020
Miguel Piedrafita 🚀 liked on 13th March 2020
Leonardo Prabangkoro liked on 13th March 2020
Meabed liked on 13th March 2020
bambamboole liked on 13th March 2020
Michael Leigeber liked on 13th March 2020
Abel Ponce retweeted on 12th March 2020
Wyatt liked on 12th March 2020
Jan-Joost Heurter liked on 12th March 2020
Freek Van der Herten replied on 12th March 2020
Yeah, sorry for that! Next time, we’ll get it right
Amir Ahmic replied on 12th March 2020
This is how email looks for me on the iphone and web version of gmail also 🤔
Patrick Brouwers replied on 12th March 2020
Great job 👌
Patrick Brouwers retweeted on 12th March 2020
Patrick Brouwers liked on 12th March 2020
Robin Dirksen liked on 12th March 2020
Freek Van der Herten replied on 12th March 2020
Thanks!
Roberto B 🚀 liked on 12th March 2020
Stuart Elliott liked on 12th March 2020
Bert Devriese replied on 12th March 2020
Any tips for cheap SMTP servers?
Julio Serpone liked on 12th March 2020
Ahmed Abd El Ftah retweeted on 12th March 2020
Jacob liked on 12th March 2020
Ahmed Abd El Ftah liked on 12th March 2020
Ahmed Abd El Ftah replied on 12th March 2020
congrats 👏👏
Sam Serrien replied on 12th March 2020
Jullie mail hierover kwam in de spam, server geblacklist precies 🤔🤷‍♂️
Mᴀʀᴋ »Q« Kubacki retweeted on 12th March 2020
Braunson Yager liked on 12th March 2020
José Cage liked on 12th March 2020
David Carr liked on 12th March 2020
Peter Brinck 🤘 liked on 12th March 2020
Alex liked on 12th March 2020
Freek Van der Herten replied on 12th March 2020
There are screens to import subscribers using a csv
Marcel Pociot retweeted on 12th March 2020
Arnold Wolfe retweeted on 12th March 2020
Pierre Lavaux liked on 12th March 2020
Robin Malfait liked on 12th March 2020
Tonistack replied on 12th March 2020
Ok
warren liked on 12th March 2020
Sebastian Rasch replied on 12th March 2020
I didn‘t find it in the docs, so just asking here very quick: Is there an API or something to import subscribers/lists from other providers or system to mailcoach?
Brandon Surowiec retweeted on 12th March 2020
NUNO MADURO retweeted on 12th March 2020
Jef retweeted on 12th March 2020
Rias Van der Veken retweeted on 12th March 2020
Eric L. Barnes retweeted on 12th March 2020
Dries Vints retweeted on 12th March 2020
getmailcoach retweeted on 12th March 2020
Mattias Geniar retweeted on 12th March 2020
NUNO MADURO liked on 12th March 2020
salierdev liked on 12th March 2020
Rias Van der Veken liked on 12th March 2020
Ghasem Fattahpour liked on 12th March 2020
G.Mastrodonato liked on 12th March 2020
Juvenal Pengele liked on 12th March 2020
Tom Coonen liked on 12th March 2020
Rabi liked on 12th March 2020
Dries Vints liked on 12th March 2020
Paul P liked on 12th March 2020
Mattias Geniar liked on 12th March 2020
getmailcoach replied on 12th March 2020
Fixed!
Freek Van der Herten replied on 12th March 2020
Thanks!
getmailcoach replied on 12th March 2020
No, we use some Laravel 7 specific features. For Laravel 6, use v1 of Mailcoach
NUNO MADURO replied on 12th March 2020
Congrats!
Flavius Constantin replied on 12th March 2020
Very nice, congrats! 😍 ps. The link to the customization Page of Unlayer in your Docs is broken. You wrote packages/v2 instead of v2/packages.😋
G.Mastrodonato replied on 12th March 2020
The WYSIWYG editor <3 Super! Good job guys, with the multi-tenant it will be completed!
Tonistack replied on 12th March 2020
Does this support laravel 6.x ?