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

DeanKH's avatar

Accessing the Mailable properties from the MessageSending event that is triggered when sending mail through the Mail facade

Hi all,

I've got a question about accessing the custom class properties that will be set on my Mailable classes within the MessageSending event that is fired when sending an email through Laravel's Mail facade.

Essentially, what I'm trying to achieve is the ability to store every email that is sent to my database, but also associating each mail log to the user that triggered the mail and the user that the email was sent to.

So for each email that is sent using a Mailable that has an instance of the User class set as one of its properties, that can be accessed inside the email's view, I'd want to be able to access the properties of the User object from within the MessageSending event that is triggered.

To give an example, let's say I have the following Mailable class:

class Test extends Mailable
{
    use Queueable, SerializesModels;

    public $user;
    
    public function __construct(User $user)
    {
        $this->user = $user;
    }

    public function build()
    {
        return $this->from('[email protected]')
                    ->view('email.test');
    }
}

Here's the event listener registered on the Illuminate\Mail\Events\MessageSending event:

class LogSentMail {

    public function handle(MessageSending $mailEvent)
    {
        $message = $mailEvent->message;

        (new EmailLog([
            'user_id_from' => Auth::user()->id ?? null,
            'user_id_to' => null, // Not sure how to retrieve this from $user on the Test mailable class
            'date' => date('Y-m-d H:i:s'),
            'to' => !$message->getHeaders()->get('To') ? null : $message->getHeaders()->get('To')->getFieldBody(),
            'from' => !$message->getHeaders()->get('From') ? null : $message->getHeaders()->get('From')->getFieldBody(),
            'cc' => !$message->getHeaders()->get('Cc') ? null : $message->getHeaders()->get('Cc')->getFieldBody(),
            'bcc' => !$message->getHeaders()->get('Bcc') ? null : $message->getHeaders()->get('Bcc')->getFieldBody(),
            'subject' => $message->getHeaders()->get('Subject')->getFieldBody(),
            'body' => $message->getBody(),
        ]))->save();
    }
}

What I'd like to be able to do is access the $user property, set on my Mailable class, from within my LogSentMail event listener so that I can populate the user_id_to field when saving the email data to the database.

Does anyone have any ideas about how I could go about acheiving this? Whether it's using the MessageSending event, or some other wizardry you can come up with.

Any help would be greatly appreciated. Thanks!

0 likes
8 replies
DeanKH's avatar

@Eddie212 I did, eventually.

What I did was created my own class that extends Laravel's Mailable class; let's call this MyMailable. All mail that I wish to log extends MyMailable instead of the default Mailable class.

Within the MyMailable class, I wrote a method for 'build' that gets triggered when a Mailable is sent. Within this method I call the Mailable's 'withSwiftMessage' method that accepts a callback. Within this callback you can set properties of the $message object that's injected, like so:

$user = User::first();
$this->withSwiftMessage(function ($message) use ($user) {
        $message->user = $user;
});

What this should do is now allow you to reference 'user' inside your listener for the MessageSending event. You should have something like this setup inside the 'listen' array of your EventServiceProvider:

'Illuminate\Mail\Events\MessageSending' => [
    'App\Listeners\LogSentMail',
],

Now, in my LogSentMail listener class, I can access my user from within the 'handle' method like so:

class LogSentMail {
    public function handle(MessageSending $mailEvent)
    {
        $user = $mailEvent->message->user;
    }
}

I hope that helps. If I haven't explained myself well enough, let me know and I'll try to elaborate more.

4 likes
Jam0r's avatar

Can you elaborate on your MyMailer class?

I'm trying to do something similar but i'm ending up with two build methods (in my UserWelcome mailer which extends my BaseMailer) which won't work.

1 like
madhab452's avatar

Well you dont have to create any BaseMailable class.

Just put a closure inside build method. and build your mail,

 public function build()
{
    $user = $this->user;

    $this->withSwiftMessage(function ($message) use($user){
        $message->user = $user;
    });
    
    return $this->from('[email protected]')
        ->view('email-templates.notification')
        ->with([
            'user' => $this->user,
        ]);
}

which you can access in your listener as

$event->message->user

1 like
kubaszymanowski's avatar

I was trying to do the same and your solution worked perfectly. Thanks.

integrasolid's avatar

@madhab452 when using queue then MessageSent event, the data inside the closure somehow become null. Do you know how to fix this? Works fine though without queue

miwal's avatar

The closure serializes OK for me on 5.5.

coffeegrounds's avatar

Easier, I think, to pass in the name of the default view to MailMessage with your user property.

(new MailMessage)
->subject($this->content['subject'])
->greeting($this->content['greeting'])
->markdown('vendor.notifications.email', ['user' => $this->notifiable]);

Please or to participate in this conversation.