"async" Parameter using Mandrill API with laravel - php

This is my code:
$email = "email#gmail.com";
$data = array(
'email' => $email,
'async' => false,
);
Mail::queue('user.mails.welcome', $data, function($message) use ($email) {
$message
->to($email, $email)
->subject('Welcome to the Laravel 4 Auth App!');
});
And this is what shows on my Mandrill account API log:
Full Request {
"key": "x",
"raw_message": "y",
"async": "1"
}
Full Response {
"email": "xy",
"status": "queued",
"_id": "z"
}
As you can see, emails are being queued by default. Why? And how can I change that?

Per the Mandrill docs:
Async: enable a background sending mode that is optimized for bulk sending. In async mode, messages/send will immediately return a status of "queued" for every recipient. To handle rejections when sending in async mode, set up a webhook for the 'reject' event. Defaults to false for messages with no more than 10 recipients; messages with more than 10 recipients are always sent asynchronously, regardless of the value of async.
Basically, it's something that Mandrill is doing on their end is is unrelated to whether you use Mail::send or Mail::queue in Laravel.
All emails sending through Mandrill are queued and sent out in accordance with the parameters defined for your account. In other words: They decide when your emails eventually get sent.
Edit: Laravel's Mandrill Mail Transport is always going to send with async mode enabled. There's no way to configure that without editing the class: MandrillTransport

