Laravel: How to Queue mails to send later - php

Im trying to use the Mail::queue to send and email, but when I call this function it simple sends the mail, and the response is delayed ... I thought that the point of using Mail::queue was to queue ....
I want the response to came instantly, not having to wait for the email to be sent
for eg
Mail::queue('emails.template', $data, function($message) {
$message->to('somemail#gmail.com');
$message->subject('Notificacion');
});
return Response::json(array('error' => 0, 'message' => 'Ok'));
I want to receive the response without waiting for the mail to be sent.
How can I do that???

What queue driver (app/config/queue.php - 'default' param) are you using? If you're using sync, and haven't set up one of the others, then you're using the synchronous driver, which does exactly what the name says: Runs your queued task as soon as the task is created.
You need to configure an MQ server for Laravel to talk to. You can get a free iron.io account for this, and then you need to configure it, for instance:
'iron' => array(
'driver' => 'iron',
'project' => 'iron-io-project-id',
'token' => 'iron-io-queue-token',
'queue' => 'queue-name',
),
Then when you use Mail::queue() it will push the instruction to iron.io. You'll then have to have another thread listening on the queue - just run php artisan queue:listen and leave it running while messages are pushed to the queue.

/**
* Get all email recipients and include their user details for Mailgun's
* template tags - %recipient.userToken%
*/
private function getRecipients()
{
foreach (User::get() as $user)
{
$this->recipients[$user->email] = [
'id' => $user->id,
'userToken' => $user->user_token,
'first_name' => $user->first_name,
'last_name' => $user->last_name,
'email' => $user->email
];
}
}
private function sendEmail()
{
$subject = 'Demo Subject';
/**
* Data for the Blade template
*/
$data = [
'foo' => 'bar'
];
// Inline the CSS for the email
$inliner = new InlineEmail('emails.some-email', $data);
$content = $inliner->convert();
// Create Emails table entry for this email. Used for Mailgun webhooks
$email = Email::create(['user_id' => $this->userId, 'subject' => $subject, 'email_id' => str_random()]);
// Prepare the email addresses
$emailAddresses = array_column($this->recipients, 'email');
$this->mailgun->sendMessage('demo.org', [
"from" => 'support#demo.org',
"to" => implode(',', $emailAddresses), // Comma separated list of email addresses
"subject" => $subject,
"html" => $content, // Inlined CSS HTML from Blade
"text" => "Plain text message here",
"recipient-variables" => json_encode($this->recipients), // Required for batch sending, matches to recipient details
"v:messageId" => $email->id, // Custom variable used for webhooks
]);
}

Related

Laravel: Dynamic email content on API

I am building an email API on Laravel. I want to send emails via this API on my other projects. I send emails successfully through this API. However, the email content is hardcoded on the client side. For example:
$body = "Dear $user->name,<br />
Thank you for your order.";
$client->request('POST', 'http://api.test/send_mail?token=' . $token, ['form_params' => [
'from' => 'client#test.com',
'title' => 'API Test Mail',
'to' => 'customer#test.com',
'body' => $body,
'signature' => 'Regards'
]]);
My aim is especially creating dynamic email body without hardcoded. So, I save email content to the database. My plan is to trigger the saved email content with request on the client side. After some researches on email systems with similar structures, I think, I have to save the related content to the database like this format:
Dear {{ username }}
Thank you for your order.
By this way, I will only send the username info to the API from the client side. I will not have to deal with HTML email content.
However, I am not sure about what to do after this stage. How can I send and parse the $username info to the API from the client side? Do I have to add request to the API on every single parameters? Here is my current request rules:
return [
'title' => 'required',
'from' => 'required|email',
'to' => 'required|email',
'body' => 'required',
'signature' => 'nullable',
'email_template' => 'nullable'
];
Also this is my mail markdown:
#component('mail::message')
{!! $mail['body'] !!}
#if(array_key_exists('signature', $mail))
<p>{!! $mail['signature'] !!}</p>
#endif
#endcomponent
Actually, dynamic parameters will be child members of the body. If I want to add $username as a dynamic variable to the email body, should I add it to the rules of the request and also to the mail markdown? If so, I have to do some static transactions on every saved different email contents and their different parameters.
How can I build this structure in a clearer way?
Yes, You should parse every parameters to your API.
You may do like this:
$data['username'] = 'John';
$data['other'] = 'other';
$client->request('POST', 'http://api.test/send_mail?token=' . $token,
['form_params' =>
[
'from' => 'client#test.com',
'title' => 'API Test Mail',
'to' => 'customer#test.com',
'data' => $data,
'signature' => 'Regards',
'template_id' => 'template_id' // parse if you have multi templates
]]);
And you can send email like this in API controller.
public function sendEmail(Request $request)
{
$template = Template::findorfail($request->template_id);
$body = $template->body;// this is template dynamic body. You may get other parameters too from database. $title = $template->title; $from = $template->from;
foreach($request->data as $key=>$parameter)
{
$body = str_replace('{{'.$key.'}}', $parameter, $body); // this will replace {{username}} with $data['username']
}
$request->body = $body;
$mailObject = new MyMail($request); // you can make php artisan make:mail MyMail
Mail::to($request->to)->send($mailObject);
}
...
class MyMail extends Mailable
{
use Queueable, SerializesModels;
public $request;
public function __construct($request)
{
$this->request= $request;
}
public function build()
{
return $this->subject($this->request['title'])
->from($this->request['from'])
->markdown('components.mail.myTemplate');
}
}
Then in myTemplate.blade.php you can render {!!$request['body'] !}}
Hope this helps you.

