Installation:
composer require laravelir/newsletters
php artisan vendor:publish --provider="Laravelir\Newsletters\Providers\NewslettersServiceProvider"
Run Migrations:
php artisan migrate
newsletters and subscribers tables.First Use Case:
resources/views/vendor/newsletters/subscribe.blade.php) or extend it:
@include('newsletters::subscribe', ['newsletter' => $newsletter])
use Laravelir\Newsletters\Facades\Newsletter;
$newsletter = Newsletter::findOrCreate('weekly-updates');
$subscriber = $newsletter->subscribe('user@example.com');
$newsletter->send('Welcome!', 'emails.newsletter.welcome', ['data' => 'value']);
config/newsletters.php (published via php artisan laravelir:newsletters).
default_from_address, queue_driver).database/migrations/xxxx_create_newsletters_tables.php.
newsletters (e.g., name, subject, body) and subscribers (e.g., email, newsletter_id).Laravelir\Newsletters\Facades\Newsletter.
Managing Newsletters:
$newsletter = Newsletter::create([
'name' => 'monthly-digest',
'subject' => 'Monthly Update',
'body' => 'Your monthly content here.',
]);
$newsletter = Newsletter::find('weekly-updates');
$newsletters = Newsletter::where('active', true)->get();
Subscriber Management:
$subscriber = $newsletter->subscribe('user@example.com');
$subscriber->unsubscribe(); // Soft deletes the subscriber
$newsletter->subscribers()->where('confirmed', false)->confirm();
Sending Newsletters:
$newsletter->send('Subject', 'emails.newsletter.template', ['key' => 'value']);
$newsletter->queue('Subject', 'emails.newsletter.template', ['key' => 'value']);
$newsletter->send('Subject', 'template', [], ['from' => 'custom@email.com']);
Confirmation Workflow:
$token = $subscriber->createConfirmationToken();
$subscriber->confirm($token);
<a href="{{ route('newsletters.confirm', $token) }}">Confirm Subscription</a>
routes/web.php):
Route::get('/newsletters/confirm/{token}', [\Laravelir\Newsletters\Http\Controllers\NewsletterController::class, 'confirm']);
Event Listeners:
Subscribed, Unsubscribed):
use Laravelir\Newsletters\Events\Subscribed;
event(new Subscribed($subscriber));
EventServiceProvider:
protected $listen = [
Subscribed::class => [
'App\Listeners\LogSubscriberActivity',
],
];
resources/views/emails/newsletter/.newsletters.queue_driver in config/newsletters.php (e.g., database, redis).php artisan queue:work
Route::post('/api/subscribe', [\App\Http\Controllers\NewsletterController::class, 'subscribe']);
Newsletter facade or use in-memory databases for tests:
$this->partialMock(Newsletter::class, function ($mock) {
$mock->shouldReceive('send')->once();
});
Migration Conflicts:
database/migrations/ folder.php artisan migrate:fresh.php artisan vendor:publish --tag=migrations to republish if needed.Queue Stuck Jobs:
php artisan queue:failed
php artisan queue:retry <job-id>
php artisan queue:flush
Double Subscriptions:
subscribers table:
Schema::table('subscribers', function (Blueprint $table) {
$table->unique(['email', 'newsletter_id']);
});
upsert in your subscription logic:
$subscriber = Subscriber::upsert(
['email' => 'user@example.com', 'newsletter_id' => $newsletter->id],
['email']
);
Email Bounces:
bounced_at column to the subscribers table and automate cleanup:
$subscriber->update(['bounced_at' => now()]);
Performance with Large Subscriber Lists:
$subscribers = $newsletter->subscribers()->cursor();
foreach ($subscribers->chunk(100) as $chunk) {
foreach ($chunk as $subscriber) {
Mail::to($subscriber->email)->send(new NewsletterMail($newsletter));
}
}
Log Subscriber Activity:
config/newsletters.php:
'log_subscribers' => true,
storage/logs/laravel.log.Verify Tokens:
Subscribers model:
protected static function boot() {
parent::boot();
static::creating(function ($subscriber) {
$subscriber->confirmation_token = Str::random(60);
$subscriber->confirmation_expires_at = now()->addDays(7);
});
}
Test Email Delivery:
MailFake for testing:
use Illuminate\Mail\Testing\Mails;
public function test_newsletter_send() {
$this->withoutExceptionHandling();
$newsletter = Newsletter::create([...]);
$newsletter->send('Test', 'template');
$this->assertCount(1, Mail::failures());
}
Custom Subscriber Fields:
subscribers table:
Schema::table('subscribers', function (Blueprint $table) {
$table->string('first_name')->nullable();
$table->string('last_name')->nullable();
});
Subscriber model:
protected $fillable = ['email', 'newsletter_id', 'first_name', 'last_name'];
Custom Newsletter Types:
type column to the newsletters table and filter accordingly:
$newsletter = Newsletter::where('type', 'promotional')->first();
Override Views:
How can I help you explore Laravel packages today?