Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

drgreen's avatar

Inject Mailable $data through buildViewDataUsing() callback

I'm implementing mailable logging in my Laravel application and need to track when emails are sending/sent. I want to add UUID and class name to every mailable data MessageSending and MessageSent events.

Currently, I'm using this approach in my AppServiceProvider:

use Illuminate\Mail\Mailable;

class AppServiceProvider extends ServiceProvider
{
	public function boot()
	{
		Mailable::buildViewDataUsing(function ($mailable) {
			return [
				'__mailable' => get_class($mailable),
				'__uuid' => (string) Str::uuid(),
			];
		});
	}
}

Then in my event listener, I access this data:

public function handleMessageSending(MessageSending $event): void
{
    $mailableClass = $event->data['__mailable'] ?? 'Unknown';
    $uniqueId = $event->data['__uuid'] ?? null;
}

public function handleMessageSent(MessageSent $event): void
{
    $uniqueId = $event->data['__uuid'] ?? null;
}

Questions:

Is buildViewDataUsing() the correct/recommended way to inject data into all mailables? Is this the way or is there better Laravel-native approach to achieve this?

Any alternative approaches would be greatly appreciated! Thanks!

0 likes
1 reply
LaryAI's avatar

You're on the right track using Mailable::buildViewDataUsing() to inject additional data into all mailables. This is a Laravel-native way to customize the data passed to mail views, and it works well for your use case.

Answers to Your Questions

1. Is buildViewDataUsing() the correct/recommended way to inject data into all mailables?

Yes, buildViewDataUsing() is designed exactly for this: to customize or add to the data that is passed to the view for every mailable. It's a global hook, so you don't have to modify each mailable individually.

2. Is this the way or is there a better Laravel-native approach to achieve this?

For your requirement (injecting extra data into every mailable for logging/tracking), this is the most straightforward and maintainable approach. Alternatives would require more boilerplate or customization per mailable.


Alternative Approaches

A. Using a Base Mailable Class

You could create a base mailable class and have all your mailables extend it, injecting the data in the build() method. However, this is more work and less DRY than your current approach.

use Illuminate\Mail\Mailable;
use Illuminate\Support\Str;

class BaseMailable extends Mailable
{
    public function build()
    {
        $this->with([
            '__mailable' => get_class($this),
            '__uuid' => (string) Str::uuid(),
        ]);
        return parent::build();
    }
}

But this requires all mailables to extend BaseMailable, which is less flexible.

B. Using Mail Events Directly

You could listen to the MessageSending and MessageSent events and mutate the data there, but the data array is already built at that point, so it's not as clean or reliable.


Your Current Approach

Your current approach is clean, maintainable, and leverages Laravel's built-in features as intended. Just make sure you're using Laravel 8.69+ (when buildViewDataUsing was introduced).

use Illuminate\Mail\Mailable;
use Illuminate\Support\Str;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Mailable::buildViewDataUsing(function ($mailable) {
            return [
                '__mailable' => get_class($mailable),
                '__uuid' => (string) Str::uuid(),
            ];
        });
    }
}

Summary

  • Your approach is correct and recommended for your use case.
  • There are alternatives, but they are less elegant or require more boilerplate.
  • You can safely continue with buildViewDataUsing() for injecting global mailable data.

If you have further requirements (like per-mailable customization), you can always combine this with per-mailable with() calls or custom logic.

1 like

Please or to participate in this conversation.