AWS SES SMTP email works. AWS SES API does not send email

I am using AWS SES on a website with shared hosting platform (not on AWS)
When I send emails using the SMTP it works perfectly. When I attempt to call and use the API, no email is sent, no error message is received.
There are two reasons I want to use the API instead of SMTP
1) According to Amazon, the API is faster.
2) One of my transactional emails is a reminder to clients to log in, and/or pay their account. SMTP cannot send email to more than 50 (if I am not mistaken). For that reason, I need to call the SendRawEmail.
Please help me with my code to send the email and to implement SendRawEmail
Here is my code:
require ('PHPMailer/PHPMailerAutoload.php');
use Aws\Ses\SesClient;
use Aws\Exception\AwsException;
// Create an SesClient. Change the value of the region parameter if you're
// using an AWS Region other than US West (Oregon). Change the value of the
// profile parameter if you want to use a profile in your credentials file
// other than the default.
$SesClient = new SesClient([
'profile' => 'default',
'version' => '2010-12-01',
'region' => 'eu-central-1'
]);
// Replace sender#example.com with your "From" address.
// This address must be verified with Amazon SES.
$sender_email = 'info#mydomain.com';
// Replace these sample addresses with the addresses of your recipients. If
// your account is still in the sandbox, these addresses must be verified.
$recipient_emails = ['info#example.com'];
// Specify a configuration set. If you do not want to use a configuration
// set, comment the following variable, and the
// 'ConfigurationSetName' => $configuration_set argument below.
//$configuration_set = 'ConfigSet';
$subject = 'Amazon SES test (AWS SDK for PHP)';
$plaintext_body = 'This email was sent with Amazon SES using the AWS SDK for PHP.' ;
$html_body = '<h1>AWS Amazon Simple Email Service Test Email</h1>'.
'<p>This email was sent with <a href="https://aws.amazon.com/ses/">'.
'Amazon SES</a> using the <a href="https://aws.amazon.com/sdk-for-php/">'.
'AWS SDK for PHP</a>.</p>';
$char_set = 'UTF-8';
try {
$result = $SesClient->sendEmail([
'Destination' => [
'ToAddresses' => $recipient_emails,
],
'ReplyToAddresses' => [$sender_email],
'Source' => $sender_email,
'Message' => [
'Body' => [
'Html' => [
'Charset' => $char_set,
'Data' => $html_body,
],
'Text' => [
'Charset' => $char_set,
'Data' => $plaintext_body,
],
],
'Subject' => [
'Charset' => $char_set,
'Data' => $subject,
],
],
// If you aren't using a configuration set, comment or delete the
// following line
'ConfigurationSetName' => $configuration_set,
]);
$messageId = $result['MessageId'];
echo("Email sent! Message ID: $messageId"."\n");
} catch (AwsException $e) {
// output error message if fails
echo $e->getMessage();
echo("The email was not sent. Error message: ".$e->getAwsErrorMessage()."\n");
echo "\n";
}
?>
Check your error log. I had the same issue:
require 'AWS/aws-autoloader.php';
For other SDK uses (such as API calls) you may need to create a .aws folder to include credentials

Sending large amounts of emails using PHP Mailer with CodeIgniter 3

I need help sending large amounts of emails through my newly designed automated email platform.
I am using PHP Mailer Library to send emails to my contacts
I built my script in CodeIgniter 3
I am using VPS level 3 from Godaddy
My email system freezes after 5 or more emails in mail queue manager
I am assuming that my script is designed for large volumes of emails.
Mail queue manager in Godaddy WHM panel shows all my emails as FROZEN
Part of my script:
$options = [
'user_id' => $this->me->id,
'limit' => 200000,
'type' => $exploded[1]
];
$this->data['contacts'] = $this->crm_model->get_portal_contacts($options);
$emails = array();
foreach ($this->data['contacts'] as $contact) {
$this->data['fullname'] = get_user($contact)->full_name(false);
$emaildata = [
'from' => ['name' => 'Domain', 'email' => '<no-reply#domai.org>\r\n'],
'to' => strip_tags(get_user($contact)->email),
'subject' => $this->data['email']['email_subject'],
'message' => $this->load->view('np_sh/email/email-template', $this->data, TRUE)
];
$status = _send_mail($emaildata);
}
_send_mail() function is merging $emaildata variable with the default parameters and preparing to send email using PHPMailer and returning boolean status.

Override CakeEmail destination globally

Is there an easy way to "override" the destination of an email using CakeEmail?
I have a model method that send an email using this piece of code:
$Email = new CakeEmail();
$Email->config('default');
$sent = $Email->template('new-notification')
->subject('A new notification for you')
->viewVars([
'name' => $foo['User']['name'],
'text' => $foo['Notification']['text'],
)
->emailFormat('html')
->to($foo['User']['email'])
->send();
And this configuration:
class EmailConfig
{
public $default = array(
'host' => 'smtp.server.com',
'port' => 587,
'username' => 'user#domain.com',
'password' => 'pwdAsYouKnow',
'from' => array('noreply#domain.com' => 'Company'),
'transport' => 'Smtp'
);
}
As you can see, I send the email for a dynamically defined user's email.
My objective is when I'm developing locally on my machine, to force every call to ->send() to force a destination like developer#domain.com.
I can easily detect if I'm on development, but I don't know how to force in a "master" way to CakeEmail only send to a defined account overriding the one set on the call.
I already tried to set an
'to' => array('developer#domain.com' => 'Dev'),
inside $default but, no success.
I appreciate any help, thanks!
i'm assuming when you are on local machine you run in debug mode, so you can check if debug mode is on then use that to send to different email
if(Configure::read('debug')){
$emailTo = 'developer#domain.com'
}
else{
$emailTo = $foo['User']['email'];
}
then you just use variable for email address:
$sent = $Email->template('new-notification')
->subject('A new notification for you')
->viewVars([
'name' => $foo['User']['name'],
'text' => $foo['Notification']['text'],
)
->emailFormat('html')
->to($emailTo)
->send();

send email by Mandrill

I need to send an email for mandrill. This Mandrill API implemented in my project, and send a test mail by apikey provided by the client. I have two options for sending mail, one is by my account of Mandrill and the other is that the user can login by MailChimp. Then insert the API KEY mandrill your account.
I have a default variable in conf file like this:
public $default = array(
'transport' => 'Smtp',
'from' => array('noreply#example.com' => 'Example'),
'host' => 'smtp.mandrillapp.com',
'port' => 587,
'timeout' => 30,
'username' => 'example#example.com',
'password' => '12345678',
'client' => null,
'log' => false
//'charset' => 'utf-8',
//'headerCharset' => 'utf-8',
);
To send mail through my account mandrill I do this:
$email = new FrameworkEmail();
$email->config('default');
$email->emailFormat('html');
And I give my account Mandrill data. But if the user chooses to identify with MailChimp and also add your API KEY Mandrill mails should be sent from your account transactional emails and not mine. Any idea if this is possible?
I do not believe this is possible to authenticate with MailChimp and then send through Mandrill. The Mandrill API uses a different set of keys than the MailChimp API, so you won't have access to a user's Mandrill API keys if they're logged in through MailChimp.
EDIT: If you have a user's Mandrill API key, you should be able to feed it directly into the Mandrill send function from the Mandrill SDK:
<?php
$mandrill = new Mandrill('USER_PROVIDED_API_KEY_GOES_HERE');
$message = array(
'html' => '<p>html content here</p>',
// other details here
)
$async = False;
$ip_pool = null;
$send_at = null;
$result = $mandrill->messages->send($message, $async, $ip_pool, $send_at);
?>
More details on the Message function in the SDK can be found here.
EDIT #2: Same approach can be achieved using CakeEmail - you just need to instantiate the $email class at the time that you receive the user's API key, rather than before.
CakeEmail recommends in a standard setup that you do this in your initial configuration:
class EmailConfig {
public $mandrill = array(
'transport' => 'Mandrill.Mandrill',
'from' => 'from#example.com',
'fromName' => 'FromName',
'timeout' => 30,
'api_key' => 'YOUR_API_KEY',
'emailFormat' => 'both',
);
}
But we can't set this up with default values and then change it per user, using their API key. We need to instantiate this at the time that we receive the user's email, like so:
App::uses('CakeEmail', 'Network/Email');
// PHP code that takes in user email
$user_api_key = // this would come from a form, or from a user's record in the database
$email = new CakeEmail(array(
'transport' => 'Mandrill.Mandrill',
'from' => 'from#example.com',
'fromName' => 'FromName',
'timeout' => 30,
'api_key' => $user_api_key,
'emailFormat' => 'both',
));
// add CakeEmail details like $email->template, $email->subject(), etc.
$email->send();
So the principles are the same regardless of what framework is used. Rather than instantiating Mandrill configuration details in a global setting (like most email frameworks recommend), you will need to instantiate them at the time the user wants to send an email, using user-specific details.

Categories