Laravel: Get bad_domains from Mailable - php

I am sending mails with Laravel like this:
foreach ($users as $user) {
\Mail::to($user())->send(new Newsletter($user));
}
I would like to have an array of all the users who had a bad_domain response. I found in the docs that Laravel uses Swiftmailer which has a way to find bad_domain respones:
// Pass a variable name to the send() method
if (!$mailer->send($message, $failures))
{
echo "Failures:";
print_r($failures);
}
/*
Failures:
Array (
0 => receiver#bad-domain.org,
1 => other-receiver#bad-domain.org
)
*/
However, I want to use the a Mailable class. I am not sure how I can do this with the Swiftmailer (which I can access through \Mail::getSwiftMailer()).
Is there any easy way of getting the bad_domains when using Mailable from Laravel?

You may only access bad_domains, but not bounces with Swiftmailer (Swiftmailer 4 does not retrieve bounces as $failedRecipients).
One can get bad_domains it with
\Mail::to($user)->send(new \App\Mail\Hi());
dd(\Mail::failures());
See Illuminate\Mail\Mailer.php
/**
* Send a Swift Message instance.
*
* #param \Swift_Message $message
* #return void
*/
protected function sendSwiftMessage($message)
{
try {
return $this->swift->send($message, $this->failedRecipients);
} finally {
$this->forceReconnection();
}
}

Related

Email configuration dynamically not changing from database in loop

I apply a foreach loop in the email configuration SMTP settings dynamically from database.there are multiple SMTP servers in the SMTP server table. I am selecting the SMTP information dynamically from the table according to smtp_server_id from processing table and storing it in an email configuration before sending the email on the fly. But on the first iteration, the SMTP servers that come from dB are stored on email configuration like as config('mail.smtp.host') but on the second iteration till the end of loop, the SMTP information do not change. The SMTP information remains the same and Mail Configuration variables remains same when they are on the first iteration. What should I do to change the SMTP configuration dynamically one one by one according to the smtp_id in foreach loop.
My cron job to send emails.
namespace App\Console\Commands;
use App\Mail\SendEmail;
use App\Models\CronjobSetting;
use App\Models\ProcessingEmail;
use App\Models\SmtpServer;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Mail;
class SendEmailsBasedonDate extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'sendemailviadate:cron';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Command description';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return int
*/
public function handle()
{
$cronJob = CronjobSetting::whereDate('created_at', Carbon::today())->first();
if ($cronJob->email_group_type == 1) {
$processingEmails = ProcessingEmail::all();
foreach ($processingEmails as $processingEmail) {
$smtpServer = SmtpServer::where('id', $processingEmail->smtp_id)->first();
Config::set('mail.mailers.smtp.host', $smtpServer->hostname);
Config::set('mail.mailers.smtp.port', $smtpServer->port);
Config::set('mail.mailers.smtp.username', $smtpServer->username);
Config::set('mail.mailers.smtp.password', $smtpServer->password);
$email = new SendEmail($processingEmail);
Mail::to($processingEmail->recipient_email)->send($email);
if (Mail::failures()) {
ProcessingEmail::where('id', $processingEmail->id)->update(
['status' => 3]
);
} else {
ProcessingEmail::destroy($processingEmail->id);
}
}
} else {
$processingEmails = ProcessingEmail::where('email_group_id', $cronJob->email_group_id)->get();
foreach ($processingEmails as $processingEmail) {
$smtpServer = SmtpServer::where('id', $processingEmail->smtp_id)->first();
Config::set('mail.mailers.smtp.host', $smtpServer->hostname);
Config::set('mail.mailers.smtp.port', $smtpServer->port);
Config::set('mail.mailers.smtp.username', $smtpServer->username);
Config::set('mail.mailers.smtp.password', $smtpServer->password);
$email = new SendEmail($processingEmail);
Mail::to($processingEmail->recipient_email)->send($email);
if (Mail::failures()) {
ProcessingEmail::where('email_leads_id', $processingEmail->email_lead_id)->update(
['status' => 3]
);
} else {
ProcessingEmail::where('email_leads_id', $processingEmail->email_lead_id)->delete();
}
}
return Command::SUCCESS;
}
}
}
SO I will give you two options
one: register the MailServiceProvider after new config.
(new \Illuminate\Mail\MailServiceProvider(app()))->register();
two: creating a new Instance of swift mailer
<?php
$transport = (new \Swift_SmtpTransport('host', 'port'))
->setEncryption(null)
->setUsername('username')
->setPassword('secret')
->setPort($port);
$mailer = app(\Illuminate\Mail\Mailer::class);
$mailer->setSwiftMailer(new \Swift_Mailer($transport));
$mail = $mailer
->to('user#laravel.com')
->send(new SendEmail($processingEmail));
Basically, you are creating a new Instance of swift mailer here and adding providing to laravel mailer.

Swift_Mailer Symfony 3 add event listener to send event

How to add eventListener to swiftmailer send event?
Every time i send email i create a file and attach it to email, and after sending i want to unlink that file. How to do that?
Thanks.
file_put_contents($path, implode(";\r\n", $result));
$message = (new \Swift_Message('VAT checking result !'))
->setFrom('vah#gmail.com')
->setTo($vat->getEmail())
->setBody(
'Hello, ...' ,'text/')
->attach(\Swift_Attachment::fromPath($path));
// START send result email
$mailer = $this->container->get('mailer');
$listener = $this->container->get('app.service.send_email_listener');
$listener->setPathToFile($path);
$mailer->registerPlugin($listener);
$mailer->send( $message );
// END send email to admin
//unlink($path); email will not be sent
I tried to register listener like that
app.service.send_email_listener:
class: AppBundle\Listener\SendEmailListener
public: true
tags:
- { name: swiftmailer.plugin }
this is listener class :
namespace AppBundle\Listener;
use \Swift_Events_SendListener as base;
class SendEmailListener implements base
{
private $pathToFile;
public function setPathToFile($path)
{
$this->pathToFile = $path;
}
public function getPathToFile($path)
{
return $this->pathToFile;
}
/**
* Invoked immediately before the Message is sent.
*
* #param \Swift_Events_SendEvent $evt
*/
public function beforeSendPerformed(\Swift_Events_SendEvent $evt)
{
}
/**
* Invoked immediately after the Message is sent.
*
* #param \Swift_Events_SendEvent $evt
*/
public function sendPerformed(\Swift_Events_SendEvent $evt)
{
if($this->pathToFile){
unlink($this->pathToFile);
}
}
}
EDIT
It executes the method, but swift is not able to stream the file , beacuse the the file is unlinked before the sending is end...
This is from dev_logs:
[2018-05-24 20:40:18] php.CRITICAL: Uncaught Exception: Unable to open file for reading [C:\Users\\projects\vat\web\vatfiles\122.txt] {"exception":"[object] (Swift_IoException(code: 0): Unable to open file for reading [C:\\Users\\\projects\\vat\\web\\vatfiles\\122.txt] at C:\\Users\\projects\\vat\\vendor\\swiftmailer\\swiftmailer\\lib\\classes\\Swift\\ByteStream\\FileByteStream.php:144)"} []
As an alternative to using a Swiftmailer Plugin, I recommend using the __destruct magic method in your service/controller that utilizes the file(s). __destruct will be called when the object is released and will unlink any of the declared paths.
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class YourController extends Controller
{
private $paths = [];
public function someAction()
{
$this->paths[] = $path;
file_put_contents($path, implode(";\r\n", $result));
$message = (new \Swift_Message('VAT checking result !'))
->setFrom('vah#gmail.com')
->setTo($vat->getEmail())
->setBody('Hello, ...' ,'text/')
->attach(\Swift_Attachment::fromPath($path));
$mailer = $this->container->get('mailer');
$mailer->send( $message );
return $this->redirectToRoute('some_route');
}
public function __destruct()
{
if ($this->paths) {
array_map('unlink', $this->paths);
}
}
}
NOTE: This approach may not work if you use a spool to send
emails, as the
email will not be sent until the thresholds have been met for the
spool.
Symfony 2.3+ will automatically register the Swiftmailer plugin when you tag a service with swiftmailer.plugin. So there is no need to call $mailer->registerPlugin($listener);, where swiftmailer will simply ignore duplicate plugin registrations.

Laravel Notifications: Mail (Mailgun) Response on NotificationSent event

I am using Mailgun as a mail driver in my laravel application, as well as nexmo for SMS purposes.
What I am trying to achieve is to maintain the delivery status of the notifications that are sent either via Mailgun or Nexmo. Incase of Nexmo I am able achieve this, since I get the nexmo MessageId in the NotificationSent event that is fired after processing a notification.
However in the event instance for email, the response is empty.
Any idea what I am missing or, how I can retrieve the mailgun message-id?
I have found a workaround that does the job for now. Not as neat as I want it to be, but posting for future references incase anyone needs this.
I have created a custom notification channel extending Illuminate\Notifications\Channels\MailChannel
class EmailChannel extends MailChannel
{
/**
* Send the given notification.
*
* #param mixed $notifiable
* #param \Illuminate\Notifications\Notification $notification
* #return void
*/
public function send($notifiable, Notification $notification)
{
if (! $notifiable->routeNotificationFor('mail')) {
return;
}
$message = $notification->toMail($notifiable);
if ($message instanceof Mailable) {
return $message->send($this->mailer);
}
$this->mailer->send($message->view, $message->data(), function ($m) use ($notifiable, $notification, $message) {
$recipients = empty($message->to) ? $notifiable->routeNotificationFor('mail') : $message->to;
if (! empty($message->from)) {
$m->from($message->from[0], isset($message->from[1]) ? $message->from[1] : null);
}
if (is_array($recipients)) {
$m->bcc($recipients);
} else {
$m->to($recipients);
}
if ($message->cc) {
$m->cc($message->cc);
}
if (! empty($message->replyTo)) {
$m->replyTo($message->replyTo[0], isset($message->replyTo[1]) ? $message->replyTo[1] : null);
}
$m->subject($message->subject ?: Str::title(
Str::snake(class_basename($notification), ' ')
));
foreach ($message->attachments as $attachment) {
$m->attach($attachment['file'], $attachment['options']);
}
foreach ($message->rawAttachments as $attachment) {
$m->attachData($attachment['data'], $attachment['name'], $attachment['options']);
}
if (! is_null($message->priority)) {
$m->setPriority($message->priority);
}
$message = $notification->getMessage(); // I have this method in my notification class which returns an eloquent model
$message->email_id = $m->getSwiftMessage()->getId();
$message->save();
});
}
}
I am still looking for a solution to achieve this with NotificationSent event.
When looking at the code (MailgunTransport) it will do the following
$this->client->post($this->url, $this->payload($message, $to));
$this->sendPerformed($message);
return $this->numberOfRecipients($message);
Since the Laravel contract requires the implementation to send back the number of e-mails send.
Even if you would be able to get into the mail transport it doesn't store the response from for this reason it not possible to catch the message id.
What you could do is to implement your own (or look in packagist) to adapt mail client but this is not a perfect solution and will require some ugly instanceof checks.

