sfcod/email-engine
Email Engine is a Laravel/PHP package for building reusable email templates and sending transactional or bulk mail through a clean, configurable API. It helps organize layouts, content, and delivery settings so email workflows stay consistent and easy to maintain.
composer require sfcod/email-engine
php artisan vendor:publish --provider="Sfcod\EmailEngine\EmailEngineServiceProvider"
config/email-engine.php first. That file is the single source of truth for driver credentials, the default driver, and any queue settings. Everything else in the package is driven from here.Mail facade. That is the whole point of adopting the package — you get one API across providers.php artisan queue:work --once or your Horizon dashboard), and confirm delivery. Slow provider calls inside the request lifecycle are exactly what this package is meant to avoid.log in local, a sandbox provider in staging, a paid provider in production — without changing a line of application code.config('email-engine.driver'). Switching from one provider to another, or splitting drivers per environment, should be a .env change only.App\Mail\Mailer (or NotificationSender) that depends on the package's contract and expose your own send(Message $message) method. Application code depends on your service, not on the vendor's message object. This contains the blast radius if the package goes unmaintained and you need to replace it.tries, backoff, and a failed() handler on the job that logs the provider, recipient, and provider error payload — not just the exception message.composer.json instead of relying on a floating range, and plan for the possibility that you own future compatibility work.1.2.0 change is a Symfony 5.4 floor, not a feature. That has two consequences: it aligns with Laravel 8.x, and it likely conflicts with Laravel 9+/Symfony 6 stacks. Run composer why-not sfcod/email-engine 1.2.0 before committing to it, and check whether the constraint is ^5.4 (floor) or ~5.4 (ceiling) — the difference changes your upgrade runway.config:cache will silently swallow env changes. Credentials must be read via env() inside the config file, and you must re-run php artisan config:cache after changing them. A stale cached config pointing at the old provider is a classic "why is it still sending from the wrong domain" bug.php artisan queue:restart. Workers hold the old config in memory; long-lived workers are the usual culprit for "it works locally but sends from the wrong provider."dispatchSync) to surface the real exception, (3) inspect the raw provider response in logs — the package's normalised exception may drop the provider's status code and error text. Log the original payload in your job's failed().php artisan package:discover
php artisan config:clear
php artisan cache:clear
Missing package:discover after install gives you a "class not found" far from the actual cause.E_DEPRECATED output before merging.How can I help you explore Laravel packages today?