Weave Code
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 Scoped Mail Config Laravel Package

lacodix/laravel-scoped-mail-config

Send emails with dynamic, per-scope mailer settings in Laravel. Provide SMTP/from config via any model or class implementing HasMailConfig—ideal for multi-tenancy (e.g., Spatie) or user/team-specific mail configurations.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Install the Package

    composer require lacodix/laravel-scoped-mail-config
    

    Publish the config (if needed):

    php artisan vendor:publish --provider="Lacodix\LaravelScopedMailConfig\ScopedMailConfigServiceProvider"
    
  2. Implement HasMailConfig Interface Add the interface to your model (e.g., Tenant, User, or Team):

    use Lacodix\LaravelScopedMailConfig\Concerns\HasMailConfig;
    
    class Tenant extends Model implements HasMailConfig
    {
        public function getMailConfig(string $name): array
        {
            return [
                'transport' => 'smtp',
                'host' => 'smtp.example.com',
                'port' => 587,
                'encryption' => 'tls',
                'username' => 'user@example.com',
                'password' => 'password',
                'from' => ['address' => 'no-reply@example.com', 'name' => 'App Name'],
            ];
        }
    }
    
  3. Resolve the Scope in AppServiceProvider Configure how the package resolves the scope (e.g., current tenant or user):

    use Lacodix\LaravelScopedMailConfig\Facades\ScopedMail;
    
    public function boot(): void
    {
        ScopedMail::resolveScopeUsing(fn () => Tenant::getCurrentTenant());
    }
    
  4. Send Scoped Emails Use the ScopedMail facade like Laravel’s native Mail facade:

    ScopedMail::to('user@example.com')->send(new OrderShipped($order));
    

First Use Case: Multi-Tenant SMTP Configuration

  • Scenario: A SaaS app where each tenant has their own SMTP server for sending transactional emails.
  • Implementation:
    1. Attach HasMailConfig to the Tenant model.
    2. Resolve the scope using Tenant::getCurrentTenant().
    3. Send emails via ScopedMail, and the package will dynamically apply the tenant’s SMTP settings.

Implementation Patterns

Workflows

  1. Dynamic SMTP per Tenant

    • Use HasMailConfig on the Tenant model to define SMTP settings.
    • Resolve the scope in AppServiceProvider to fetch the current tenant.
    • Send emails with ScopedMail, and the package will override Laravel’s default mail config.
  2. User-Specific Email Signatures

    • Implement HasMailConfig on the User model to define from address and name.
    • Resolve the scope using the authenticated user:
      ScopedMail::resolveScopeUsing(fn () => Auth::user());
      
    • Emails sent via ScopedMail will use the user’s signature.
  3. Team-Based Mail Configs

    • Attach HasMailConfig to a Team model to define shared SMTP settings for team members.
    • Resolve the scope based on the current team (e.g., via middleware or context).

Integration Tips

  1. Middleware for Scope Resolution Dynamically resolve the scope based on request context (e.g., subdomain or API token):

    public function handle(Request $request, Closure $next)
    {
        $tenant = Tenant::findBySubdomain($request->getHost());
        ScopedMail::resolveScopeUsing(fn () => $tenant);
        return $next($request);
    }
    
  2. Fallback to Global Config If no scope is resolved, the package falls back to Laravel’s default config/mail.php. Test this behavior:

    // Test with no scope resolved
    ScopedMail::fake();
    ScopedMail::to('user@example.com')->send(new TestMail());
    
  3. Testing Scoped Emails Use the fake() method to mock scoped emails in tests:

    public function test_scoped_email()
    {
        ScopedMail::fake();
        ScopedMail::to('user@example.com')->send(new TestMail());
    
        ScopedMail::assertSent(TestMail::class, function ($mail) {
            return $mail->hasTo('user@example.com');
        });
    }
    
  4. Queue Workers Scoped mail configs work seamlessly with Laravel queues. Ensure the scope is resolved before the job runs (e.g., in the job’s handle method or via middleware).


