Advanced usage for PHP devs

On this page:

Throttling sends

Most email providers have a limit on how many emails you can send within a given amount of time. By default, five mails per second will be sent. You can add throttling config to your mailer:

// config/mail.php
'mailers' => [
    'ses' => [
        'transport' => 'ses',
        
        // Throttling for 10 mails / second
        'timespan_in_seconds' => 1,
        'mails_per_timespan' => 10,
    ],
]

Handling events

This package fires several events. You can listen for them firing to perform extra logic.

Spatie\Mailcoach\Domain\Audience\Events\SubscribedEvent

This event will be fired as soon as someone subscribes. If double opt-in is enabled on the email list someone is in the process of subscribing to, this event will be fired when the subscription is confirmed.

The event has one public property: $subscriber which is an instance of Spatie\Mailcoach\Models\Subscriber.

You can get the email address of the subscriber like this:

$email = $event->subscriber->email;

This is how to get the name of the email list:

$nameOfEmailList = $event->subscriber->emailList->name;

Spatie\Mailcoach\Domain\Audience\Events\UnconfirmedSubscriberCreatedEvent

This event will fire after a new email address is added to an email list that requires confirmation.

It has one public property: subscriber.

Spatie\Mailcoach\Domain\Audience\Events\UnsubscribedEvent

This event will be fired as soon as someone unsubscribes. When somebody unsubscribes, the subscription won’t be deleted, but its status attribute will be set to unsubscribed.

The event has two public properties:

  • $subscriber which is an instance of Spatie\Mailcoach\Models\Subscriber.
  • $send an instance of Spatie\Mailcoach\Models\Send. You can use this to determine in which campaign the unsubscribe occurred: $send->campaign.

You can get the email address of the subscriber like this:

$email = $event->subscriber->email;

This is how to get the name of the email list:

$nameOfEmailList = $event->send->campaign->emailList->name;

Spatie\Mailcoach\Domain\Campaign\Events\CampaignSentEvent

This event will be fired after you’ve sent a campaign, and all mails are queued to be sent. Keep in mind that not all your subscribers will have gotten your mail when this event is fired.

The event has one public property: $campaign, which is an instance of the \Spatie\Mailcoach\Models\Campaign model.

Spatie\Mailcoach\Domain\Campaign\Events\CampaignMailSentEvent

This event will be fired when a mail has actually been sent to a single subscriber.

The event has one public property: $send which is an instance of the Spatie\Mailcoach\Models\Send model.

You can get the email the mail was sent to like this:

$email = $event->send->subscription->subscriber->email;

You can also retrieve the name of the campaign that this mail was part of:

$campaignName = $event->send->campaign->name;

Spatie\Mailcoach\Domain\Campaign\Events\CampaignOpenedEvent

This event will be fired when somebody opens an email. Be aware that this event could be fired many times after sending a campaign to a email list with a large number of subscribers. This event will only be fired for campaigns that have open tracking enabled.

It has one public property: $campaignOpen, which is an instance of the Spatie\Mailcoach\Models\CampaignOpen model.

You can get the email address that opened your email like this:

$email = $event->campaignOpen->subscriber->email;

This is how you can get to the name of the campaign:

$name = $event->campaignOpen->campaign->name;

Spatie\Mailcoach\Domain\Campaign\Events\CampaignLinkClickedEvent

This event will be fired when somebody clicks a link in a mail. This event will only be fired for campaigns that have click tracking enabled.

It has one public property $campaignClick, which is an instance of the Spatie\Mailcoach\Models\CampaignClick model.

You can get to the url of the link clicked like this:

$url = $event->campaignClick->link->original_link;

The email address of the subscriber who clicked the link can be retrieved like this:

$email = $event->campaignClick->subscriber->email;

Spatie\Mailcoach\Domain\Campaign\Events\CampaignStatisticsCalculatedEvent

After a campaign has been sent, statistics will be calculated according to a schedule.

Each time the statistics are calculated this event will be fired. It has one public property $campaign, which is an instance of the Spatie\Mailcoach\Models\Campaign model.

Spatie\Mailcoach\Domain\Campaign\Events\BounceRegisteredEvent

This event will fire when a complaint has bounced hard.

The event has one public property: $send which is an instance of the Spatie\Mailcoach\Models\Send model.

You can get the email the mail was sent to like this:

$email = $event->send->subscriber->email;

You can also retrieve the name of the campaign that this mail was part of:

$campaignName = $event->send->campaign->name;

Spatie\Mailcoach\Domain\Audience\Events\ComplaintRegisteredEvent

This event will fire when a complaint has been received about a sent mail. In many cases this means that the receiver marked it as spam.

The event has one public property: $send which is an instance of the Spatie\Mailcoach\Models\Send model.

You can get the email the mail was sent to like this:

$email = $event->send->subscription->subscriber->email;

You can also retrieve the name of the campaign that this mail was part of:

$campaignName = $event->send->campaign->name;

Spatie\Mailcoach\Domain\Audience\Events\TagAddedEvent

Spatie\Mailcoach\Domain\Audience\Events\TagRemovedEvent

Spatie\Mailcoach\Domain\Automation\Events\AutomationMailLinkClickedEvent

Spatie\Mailcoach\Domain\Automation\Events\AutomationMailOpenedEvent

Spatie\Mailcoach\Domain\Automation\Events\AutomationMailSentEvent

Spatie\Mailcoach\Domain\Automation\Events\AutomationMailStatisticsCalculatedEvent

Spatie\Mailcoach\Domain\Campaign\Events\WebhookCallProcessedEvent

Spatie\Mailcoach\Domain\TransactionalMail\Events\TransactionalMailLinkClickedEvent

Spatie\Mailcoach\Domain\TransactionalMail\Events\TransactionalMailOpenedEvent

Spatie\Mailcoach\Domain\TransactionalMail\Events\TransactionalMailStored

Custom authorization policies

By default, all Mailcoach backend and API actions are protected by the “viewMailcoach” gate. If this is
sufficient for your use case, then no further action is required. However, you may need more fine-grained
authorization logic to govern access to different Mailcoach features. In that case, you can create custom
authorization policies.

Policies are resolved via the Laravel service container, so swapping out a default policy for
one of your own is as simple as binding it in a service provider:

use Spatie\Mailcoach\Domain\Audience\Policies\EmailListPolicy;
...
    public function register()
    {
        app()->bind(EmailListPolicy::class, MyFancyCustomListPolicy::class);
    }
...

Policies are currently supported for the following model/action combinations:

  • EmailList
    • “viewAny”
    • “create”
    • “view”
    • “update”
    • “delete”
  • Campaign
    • “viewAny”
    • “create”
    • “view”
    • “update”
    • “delete”
  • Template
    • “viewAny”
    • “create”
    • “view”
    • “update”
    • “delete”

Manually handling feedback

After a mail is sent, most email providers send feedback on the result.

You can also manually handle feedback.

First you must add a transport id to a Send model. Here’s an example listener:

class StoreTransportMessageId
{
    public function handle(MessageSent $event)
    {
        if (! isset($event->data['send'])) {
            return;
        }

        /** @var \Spatie\Mailcoach\Models\Send $send */
        $send = $event->data['send'];

        $transportMessageId = $event->message->getHeaders()->get('header-name-used-by-your-email-provider')->bodyAsString();

        $send->storeTransportMessageId($transportMessageId);
    }
}

You must register that listener. A typical place would be in a service provider.

Event::listen(Illuminate\Mail\Events\MessageSent::class, StoreTransportMessageId::class);

Next, in the code that handles feedback you can get to the MailSend like this:

$send = Send::findByTransportMessageId($messageId);

You can mark a send as bounced.

$send->markAsBounced(SendBounceSeverity::PERMANENT);

When a Send is marked as permanently bounced, the subscriber will get unsubscribed from the email list.

Bring your own editor

If you’ve installed Mailcoach in an existing Laravel app, you can add support for any editor you’d like.

An Editor is a simple PHP class that implements the \Spatie\Mailcoach\Support\Editor\Editor interface.

It requires that you implement a render() function that accepts a \Spatie\Mailcoach\Models\Concerns\HasHtmlContent object, this is a model that has 2 methods on it.

You can use the default \Spatie\Mailcoach\Support\Editor\TextEditor implementation as a reference

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

This renders the textEditor.blade.php view of Mailcoach that makes heavy use of Blade Components.

This view will be rendered inside the forms of Campaigns and Templates, the only thing we require is that the Editor passes valid html through an input with the name html.

Mailcoach provides a secondary structured_html field in which you can pass anything your Editor needs to rebuild its view (our Unlayer editor uses this field to store the Unlayer specific JSON format).

Transactional mails