Laravel: Invalid argument supplied foreach()

Working with a project that uses OAuth user registration through GitHub. Everything is working fine until the last step of confirming the account through my application.
Here is the function in question:
/**
* Get the primary, verified email address from the Github data.
*
* #param mixed $emails
* #return mixed
*/
protected function getPrimaryEmail($emails)
{
foreach ($emails as $email) {
if (! $email->primary) {
continue;
}
if ($email->verified) {
return $email->email;
}
throw new GithubEmailNotVerifiedException;
}
return null;
}
Anyone else ever experience this when working with OAuth and GitHub? Thank you
Not to be an arse, but it sounds like emails is not an array. Is it possibly a null?
Log the actual value with Log::debug('WTF IS THIS THEN?!!: '.print_r($emails, true)); and see.

looping phpmailer

when i send email i get two emails but it should send email to respective emails.
Lopoping problem ?
$array_values = Array
(
[0] => Array
(
[0] => uname1
[1] => fullname1
[2] => email 1
)
[1] => Array
(
[0] => uname2
[1] => fullname2
[2] => email 2
)
)
$f=0;
foreach($array_values as $mail_vars)
{
//$mail->AddReplyTo($mail_vars[2],'RED');
$mail->AddAddress($mail_vars[2], 'sss');
$body .="<br>";
$body .= 'Username: '. $mail_vars[0];
$body .="<br>";
$body .= 'Password: '.$mail_vars[1];
$body .="<br>";
$mail->SetFrom('email', 'FULLNAME');
$mail->Subject = "NEW";
$mail->MsgHTML($body);
//$mail->Send();
$f++;
}
Looking through the source of PHP Mailer, you will need to clear the fields. At least the address, maybe more. Here is the section of code from the PHPMailer class that has the clear functions. You are more than welcomed to look through them and try them etc. This is obviously an alternative to re-instantiating a new object, which may or may not cause a memory leak (depending on how many calls you make to it).
So implementing the clearAddresses code:
$mail->Subject = "NEW";
$mail->MsgHTML($body);
$mail->Send();
$mail->ClearAddresses(); // should reset the To address and remove the first one from it.
I removed the actual code as you just need the description and function name.
/////////////////////////////////////////////////
// CLASS METHODS, MESSAGE RESET
/////////////////////////////////////////////////
/**
* Clears all recipients assigned in the TO array. Returns void.
* #return void
*/
public function ClearAddresses() {
}
/**
* Clears all recipients assigned in the CC array. Returns void.
* #return void
*/
public function ClearCCs() {
}
/**
* Clears all recipients assigned in the BCC array. Returns void.
* #return void
*/
public function ClearBCCs() {
}
/**
* Clears all recipients assigned in the ReplyTo array. Returns void.
* #return void
*/
public function ClearReplyTos() {
}
/**
* Clears all recipients assigned in the TO, CC and BCC
* array. Returns void.
* #return void
*/
public function ClearAllRecipients() {
}
/**
* Clears all previously set filesystem, string, and binary
* attachments. Returns void.
* #return void
*/
public function ClearAttachments() {
}
/**
* Clears all custom headers. Returns void.
* #return void
*/
public function ClearCustomHeaders() {
}
if you look through the php mailer code, there is this useful method ClearAllRecipients() if you want to clear to, cc, and bcc all at once.
You need a:
$mail=new PHPMailer()
in the beginning of your for loop -as it is, the second time through it just messes around with the first email (since a new one isn't created).
As you pointed out body also needs to be reset - in fact using a separated var like that isn't very helpful - better to just supply directly to MsgHTML. Since the content of your email is trivial you may also want to send a plain-text version of the data (depends on your target recipient I guess).
So the updated script:
foreach($array_values as $mail_vars)
{
$mail=new PHPMailer();
$mail->SetFrom('email', 'FULLNAME');
$mail->AddAddress($mail_vars[2], 'sss');
$mail->Subject = "NEW";
$mail->MsgHTML("<br>\nUsername: ".$mail_vars[0]."<br>\nPassword: ".$mail_vars[1]."<br>");
//$mail->Send();
$f++;
}

Categories