Gotchas and Tips

Pitfalls

  1. Scope Resolution Timing

    • Issue: If the scope is not resolved before sending an email (e.g., in a queue job), the package will fall back to the global config.
    • Fix: Resolve the scope early in the request lifecycle (e.g., middleware) or in the job’s handle method:
      public function handle()
      {
          $tenant = Tenant::find($this->tenantId);
          ScopedMail::resolveScopeUsing(fn () => $tenant);
          Mail::to($this->email)->send(new TestMail());
      }
      
  2. Config Caching

    • Issue: If you use php artisan config:cache, scoped mail configs may not update dynamically. The package reads configs at runtime, but cached configs might persist stale values.
    • Fix: Avoid caching configs in production if scopes change frequently, or implement a cache invalidation strategy.
  3. Missing getMailConfig Method

    • Issue: Forgetting to implement getMailConfig on the scoped model will cause runtime errors.
    • Fix: Ensure all models implementing HasMailConfig include the method. Use IDE hints or static analysis tools to catch missing implementations.
  4. Laravel 12+ Compatibility

    • Issue: The package supports Laravel 12, but some older Laravel versions (e.g., <8) may not work.
    • Fix: Verify compatibility with your Laravel version. If using Laravel 11 or below, check the package’s changelog for breaking changes.

Debugging

  1. Inspect Active Scope Use the facade to debug the current scope:

    $scope = ScopedMail::getScope();
    dd($scope); // Outputs the resolved model (e.g., Tenant instance)
    
  2. Check Mail Config Overrides Log the resolved mail config to verify overrides:

    $config = ScopedMail::getMailConfig();
    \Log::info('Scoped Mail Config:', $config);
    
  3. Test with fake() Use ScopedMail::fake() to isolate scoped email logic in tests and verify configs:

    ScopedMail::fake();
    ScopedMail::to('user@example.com')->send(new TestMail());
    $mail = ScopedMail::assertSent(TestMail::class);
    dd($mail->mailables[0]->config); // Inspect the applied config
    

Extension Points

  1. Custom Scope Resolvers Extend the package by creating a custom resolver. Override the resolveScopeUsing method in a service provider:

    ScopedMail::resolveScopeUsing(function () {
        return Tenant::where('id', request()->tenant_id)->first();
    });
    
  2. Dynamic Config Merging Merge scoped configs with global configs in getMailConfig:

    public function getMailConfig(string $name): array
    {
        $globalConfig = config('mail.default');
        $scopedConfig = [
            'from' => ['address' => 'tenant@example.com'],
        ];
        return array_merge($globalConfig, $scopedConfig);
    }
    
  3. Event-Based Scoping Trigger scope resolution via events (e.g., TenantSwitched):

    event(new TenantSwitched($tenant));
    // In a listener:
    ScopedMail::resolveScopeUsing(fn () => $event->tenant);
    
  4. API for External Configs Fetch scoped configs via an API endpoint for admin dashboards:

    Route::get('/mail-config', function () {
        $tenant = Tenant::getCurrentTenant();
        return response()->json($tenant->getMailConfig('default'));
    });
    

Tips

  1. Use config/mail.php as a Fallback Define default mail settings in config/mail.php and override them per scope. This ensures emails are always deliverable, even if a scope fails to resolve.

  2. Environment-Specific Scopes Combine scoped configs with environment variables for flexibility:

    public function getMailConfig(string $name): array
    {
        return [
            'host' => env('SMTP_HOST', 'default.smtp.com'),
            'port' => env('SMTP_PORT', 587),
        ];
    }
    
  3. Validate SMTP Credentials Add validation to getMailConfig to catch misconfigurations early:

    public function getMailConfig(string $name): array
    {
        $config = [
            'host' => 'smtp.example.com',
            'username' => 'user@example.com',
            'password
    
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.
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
ecotone/kafka
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata