1

In config/mail.php, we have:

'reply_to' => [
    'address' => env('MAIL_REPLY_TO_ADDRESS', 'default@company.com'),
    'name' => env('MAIL_REPLY_TO_NAME', 'Company')
],

And the mailable looks like this:

namespace App\Mail;

use App\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class SupportMessage extends Mailable implements ShouldQueue
{
    use Queueable, SerializesModels;

    public $user;
    public $senderEmail;
    public $message;

    public function __construct(User $user, $email, $message)
    {
        $this->user = $user;
        $this->senderEmail = $email;
        $this->message = $message;
    }

    public function build()
    {
        return $this->markdown('emails.support-message')
            ->subject('Support Message')
            ->replyTo(['email' => $this->senderEmail]);
    }
}

For some reason, instead of replacing the default reply-to header in the email, Laravel concatenates $this->senderEmail onto the existing default@company.com, which email clients don't seem to be responding to (blank email list when replying). The header comes through looking something like this: reply-to: Company <default@company.com>, sender_email@company.com

I have also tried ->replyTo($this->senderEmail), which results in the same concatenation.

Is there a way to replace the global reply-to rather than concatenating?

Abdulla Nilam
  • 36,589
  • 17
  • 64
  • 85
Josh Mountain
  • 1,888
  • 10
  • 34
  • 51
  • Which version of Laravel are you using? Because Laravel changed this in L9. In any version before, it's `address`, not `email`. – Yinci May 11 '23 at 06:26
  • @Yinci Laravel 10, but I tried both keys with the same result. – Josh Mountain May 11 '23 at 16:33
  • 1
    I just ran into this today with Laravel 9.x. As best I can tell, the `reply_to` config updates info on the `Mailer` itself, not the `Mailable`. So even if you log what `$this->replyTo` is on the `Mailable` (and see it's the recipient(s) you expect), the `Mailer` will add the global `reply_to` recipients. I have not yet dug further into how you can stop `Mailer` from doing this without just not using the global `reply_to`. – alexkorn May 12 '23 at 15:52
  • Thanks @alexkorn I appreciate the insight. If you find out anything more please let me know. The global is very helpful but I suppose we could manually add the reply_to to all emails if we had to. – Josh Mountain May 13 '23 at 00:00
  • Maybe just override setAddress function? – StewieSWS May 23 '23 at 14:12
  • @alexkorn close, but it's a bit deeper than that. In `Mailer::createMessage()`, a new `Message` object is created. That's where the Reply-To is stored. – miken32 May 26 '23 at 19:39
  • In addition to my answer there, have you tried just clearing the config setting in `SupportMessage::build()` before sending it? `config(["mail.reply_to" => []]);` – miken32 May 26 '23 at 19:42

5 Answers5

0

I would try to override function setAddress() in your mailable class, or if i'd have multiple emails, then create new class which extends mailable and in concerned email extend this new class.

Something like this :

protected function setAddress($address, $name = null, $property = 'to')
{
    if (empty($address)) {
        return $this;
    }

    foreach ($this->addressesToArray($address, $name) as $recipient) {
        $recipient = $this->normalizeRecipient($recipient);
        if($this->property === 'replyTo'){
           $this->{$property} = [[
              'name' => $recipient->name ?? null,
              'address' => $recipient->email,
           ]];
        }
        else{
           $this->{$property}[] = [
              'name' => $recipient->name ?? null,
              'address' => $recipient->email,
           ];
        }
    }

    $this->{$property} = collect($this->{$property})
        ->reverse()
        ->unique('address')
        ->reverse()
        ->values()
        ->all();

    return $this;
}
StewieSWS
  • 546
  • 3
  • 10
0

Try to add a new Reply-To header using the addTextHeader() method.

$message = $this->markdown('emails.support-message')->subject('Support Message');

$message->getHeaders()->addTextHeader('Reply-To', $this->senderEmail);
        
return $message;
discussion
  • 81
  • 8
0

So, in brief: Mailable::send() calls Mailer::send() and passes it a closure. A Message object is created that contains the global Reply-To address. Then the closure is run with the Message object, which is passed to Mailable::buildRecipients(), which calls Message::replyTo(), which concatenates the new address to the old.

The real solution is to override Message::replyTo() so that it doesn't concatenate the values any more. I've done this kind of thing before, and it's a pain changing the dependency chain all the way back. So a kind of hacky 5 minute solution is to just rewrite Mailable::buildRecipients() like this:

<?php

namespace App\Mail;

use Illuminate\Mail\Mailable as MailableBase;

class Mailable extends MailableBase
{
    protected function buildRecipients($message): static
    {
        parent::buildRecipients($message);

        if (count($this->replyTo)) {
            // erase any existing Reply-To header
            $message->getSymfonyMessage()->getHeaders()->remove('ReplyTo');

            // re-apply just ours
            foreach ($this->replyTo as $recipient) {
                $message->replyTo($recipient['address'], $recipient['name']);
            }
        }

        return $this;
    }
}

All it does is run the built-in Mailable::buildRecipients() and then erase any Reply-To header that got added before adding the defined values back in. If you haven't run Mailable::replyTo() then it doesn't do anything.

Just save it in app\Mail\Mailable.php and make App\Mail\SupportMessage extend the new class, instead of Laravel's built-in mailable.

miken32
  • 42,008
  • 16
  • 111
  • 154
  • This is untested BTW but based on what I saw in the `Mailable`, `Mailer`, and `Message` classes it should do the trick. – miken32 May 26 '23 at 19:32
0

As I know, Laravel replyTo doesn't override global reply_to.

Try this (Not tested. give it a try)

public function build()
{
    config(['mail.reply_to' => [
        'address' => $this->senderEmail,
        'name' => 'Sender'
    ]]);

    return $this->markdown('emails.support-message')
        ->subject('Support Message');
}

This will change the mail config in runtime and affect the current request only. The next request will restore the original config(reply_to).

Abdulla Nilam
  • 36,589
  • 17
  • 64
  • 85
0

Try this way to override the global 'reply_to' setting. This is tested in Laravel 5.8 . Thanks @miken32 for the hackish solution.

Just save the below code in app\Mail\MailableExtend.php and make App\Mail\SupportMessage extend the MailableExtend class, instead of Laravel's built-in mailable.

<?php

namespace App\Mail;

use Illuminate\Mail\Mailable as MailableBase;

class MailableExtend extends MailableBase
{

    protected function buildRecipients($message)
    {
        parent::buildRecipients($message);

        if (count($this->replyTo)) {
            //erase any existing reply-to header
            $message->getHeaders()->remove('reply-to');

            //add re-apply just ours
            foreach ($this->replyTo as $recipient) {
                $message->replyTo($recipient['address'], $recipient['name']);
            }
        }

        return $this;
    }

}
Suvash sarker
  • 3,140
  • 1
  • 18
  • 21