laravel / framework

The Laravel Framework.
https://laravel.com
MIT License
32.38k stars 10.97k forks source link

[Feature Request] Get message ID when sending email via Mandrill driver #8848

Closed markovic131 closed 9 years ago

markovic131 commented 9 years ago

I allow my users to send emails to their clients via the application, but when the message is not delivered (soft or hard bounce) I want to use Mandrill webhooks to notify my user that certain message was not delivered.

I'm trying to find a way to get the message id (or the whole response object) when an email is being sent using the Mandrill driver. All my emails are sent using queues, so at that time - I want to be able to store the Mandrill response object into my database for later mapping with a webhook call.

This is an example of Mandrill response:

[
    {
        "email": "recipient.email@example.com",
        "status": "sent",
        "reject_reason": "hard-bounce",
        "_id": "abc123abc123abc123abc123abc123"
    }
]

And if certain email was not delivered, the Mandrill webhook POSTs to my application the message _id and the reason for the error. This way, I will know who the sender is (in my application) and what the problem was.

I know I can completely ditch the Laravel Mailer, and just straight use the Mandrill PHP SDK, but I'm wondering if there is another way.

All input would be much appreciated on how to go about solving this.

GrahamCampbell commented 9 years ago

Not possible in our implementation. Our mail implementation is designed not to do stuff that all drivers don't support.

markovic131 commented 9 years ago

@GrahamCampbell Thanks for the very helpful insight.

P.S Laravel supports push queues for Iron.io driver only, but that's none of my business.

PeterMartinez commented 8 years ago

$response = Mail::send('emails.welcome',[], function($message) use ($data) {
$message->from('from-email', 'no-reply'); $message->to('to-email')->subject('test'); }); dd($response->getBody()->getContents());

"[{"email":"to-email","status":"sent","_id":"7ec4ad41ccf84eef904b59b4c3955c21","reject_reason":null}]"

LuisEsteban-Wasi commented 7 years ago

Use https://github.com/weblee/Mandrill to get a response

mariusghitoiu commented 6 years ago

@psybaron, I know this is a bit too late (3+ years) and for a version of Laravel that did not exist when you posted this, but I am going to post a solution anyway.

I have the same need now, with Amazon SES and what I found and it works is this.

This works starting with Laravel 5.4.

Create an event Listener, let's call it EmailSentListener to MessageSent event that is triggered automatically when an email is sent.

In EventServiceProvider@boot I have done this

$this->listen['Illuminate\Mail\Events\MessageSent'] = [
            'App\Listeners\EmailSentListener',
        ];

Now, in EmailSentListener you will have public function handle(MessageSent $event) and you will be able to access $event->message.

Knowing you want to listen for Mandrill webhooks, to see if your email bounced, was rejected, opened etc you probably save every email in a database table.

The question is, how do you know in the listener, which email (from your database) is the Mandril or Amazon SES ID for?

The solution for this is to use Text Headers, so in your Mailable instance you can do something like this

```

public function build() { $this->view($this->emailType->template) ->subject($this->emailType->subject); $email = $this->email;

    return $this->withSwiftMessage(function ($message) use($email) {
        $message->getHeaders()
            ->addTextHeader(SendEmail::EMAIL_ID_HEADER, $email->id);
    });
}


Having this in the listener you just read the header with `$event->message->getHeaders()` and update the right entry in the database with Mandrill / SES id.

Hopes this helps, at least those who land on this topic, like me. 
rooniieesss commented 1 year ago

@psybaron, I know this is a bit too late (3+ years) and for a version of Laravel that did not exist when you posted this, but I am going to post a solution anyway.

I have the same need now, with Amazon SES and what I found and it works is this.

This works starting with Laravel 5.4.

Create an event Listener, let's call it EmailSentListener to MessageSent event that is triggered automatically when an email is sent.

In EventServiceProvider@boot I have done this

$this->listen['Illuminate\Mail\Events\MessageSent'] = [
          'App\Listeners\EmailSentListener',
      ];

Now, in EmailSentListener you will have public function handle(MessageSent $event) and you will be able to access $event->message.

Knowing you want to listen for Mandrill webhooks, to see if your email bounced, was rejected, opened etc you probably save every email in a database table.

The question is, how do you know in the listener, which email (from your database) is the Mandril or Amazon SES ID for?

The solution for this is to use Text Headers, so in your Mailable instance you can do something like this


public function build() { $this->view($this->emailType->template) ->subject($this->emailType->subject); $email = $this->email;
return $this->withSwiftMessage(function ($message) use($email) {
    $message->getHeaders()
        ->addTextHeader(SendEmail::EMAIL_ID_HEADER, $email->id);
});

}

Having this in the listener you just read the header with $event->message->getHeaders() and update the right entry in the database with Mandrill / SES id.

Hopes this helps, at least those who land on this topic, like me.

where that come from -> $email = $this->email; ???

mariusghitoiu commented 1 year ago

@rooniieesss I don't remember much about this post, but if it helps I will post here the entire clase.

<?php

namespace App\Mail;

use App\Jobs\SendEmail;
use App\Models\Email;
use App\Models\EmailType;
use App\Models\Project;
use App\User;
use Config;
use Illuminate\Mail\Mailable;

class BaseMailable extends Mailable
{

    public $user;
    public $email;
    public $project;
    public $emailType;

    /**
     * Create a new message instance.
     *
     * @param Email $email
     */
    public function __construct(Email $email)
    {
        $this->email = $email;
        $this->emailType = EmailType::find($email->email_type_id);
        $this->user = User::findOrFail($email->to_user_id);
        $this->project = Project::find($email->project_id) ?? null;
    }

    public function build()
    {
        $this->email->subject = $this->getSubject();
        $this->email->save();

        $this->view($this->emailType->template)->setFrom()
            ->subject($this->email->subject);

        $email = $this->email;
        return $this->withSwiftMessage(function ($message) use($email) {
            $message->getHeaders()
                ->addTextHeader(SendEmail::EMAIL_ID_HEADER, $email->id);
        });
    }

    public function getSubject()
    {
        return $this->emailType->subject;
    }

    public function setFrom()
    {
        $fromAddress = Config::get('mail.from.address');
        $fromName = $this->email->from_name ?? Config::get('mail.from.name');
        return parent::from($fromAddress, $fromName);
    }
}

This class is extended by all Mail classes created with Laravel, for example:

class ResetPassword extends BaseMailable
{
    use Queueable, SerializesModels;

    public $token;
....
rooniieesss commented 1 year ago

@mariusghitoiu SendEmail::EMAIL_ID_HEADER, $email->id);

the $email->id

Is that true, email->id from id generate from db?? So after save data, then the ID from db save and sended to mandrill???

Can I see the use App\Jobs\SendEmail; ??