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

Easy Mailer Laravel Package

vanio/easy-mailer

vanio/easy-mailer is a lightweight Laravel/PHP package that streamlines sending email through a simple, easy-to-use API, helping you configure mailers and dispatch messages with minimal boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

Verify the target framework before anything else. Vanio's packages are published as Symfony bundles/DI services, and this one is no exception in spirit — it wraps a mailer abstraction with a simpler send API. Open composer.json first and look at require: if you see symfony/mailer, symfony/dependency-injection, or twig/twig, you are not installing a Laravel package, you are installing a Symfony component wrapper. That single fact drives everything below.

Minimal steps:

  1. composer require vanio/easy-mailer and immediately inspect vendor/vanio/easy-mailer/composer.json plus the src/ tree.
  2. Read the service definition (services.xml / services.yaml) — it tells you the constructor arguments you'll need to build by hand in Laravel, and it's the closest thing to documentation this package has.
  3. Read the tests. With 0 stars and 0 dependents there is no Stack Overflow corpus, no blog posts, no issue history to learn from; the test suite is the API reference.
  4. Wire a binding in AppServiceProvider and send one plain-text email through your configured transport before touching templates, attachments, or HTML.

First use case: a single transactional email — password reset or order confirmation — rendered from a template, with one attachment, sent through SMTP. Do not start with bulk sending or queueing; you need to know whether the transport layer even resolves cleanly inside Laravel's container first.

// AppServiceProvider::register() — names illustrative, confirm against src/
$this->app->singleton(EasyMailer::class, function ($app) {
    return new EasyMailer(
        Transport::fromDsn(config('mail.dsn')),  // build from your MAIL_* env
        $app->make(RendererInterface::class),
    );
});

If the package's mailer is a thin, injectable service with a transport and a renderer as constructor arguments, this works and you are done. If it reaches into a global container or expects Symfony's EventDispatcherInterface, stop and reassess — see Gotchas.

Implementation Patterns

Wrap it, don't sprinkle it. The package's API surface is small and unversioned. Put it behind your own App\Mail\Mailer interface with a method per email type (sendPasswordReset, sendOrderConfirmation). When the package goes unmaintained — and with 0 dependents, assume it might — you swap one adapter class instead of grepping 40 call sites.

Treat message construction as a builder, sending as a separate step. The typical shape is a fluent builder returning a message object that a mailer service then sends. Keep those two phases in different methods on your adapter so you can snapshot/assert the message in tests without sending:

$message = $this->mailer->createMessage()
    ->to($user->email, $user->name)
    ->subject(__('mail.reset.subject'))
    ->html($this->blade->render('mail.reset', ['user' => $user]))
    ->attach($pdfPath, 'invoice.pdf');

$this->mailer->send($message);

Render with Blade, not Twig. If the package's renderer expects Twig, don't pull twig/twig + a Laravel Twig bridge into your stack just for email templates. Render with view()->render() and hand the package finished HTML. You lose the package's template-context convenience and keep your team's Blade knowledge, your existing layouts, and your @lang directives. This is the single highest-leverage decision in the whole integration.

Map your existing env to a DSN. Laravel developers have MAIL_MAILER, MAIL_HOST, MAIL_PORT, MAIL_USERNAME, MAIL_PASSWORD. Symfony's transport layer wants a DSN string. Build it once in a config file so local/staging/production differ by env only:

// config/mail.php
'easy_mailer_dsn' => env('MAIL_DSN', sprintf(
    'smtp://%s:%s@%s:%s',
    urlencode(env('MAIL_USERNAME')), urlencode(env('MAIL_PASSWORD')),
    env('MAIL_HOST'), env('MAIL_PORT', 587),
)),

Note the urlencode — passwords with @, :, or / silently break DSN parsing otherwise.

Queue at the Laravel layer. The package will almost certainly not know about Laravel's queue. Make your adapter method synchronous, then wrap calls in a ShouldQueue job:

SendOrderConfirmation::dispatch($order)->onQueue('mail');

This gives you retries, backoff, failed_jobs, and Horizon visibility — all things you'd otherwise reimplement.

Test against a capturing transport, not the network. Point the DSN at a null/log transport in phpunit.xml and assert on the message object your adapter builds. Laravel's Mail::fake() will not see these emails — it only intercepts Illuminate\Mail\Mailable. Write a small FakeEasyMailer implementing your own adapter interface and assert against that; it's more valuable than spying on the package internals.

Multi-tenant / per-brand sending. If you send on behalf of tenants, resolve the transport per-tenant (from-address, DKIM credentials) at construction time in a scoped binding rather than mutating a singleton. Singletons that hold credentials are a classic source of "email sent as the wrong tenant" bugs.

Gotchas and Tips

  • No service provider, no facade, no auto-discovery. Do not expect php artisan vendor:publish to do anything. Everything is manual binding. Budget a half-day for this, not ten minutes.
  • Two mail stacks in one app. The moment this package is in, you have Laravel's Mail component and Symfony's Mailer transport pool. They don't share config, connection pooling, or log context. Logs from one won't show up where you expect. Consider standardizing on one; running both long-term is how teams end up with "why did the invoice email go out twice."
  • Symfony Messenger expectations. Some Symfony mailers dispatch through a message bus for async. Laravel has no such bus. If the package's constructor wants a MessageBusInterface, pass a no-op implementation that sends immediately, and do your async in Laravel jobs as above.
  • Exception types differ. Symfony Mailer throws Symfony\Component\Mailer\Exception\TransportExceptionInterface; Laravel's mail throws its own. Catch the Symfony interface explicitly in your adapter and normalize to a domain exception, or your failed_jobs handler will miss real SMTP failures.
  • Port/TLS confusion. Symfony's DSN is literal: smtp:// on 587 with STARTTLS, smtps:// on 465 with implicit TLS. Laravel's MAIL_ENCRYPTION=tls does not translate to smtps. Getting this wrong produces a 30-second hang, not an error — very easy to misdiagnose as a firewall issue.
  • Inline images and CID. If you're embedding logos via CID, remember that many HTML sanitizers and some clients strip them. Prefer absolute URLs to hosted assets for anything user-facing; reserve CID for PDFs and truly private assets.
  • Debugging. Enable the underlying transport's logger during integration — Symfony's transport accepts a PSR-3 logger, and Laravel's Log::channel('mail') works fine. Set it at debug level temporarily and you'll see the full SMTP conversation. Without it, failures look like generic "message not sent."
  • Extension points are yours, not theirs. With no community, don't wait for a feature. Decorate: your adapter wraps the package service; a custom builder class wraps the message builder; a TransportFactory-style resolver handles multi-tenant. Everything extensible lives in your App\Mail namespace.
  • Maintenance risk is the real finding. MIT license is fine, but 0 stars / 0 dependents / low activity means no security review, no CVE feed, no answers when it breaks against a future PHP or Symfony version. Pin the exact version in composer.json, and write an integration test that actually sends through a real SMTP sink (MailHog/Mailpit) in CI so a broken upgrade fails loudly rather than silently dropping mail.
  • Honest recommendation: unless the package offers a specific rendering or attachment convenience you genuinely need, Laravel's native Mail + Mailable + Mail::fake() covers 95% of this with first-party support. Adopt this package only for a concrete win, and only behind your own adapter so reversing the decision costs an afternoon.
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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
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