I am not sure I understand. Your emails are being queued because you are using the queue() method:
Mail::queue('user.mails.welcome', $data, function($message) use ($email) {
If you want to not queue, then use the send() method:
Mail::send('user.mails.welcome', $data, function($message) use ($email) {

I had to set a "from" email to have this work.

The 'async' parameter of the mandrill api isn't exposed to the Laravel mail closure. I just extended the MandrillTransport class and passed a Swift_Mime_Message into it so that I could capture the response, it will be 'sent' or 'rejected' (and reject_reason will be populated with something like "hard/soft bounce") which I can store in my application:
class Mandrill extends MandrillTransport
{
public function send(\Swift_Mime_Message $message, &$failedRecipients = null)
{
$client = $this->getHttpClient();
$response = $client->post('https://mandrillapp.com/api/1.0/messages/send-raw.json', [
'body' => [
'key' => $this->key,
'raw_message' => (string)$message,
'async' => false,
],
]);
return $response->json();
}
}

Related

SwiftMailer not sending mail in MessageHandler

I am using SwiftMailer in my Symfony 5 project to send emails.
I was using it in a controller to send a reset password e-mail, and everything was working.
I am now trying to use it in a MessageHandler, here is the code I am now using :
final class SendEmailMessageHandler implements MessageHandlerInterface
{
private $mailer;
public function __construct(\Swift_Mailer $mailer)
{
$this->mailer = $mailer;
}
public function __invoke(SendEmailMessage $message)
{
$mail = (new \Swift_Message())
->setFrom($message->getFrom())
->setTo($message->getTo())
->setBody($message->getBody(), $message->getContentType())
->setSubject($message->getSubject());
$response = $this->mailer->send($mail);
}
}
The response is ok, but the mail never reach my mailbox.
Here is how I am dispatching my SendEmailMessage :
class AskResetPassword extends AbstractController
{
use ResetPasswordControllerTrait;
private $resetPasswordHelper;
private $validator;
private $bus;
public function __construct(ResetPasswordHelperInterface $resetPasswordHelper, ValidatorInterface $validator, MessageBusInterface $bus)
{
$this->resetPasswordHelper = $resetPasswordHelper;
$this->validator = $validator;
$this->bus = $bus;
}
public function __invoke($data)
{
$emailConstraints = new Assert\Email();
$email = $data->getEmail();
if ($email) {
$errors = $this->validator->validate($email, $emailConstraints);
if (count($errors) === 0) {
return $this->processPasswordReset($email);
} else {
return new JsonResponse(['success' => false, 'error' => 'Invalid E-Mail format'], 404);
}
}
}
private function processPasswordReset($email)
{
$user = $this->getDoctrine()->getRepository(User::class)->findOneBy([
'email' => $email,
]);
$this->setCanCheckEmailInSession();
if (!$user) {
// Do not reveal whether a user account was found or not.
return new JsonResponse(['success' => true], 200);
}
try {
$resetToken = $this->resetPasswordHelper->generateResetToken($user);
} catch (ResetPasswordExceptionInterface $e) {
return new JsonResponse(['success' => false, 'error' => 'There was a problem handling your password reset request - ' . $e->getReason()]);
}
$message = new SendEmailMessage($email);
$message->setFrom('from.from#from.from');
$message->setBody(
$this->renderView('reset_password/email.html.twig', [
'resetToken' => $resetToken,
'tokenLifetime' => $this->resetPasswordHelper->getTokenLifetime()
])
);
$message->setSubject('Votre demande de changement de mot de passe');
$this->bus->dispatch($message);
return new JsonResponse(['success' => true], 200);
}
}
Here is my swiftmailer.yaml :
swiftmailer:
url: '%env(MAILER_URL)%'
spool: { type: 'memory' }
Can you help me ?
The answer is "DO NOT spool emails unless you want to process them later".
Check docs Spool Emails
A spooler is a queue mechanism which will process your message queue one by one. This was introduced when swift-mailer was rewritten and added back to symfony. In combination with Messenger Component which provides abstract interface MessageBusInterface, it would delegate to right backend service which can be smtp relay, push notification or any other type of RPC which may trigger actions on separate web services.
As symfony adds new capabilities to the message bus, this feature was added to utilize it for message queues & other services where transactional emails and notifications are processed separately.
To process your spool simply run :
APP_ENV=prod php bin/console swiftmailer:spool:send
In typical installation spooling is disabled, when you enable spooling in memory, it will wait till request is finished and kernel is about to exit. If you are using anything else to debug that terminates kernel halfway or there are other components & parts of application that keeps kernel in memory, events will not be triggered and mail will not be sent.
You can check whole documentation here : Sending Emails
I cant remember the exact reason why and at the time of posting this I'm struggling to find the answer but the swiftmailer type must be file instead of memory. This GitHub issue references this. You can also see how to change the type here.

How retrieve Mailgun Delivered Message in Laravel

In my Node.js application, I followed the Mailgun docs https://documentation.mailgun.com/en/latest/quickstart-sending.html#send-with-smtp-or-api for sending an email similar to the following:
mailgun.messages().send(data, (error, body) => {
console.log(body);
// body is {id: some_mailgun_built_id, message: 'Queued. Thank You'}
// which I store the body.id in my database
});
The issue I am faced with is how can I access that same Mailgun response when I send an email with Laravel? The Mailgun docs don't provide any examples showing how to retrieve that data.
This is how I am sending emails with Laravel:
\Mail::to($recipients)->send(
// this just renders my blade file which formats my email
new SendEmail($email);
);
// ?? How to get Message was sent object here
If anyone knows of any solution it would be greatly appreciated!
Hello and Welcome to SO!
Laravel has two events for the emails as explained in the official documentation: MessageSending and MessageSent
You can follow the events official documentation in order to listen for these specific events:
/**
* The event listener mappings for the application.
*
* #var array
*/
protected $listen = [
'Illuminate\Mail\Events\MessageSending' => [
'My\Email\Listener',
],
'Illuminate\Mail\Events\MessageSent' => [
'My\Other\Listener',
],
];
You will receive as input the Swift_message which contains a header that is the Mailgun ID you're looking for. Let's have a look at the MailgunTransport#send source code in order to understand what's going on behind the scenes:
/**
* {#inheritdoc}
*/
public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null)
{
// [...]
$response = $this->client->request(
'POST',
"https://{$this->endpoint}/v3/{$this->domain}/messages.mime",
$this->payload($message, $to)
);
$message->getHeaders()->addTextHeader(
'X-Mailgun-Message-ID', $this->getMessageId($response) // <-- HERE LARAVEL SETS THE MESSAGE ID
);
// [...]
}
Looking for the same key in your listener you can recover the message ID that mailgun assigned to your e-mail. Unfortunately you can't access the entire Mailgun response but with the API you can easily retrieve your message.

Send massive emails in laravel

Goodnight
Porblem 1.-
I need to send more than 1000 emails for each event created, and for this I use queue (as Laravel's documentation says), but when sending the emails I have to wait until all the emails are sent to return to the view of control Panel
this is my "store" function in NewsEvents.php controller that sends the emails
public function store(Request $request)
{
$attributes = request()->validate(News::$rules, News::$messages);
$news = $this->createEntry(News::class, $attributes);
//queue for sending emails
$this->dispatch(new Nevent($news));
return redirect_to_resource();
}
the "handle" function of job "Nevent.php"
public function handle()
{
//
$users=User::where('tipo_user','user')->get();
foreach($users as $user)
{
$user->notify(new EventCreated($this->news));
echo 'enviado correo';
Informe::create([
'event_id' => $this->news->id,
'total' => '1',
'tipo' => 'invitacion',
'dst_id' => $user->id,
'estado' => 'correcto',
]);
}
}
What could be the problem?
problem 2.-
How could I send an email for every minute?
since when sending all emails my server responded with this message:
Domain mu.edu.fi has exceeded the max emails per hour (100/100 (100%)) allowed. Message will be reattempted later
If u are using Redis server for managing jobs, Laravel provides a simple
API for Rate Limiting API's
Redis::throttle('your job id')->allow(10)->every(60)->then(function () {
// Job logic...
}, function () {
return $this->release(10);
});
Hope this helps.

How to set up headers "h:Reply-To" with Mailgun php API

How to set up headers "Reply-to" in Mailgun php API?
I've using this code but can't imaging hot to set up headers
Mail::send('email.message', $data, function ($message) use ($data) {
$message->to($data['to_email'], $data['to_name'])
->subject($data['subject'])
->from($data['from_email'], $data['from_name']);
});
It's as simple as adding a replyTo on your $message chain
Mail::send('email.message', $data, function($message) use($data)
{
$message->to($data['to_email'], $data['to_name'])
->subject($data['subject'])
->from($data['from_email'], $data['from_name'])
->replyTo('REPLY.TO.THIS#email.com');
});
if you want to add a name to the reply to, just add another parameter with the name:
->replyTo('REPLY.TO.THIS#email.com', 'Arsen Ibragimov')

How to handle callback when mail is sent in laravel 4

I wonder how to handle the callback when mail is sent? I want to do some action when the mail is sent. I know the code below is wrong, let's say $callbackOnSend is called before the mail is send and $callbackAfterSent is called after the mail is sent.
Mail::send(array('text' => 'view'), $data, $callbackOnSend, $callbackAfterSent);
I wonder how can I achieve the $callbackAfterSent?
If the mail is sent, the Mail::send() method will return true.
$mailVariables = ['from' => 'abc#abc.com'. 'fromName' => 'abc', 'to' => '123#123.com', 'cc' => 'xyz#xyz.com', 'attachment' => 'file1'];
Mail::send('emails.welcome', $data, function($message) use ($mailVariables)
{
$message->from($mailVariables['from'], $mailVariables['fromName']);
$message->to($mailVariables['to'])->cc($mailVariables['cc']);
$message->attach($mailVariables['attachment']);
});
If you need to do something if/after the mail is sent, you can wrap the whole Mail::send() in an if statement, however, from what I've read, it won't work if you have 'pretent' set to true in your config as with 'pretend' set to true, mail::send() will always return false.
if(Mail::send('emails.welcome', $data, function($message) use ($mailVariables)
{
$message->from($mailVariables['from'], $mailVariables['fromName']);
$message->to($mailVariables['to'])->cc($mailVariables['cc']);
$message->attach($mailVariables['attachment']);
})) {
// do something
}

Categories