Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Subscribers Laravel Package

mydnic/laravel-subscribers

A Laravel package for managing newsletter subscribers and mailing lists, with database migrations, models, and a simple API for handling subscriptions, unsubscriptions, and email opt-ins in your application.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and scaffold in four commands:

composer require mydnic/laravel-subscribers
php artisan vendor:publish --provider="Mydnic\Subscribers\SubscribersServiceProvider"
php artisan migrate

If the provider name differs in your version, run php artisan vendor:publish with no arguments and pick the mydnic/laravel-subscribers entry interactively — the package publishes config, migrations, and usually views/routes under separate tags (e.g. --tag=subscribers-config, --tag=subscribers-views). Check vendor/mydnic/laravel-subscribers/src right after install; that directory is the real documentation. Read in this order:

  1. src/Models/ — the subscriber and campaign/email entities you'll be querying and extending.
  2. routes/ (or src/Http/) — the built-in subscribe/confirm/unsubscribe/tracking endpoints. These are your integration surface.
  3. config/subscribers.php — table names, mail settings, model bindings, tracking toggles.
  4. database/migrations/ — column names and nullability, which dictate what you can safely add.

First use case to validate the install end-to-end: publish the config and views, drop the package's subscribe form (or POST to its subscribe route) on a staging page, submit an email, confirm the record appears in the subscribers table, and confirm the confirmation/welcome mail lands in Mailtrap or log driver. If that loop works, you know migrations, routing, mail config, and model bindings are all wired correctly before you touch campaigns.

Implementation Patterns

Treat the package as a domain layer, not a UI kit. Its routes and Blade views are convenient for a marketing landing page, but in an existing app you'll usually keep your own controller and call the package's models/actions directly:

// app/Http/Controllers/NewsletterController.php
public function store(Request $request)
{
    $request->validate(['email' => ['required', 'email']]);

    Subscriber::firstOrCreate(
        ['email' => strtolower($request->email)],
        ['source' => 'footer-form', 'status' => 'pending']
    );

    return back()->with('status', 'Check your inbox to confirm.');
}

Using firstOrCreate with a normalized email makes re-submissions idempotent — critical because the package's unique index will otherwise throw on double clicks.

Double opt-in as the default, single opt-in as the exception. Wire confirmation through the package's signed/tokenized confirm route and only mark a subscriber active on that hit. If your product genuinely needs instant access (e.g. gated PDF download), branch explicitly rather than disabling confirmation globally:

if (config('subscribers.double_opt_in')) {
    // send confirmation, status = pending
} else {
    // status = active, send welcome
}

Campaign sending belongs on the queue, never in a request. Build campaigns as records first (subject, body, target segment), then dispatch a job that chunks subscribers and sends per-batch. This gives you pause/resume, retries, and per-recipient failure isolation:

Bus::chain(
    Subscriber::where('status', 'active')->pluck('id')
        ->chunk(500)
        ->map(fn ($ids) => new SendCampaignBatch($campaign, $ids))
        ->all()
)->dispatch();

Hook into lifecycle with model events, not by forking. Map new subscribers into your CRM, tag them by signup source, or fire an internal Slack notification from an observer:

Subscriber::created(fn ($s) => SyncToCrm::dispatch($s));

Anything the package doesn't expose an event for, prefer a booted() observer over editing vendor files.

Segment via your own columns + scopes. Small newsletter packages rarely ship a query builder. Add source, tags (json), confirmed_at, unsubscribed_at columns in your own migration and compose scopes (active(), fromSource('pricing-page')). This keeps you one composer update away from breakage instead of entangled in package internals.

Route the package's mail through your existing transport. Set the mailer in config/subscribers.php rather than in config/mail.php, so campaign traffic can use a separate provider/domain from transactional mail. This is the single highest-value config change for deliverability: newsletter blasts on a dedicated subdomain protect password resets from reputation damage.

Override views, don't rewrite them. Publish views to resources/views/vendor/... and edit there. Same for any layout the package's mail templates extend — keep your brand header/footer in one partial so campaign emails and transactional emails match.

Testing. Mail::fake() + Queue::fake(), assert Subscriber::assertDatabaseHas(...) after hitting your subscribe endpoint, and assert the confirmation mailable was queued. For unsubscribe, assert the route works for valid tokens and 404/403s for tampered ones.

Gotchas and Tips

  • Config caching bites silently. After editing config/subscribers.php run php artisan config:clear. A stale cached config is the most common "my tracking toggle did nothing" cause.
  • Publish migrations before you have data, not after. If the package ships a subscribers table and you already have a subscribers or newsletter_subscribers table, rename via config before migrating. Renaming later means hand-writing a migration and rewriting foreign keys.
  • Tracking opens is noisy by design. Apple Mail Privacy Protection pre-fetches pixels and many clients block images. Treat open rates as directional, clicks as real, and never trigger business logic (like "engaged user" flags) off opens alone.
  • Unsubscribe links must be signed and non-enumerable. If the package's route uses an ID rather than a token, override the route — a sequential ID lets anyone unsubscribe anyone. Also add a List-Unsubscribe and List-Unsubscribe-Post header; Gmail and Yahoo now expect it for bulk senders.
  • Small-package risk is real (26 stars). Pin the version in composer.json ("mydnic/laravel-subscribers": "^1.0"), don't rely on dev-main, and check the repo's issue/commit cadence before betting a launch on it. If it goes quiet, the escape hatch is the database schema: your subscribers are plain rows, so you can swap the sending layer for Laravel's native Mail + a Mailable and lose almost nothing.
  • Rate limits, not just queues. SES, Postmark, and Mailgun throttle per-second. Add ->delay() or a rate-limited job middleware when chunking; a 10k-recipient blast that ignores provider limits silently drops mail.
  • Normalize emails on write and compare case-insensitively. [email protected] and [email protected] are the same mailbox but different unique keys unless you lowercase at the boundary. Do it in a mutator so every entry path (form, API, import) is covered.
  • Soft-delete vs. hard-delete for unsubscribes. Physically deleting a subscriber means they can re-subscribe unknowingly and you lose suppression history — a compliance problem. Keep the row with a status/timestamp and filter it out at send time.
  • Debugging sends: set the package's mailer to log and inspect storage/logs/laravel.log for the rendered MIME before involving a real provider. If mail vanishes, check failed_jobs first — a broken view throws inside the queued job, not at dispatch time.
  • Extension points to prefer, in order: model events/observers → published views → published config → custom subclass bound in config → a fork. Anything at level 4 or 5, document in your README so the next developer knows why the package is partially bypassed.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin