PHPnews.io

★ Selling digital products using Laravel part 8: Mailing updates and news using Mailcoach

Written by murze.be / Original link on Oct. 13, 2020

We'd like to stay in touch with the people interested in our products by sending them emails when we got some news on an upcoming product, or when we are running a promo for existing products. To handle subscriptions and send out emails, we use our home-grown Laravel package Mailcoach. Let's take a look at how we use Mailcoach ourselves.

mailcoach-index.jpg

This post is a part of a series where we explore the source code of spatie.be which you'll find in this repo on GitHub
  • Part 8: Mailing updates and news using Mailcoach (you are here)

Our digital products can be bought on spatie.be. We want each product to have it's own "face" as well, so each product has its own marketing site. Here are the ones for Front Line PHP, Media Library Pro, Mailcoach, Laravel Package Training and Laravel Beyond CRUD.

Subscribing to a list #

Each of those marketing sites displays a subscription form where people can subscribe to the spatie.be email list. Here's how that looks like on the Front Line PHP site.

front-line-subscription.jpg

When creating a new account on spatie.be there's an option to subscribe to the mailing list as well.

spatie-subscription.jpg

The forms on the external marketing sites send a POST request to https://spatie.be/mailcoach/subscribe/. They also send a tag that will be added to the subscriber. This way, we know what content this subscriber is interested in. Here's the relevant controller in the spatie/laravel-beyond-crud.com repo.

class SubscribeToEmailListController
{
    public function __invoke(SubscribeToEmailListRequest $request)
    {
        if (! app()->environment('production')) {
            flash()->error('Subscribing is only possible in production');
        }
    $response = Http::post(<span class="hljs-string">"https://spatie.be/mailcoach/subscribe/"</span> .config(<span class="hljs-string">'services.mailcoach.subscription_uuid'</span>), [
        <span class="hljs-string">'email'</span> =&gt; $request-&gt;email,
        <span class="hljs-string">'tags'</span> =&gt; <span class="hljs-string">'laravel-beyond-crud-waiting-list'</span>,
    ]);

    <span class="hljs-keyword">if</span> (! $response-&gt;successful()) {
        ld()-&gt;error(<span class="hljs-string">'Something went wrong'</span>, $response, $response);

        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-keyword">Exception</span>(<span class="hljs-string">'Could not subscribe'</span>);
    }

    flash()-&gt;success(<span class="hljs-string">'Thanks for your interest! We will keep you posted with updates on the course.'</span>);

    <span class="hljs-keyword">return</span> back();
}

}

Using segments #

Using these tags, we can define Mailcoach segments to send emails to a specific part of our audience. Here's the segment that has all people that subscribed to the Mailcoach mailing list but has not bought Mailcoach yet.

mailcoach-segment.jpg

When v3 of Mailcoach was released, we used that segment to send a promo code to all people that were interested in Mailcoach but didn't buy it just yet.

mailcoach-send-campaign.jpg

Send coupons automatically #

We also use Mailcoach events to perform specific actions. At the moment of writing, we haven't launched our Front Line PHP course yet. To thank people for subscribing to that particular list, we send a coupon code that grants a little discount when buying one of our released products.

Here's how that's done. In EventSubscriber we've we've added a SendCoupon listener that will be executed whenever Mailcoach' SubscribedEvent is fired.

class EventServiceProvider extends ServiceProvider
{
protected $listen = [
// ...
SubscribedEvent::class => [
SendCoupon::class,
],
];
}

In SendCoupon, we check if the new subscriber does have the tag front-line-php-waiting-list. If so, we are going to send that subscriber the coupon.

Here's the code of SendCoupon:

namespace App<span class="hljs-title">Listeners;

