jeffersongoncalves/laravel-newsletter

Compose, schedule and send double opt-in email newsletters with open/click tracking for Laravel applications.

Maintainers

Package info

github.com/jeffersongoncalves/laravel-newsletter

pkg:composer/jeffersongoncalves/laravel-newsletter

Transparency log

Fund package maintenance!

jeffersongoncalves

Statistics

Installs: 0

Dependents: 1

Suggesters: 0

Stars: 1

Open Issues: 0

dev-master 2026-07-10 03:38 UTC

This package is auto-updated.

Last update: 2026-07-10 03:44:38 UTC


README

Laravel Newsletter

Laravel Newsletter

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads License

Compose, schedule and send double opt-in email newsletters with open/click tracking for Laravel applications.

Requirements

  • PHP 8.2+
  • Laravel 11, 12, or 13

Installation

You can install the package via composer:

composer require jeffersongoncalves/laravel-newsletter

The package uses Laravel's auto-discovery, so the service provider and facade are registered automatically.

Publish Configuration

php artisan vendor:publish --tag=newsletter-config

Publish Migrations

php artisan vendor:publish --tag=newsletter-migrations

Run Migrations

php artisan migrate

Publish Translations (optional)

php artisan vendor:publish --tag=newsletter-translations

Configuration

The configuration file is located at config/newsletter.php:

return [
    // URI prefix for subscribe/confirm/unsubscribe/tracking/webview routes
    'route_prefix' => 'newsletter',

    // Email group used when a subscription request does not specify one
    'default_email_group' => 'Website',

    // Registers `newsletter:send-scheduled` on the schedule (hourly)
    'schedule_enabled' => true,

    // Tag outgoing links with UTM parameters and inject the open-tracking pixel
    'tracking_enabled' => true,
];

Usage

Composing a Newsletter

use JeffersonGoncalves\Newsletter\Models\Newsletter;
use JeffersonGoncalves\Newsletter\Enums\NewsletterContentType;

$newsletter = Newsletter::create([
    'subject' => 'Monthly Update',
    'sender_name' => 'Example Co',
    'sender_email' => 'news@example.com',
    'content' => '<p>Hello there!</p>',
    'content_type' => NewsletterContentType::Html,
    'send_unsubscribe_link' => true,
    'send_webview_link' => true,
    'route' => 'monthly-update',
    'published' => true,
    'utm_campaign' => 'monthly-update',
]);

$emailGroup = \JeffersonGoncalves\Newsletter\Models\EmailGroup::firstOrCreate(['title' => 'Website']);

$newsletter->emailGroups()->attach($emailGroup);

Attachments can be added through the attachments media collection (powered by spatie/laravel-medialibrary):

$newsletter->addMedia($uploadedFile)->toMediaCollection('attachments');

Subscribing (Double Opt-in)

use JeffersonGoncalves\Newsletter\Facades\Newsletter;

$member = Newsletter::subscribe('subscriber@example.com', 'Website');
// $member->confirmed_at is null until the confirmation link is clicked

A confirmation email with a signed link is queued automatically. Once the subscriber clicks it (GET /newsletter/confirm/{emailGroupMember}), confirmed_at is set. Unsubscribing works the same way via GET /newsletter/unsubscribe/{emailGroupMember}.

Sending

use JeffersonGoncalves\Newsletter\Facades\Newsletter;

Newsletter::send($newsletter);

This resolves every confirmed, non-unsubscribed member of the newsletter's email groups (excluding anyone it was already sent to), dispatches one queued job per recipient inside a batch, and marks the newsletter as sent once the batch completes. Delivery outcomes are recorded per recipient in NewsletterSentRecipient.

Sending a Test Copy

Newsletter::sendTest($newsletter, 'preview@example.com');

Renders and sends synchronously to a single address without touching delivery tracking.

Scheduling

use JeffersonGoncalves\Newsletter\Enums\NewsletterStatus;

$newsletter->update([
    'status' => NewsletterStatus::Scheduled,
    'scheduled_at' => now()->addDay(),
]);

The newsletter:send-scheduled command runs hourly by default (disable via NEWSLETTER_SCHEDULE_ENABLED=false) and sends every newsletter whose scheduled_at is due.

Tracking

When tracking_enabled is on, every external link in the rendered email is tagged with utm_source, utm_medium and utm_campaign. When a newsletter has send_webview_link enabled, a 1x1 open-tracking pixel is appended, incrementing total_views on each open.

Detecting Broken Links

use JeffersonGoncalves\Newsletter\Actions\FindBrokenNewsletterLinksAction;

$broken = app(FindBrokenNewsletterLinksAction::class)->handle($newsletter);

Returns a collection of ['url' => ..., 'status' => int|null, 'error' => string|null] for every link/image that returned an error status or could not be reached.

Events

NewsletterSent, NewsletterScheduled, EmailGroupMemberSubscribed, EmailGroupMemberConfirmed, EmailGroupMemberUnsubscribed.

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.