use App<span class="hljs-title">Models<span class="hljs-title">Subscriber; use App<span class="hljs-title">Notifications<span class="hljs-title">WelcomeFrontLinePhpWaitingListNotification; use Illuminate<span class="hljs-title">Database<span class="hljs-title">Eloquent<span class="hljs-title">ModelNotFoundException; use Spatie<span class="hljs-title">Mailcoach<span class="hljs-title">Events<span class="hljs-title">SubscribedEvent; use Spatie<span class="hljs-title">Mailcoach<span class="hljs-title">Models<span class="hljs-title">Subscriber as MailcoachSubscriber;

class SendCoupon { public function handle(SubscribedEvent $event): void { if ($event->subscriber->hasTag('front-line-php-waiting-list')) { $this->upcastSubscriber($event->subscriber)->notify(new WelcomeFrontLinePhpWaitingListNotification()); } }

<span class="hljs-keyword">protected</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">upcastSubscriber</span><span class="hljs-params">(MailcoachSubscriber $subscriber)</span>: <span class="hljs-title">Subscriber</span>
</span>{
    $subscriber = Subscriber::findByUuid($subscriber-&gt;uuid);

    <span class="hljs-keyword">if</span> (! $subscriber) {
        <span class="hljs-keyword">throw</span> (<span class="hljs-keyword">new</span> ModelNotFoundException())-&gt;setModel(Subscriber::class);
    }

    <span class="hljs-keyword">return</span> $subscriber;
}

}

That upcasting bit in the code above is needed because, by default, a Subscriber model of Mailcoach is not Notifiable. Our local Subscriber model extends from Mailcoach' one and is Notifiable. In Mailcoach v4, which we'll release early next year, the Subscriber will be Notifiable, so this little upcasting dance can be removed.

The WelcomeFrontLinePhpWaitingListNotification is a standard notification that sends a MailMessage.

namespace App<span class="hljs-title">Notifications;

use App<span class="hljs-title">Models<span class="hljs-title">Subscriber; use Illuminate<span class="hljs-title">Bus<span class="hljs-title">Queueable; use Illuminate<span class="hljs-title">Notifications<span class="hljs-title">Messages<span class="hljs-title">MailMessage; use Illuminate<span class="hljs-title">Notifications<span class="hljs-title">Notification;

class WelcomeFrontLinePhpWaitingListNotification extends Notification { use Queueable;

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">via</span><span class="hljs-params">($notifiable)</span>: <span class="hljs-title">array</span>
</span>{
    <span class="hljs-keyword">return</span> [<span class="hljs-string">'mail'</span>];
}

<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">toMail</span><span class="hljs-params">(Subscriber $notifiable)</span>: <span class="hljs-title">MailMessage</span>
</span>{
    <span class="hljs-keyword">return</span> (<span class="hljs-keyword">new</span> MailMessage)
        -&gt;subject(<span class="hljs-string">"You are now subscribed to the Front Line PHP waiting list"</span>)
        -&gt;greeting(<span class="hljs-string">'Hi!'</span>)
        -&gt;line(<span class="hljs-string">"Thank you for subscribing to the Front line PHP waiting list. In the coming weeks we'll send you a couple of previews, and of course we'll notify you as soon as Front Line PHP is available."</span>)
        -&gt;line(<span class="hljs-string">'We would like to offer you this coupon code, which is valid for two weeks only, that grants you a 25% discount on all products in our store:'</span>)
        -&gt;line(<span class="hljs-string">'WAITING-FOR-FRONT-LINE-PHP'</span>)
        -&gt;action(<span class="hljs-string">'Redeem the coupon on our store'</span>, route(<span class="hljs-string">'products.index'</span>))
        -&gt;line(<span class="hljs-string">"We are very excited about PHP 8 and can't wait to share our insights and knowledge with you."</span>);
}

}

It's pretty nice that we can use Mailcoach on our own website. I think Mailcoach is one of the better products in this category because we continuously improve it for our use case.

This series is continued in part 9: Serving ads on GitHub.

murze

« ★ Selling digital products using Laravel part 9: Serving ads on GitHub - ★ Selling digital products using Laravel part 7: Importing package documentation from GitHub »