Laravel provides the possibility to implement your own mail transport in order to send email over APIs that they aren't implemented in the framework itself by extending Illuminate\Mail\Transport\Transport. I have done so for the gmail API as this provides some benefits over sending over SMTP. There is however a scenario which is giving me some difficulties: the API requires you to impersonate a given user to send mail as that user. Currently, I'm using the 'from' address to perform that action.
use Illuminate\Mail\Transport\Transport;
use Google\Client as GoogleClient;
class MailTransport extends Transport
{
public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null)
{
$client = new GoogleClient;
// initialise the client further, code omitted
$client->setSubject(
array_key_exists(0, array_keys($message->getFrom()))
? array_keys($message->getFrom())[0]
: self::DEFAULT_SUBJECT
);
// send the email here using the impersonated $client
}
}
This works fine, however I'm running into an issue when I want to send a mail from a mail alias of that user. In that case, the from address doesn't match the google account to impersonate and sending the mail doesn't work. I'm looking for a way to pass the google account to impersonate to the Mailtransport. As an instance of this class is automatically created by the framework and only Swift_Mime_SimpleMessage can be used, my best solution right now is to add a custom header to the email which is read and then removed by the MailTransport class. This feels like a dirty workaround however, does anybody know a cleaner solution?
Related
Currently, I am trying to integrate Mandrill functionality into a Zend 1 legacy application, and I'm not too familiar with the way they handle mail transports. Currently, I'm including the mandrill/mandrill composer package and attempting to follow the pattern of the other transport layers included in Zend 1:
class MandrillTransport extends Zend_Mail_Transport_Abstract
{
private $message = array();
protected function _formatMandrillArray()
{
//grab all the relavant data from the pre-existing mail object and put it into a Mandrill-friendly array
}
protected function _getAttachments() {
//loop through and format attachments that get added to array
}
protected function _sendMail()
{
//send Mandrill message with included data
$mandrill = new Mandrill(Zend::registry('config')->mandrill->APIKey);
$mandrill->messages->send($this->message);
}
public function send() {
//Format all the stuff and send it
}
}
And then I'm assuming I can use this in the "send()" function, which appears to take the transport as a parameter:
//TODO: Put this in a better spot
require_once __DIR__ . "/ZendFramework/Zend/Mail/Transport/MandrillTransport.php";
$tr = new MandrillTransport();
$email->send($tr);
Am I on the right track here? The reason I'd like to do this as a transport layer rather than just ripping out the existing code and replacing it with Mandrill is because there are a lot of different business processes sending mail, and we'd like to be able to pick and choose which is using the Sendmail transport and which are using Mandrill.
Thanks in advance,
-Chris
I need to send hundreds of emails using different credentials from laravel.
Each customer of mine has his/hers mail list and needs to provide their own SMTP server. I process that list and send emails on customer's behalf.
This is what I have so far. It is working, but it is very slow and I don't have many emails so far. I see a problem when I get more emails.
Any suggestions on how to improve?
PS- I use cron Console Command and use Kernel to schedule the job.
public function sendMailings($allMailings) {
foreach ($allMailings as $email) {
Config::set('mail.host', $email['smtpServer']);
Config::set('mail.port', $email['smtpPort']);
Config::set('mail.username', $email['smtpUser']);
Config::set('mail.password', $email['smtpPassword']);
Config::set('mail.encryption', $email['smtpProtocol']);
Config::set('mail.frommmail', trim($email['fromEmail']));
Config::set('mail.fromuser', trim($email['fromUser']));
Config::set('mail.subject', trim($email['subject']));
Config::set('mail.toEmail', trim($email['toEmail']));
Config::set('mail.toName', trim($email['toName']));
Config::set('mail.pretend', false);
$email_body = $email['emailBody'];
Mail::send('emails.availability, compact('email_body')
, function($message) {
$message->from(config('mail.username'), config('mail.fromUser'));
$message->replyTo(config('mail.frommmail'), config('mail.fromUser'));
$message->to(config('mail.toEmail'), config('mail.toName'))->subject(config('mail.subject'));
});
Log::info('Mail was sent');
}
}
You can not change email provider configs on-the-fly, so you must make new instance of mailer in service container. I did it before, i wrote a method in my own class to get new mailer instance:
/**
* #return Mailer
*/
protected function getMailer()
{
// Changing mailer configuration
config(['mail.driver' => static::getName()]);
// Register new instance of mailer on-the-fly
(new MailServiceProvider($this->container))->register();
// Get mailer instance from service container
return $this->container->make('mailer');
}
Sending e-mail messages directly in web app can drastically slow down the responsiveness of your application. You should always queue your messages.
Instead of Mail::send You can use Mail::queue
and then from cron or manually call
php artisan queue:work
That will process the next item on the queue. This command will do nothing if the queue is empty. But if there’s an item on the queue it will fetch the item and attempt to execute it.
In my project I'm trying to send e-mail through my own SMTP server using Laravel 5. I have everything set up correctly using the SMTP driver and I'm managing to send and receive e-mail fine.
I want to get set up using DKIM. I've set up my public key and made it available in my DNS and I have a private key ready to start signing my messages.
However, I can't find any documentation on how to set up DKIM signing using Laravel/Swift Mailer. I've managed to sign my e-mails with DKIM before in another non-Laravel project that used PHPMailer but can't find any way of doing it here. I've had a browse through Illuminate\Mail\Message and Illuminte\Mail\Mailer but can't find anything relevant.
Does anyone know how to do this?
Current example code:
public function handle(UserWasRegistered $event)
{
$user = $event->getUser();
$this->mailer->send(['emails.users.welcome.html', 'emails.users.welcome.text'], ['user' => $user], function($message) use($user) {
$message->subject('Welcome to XXXXXX');
$message->to($user->email);
});
}
Ideally, I would like to be able to provide my DKIM private key in the config somewhere and have Laravel/Swift Mailer (Or write some code once) sign my messages for me.
Cheers
I wrote decorator of MailServiceProvider for Laravel 5 that provides ability to sign outgoing messages with DKIM:
https://github.com/vitalybaev/laravel5-dkim
You have to extend Laravel Mailer and mail service provider. Default laravel mailer using new Swift_Message class not Swift_SignedMessage thats why you don't have options for signing.
Here is a package for laravel 4.
I'm trying to use different SMTP configuration for each user of my application. So, using Swift_SmtpTransport set a new transport instance, assign it to Swift_Mailer and then assign it to Laravel Mailer.
Below the full snippet:
$transport = Swift_SmtpTransport::newInstance($mailConfig['smtp_host'], $mailConfig['smtp_port'], 'ssl');
$transport->setUsername($mailConfig['smtp_user']);
$transport->setPassword($mailConfig['smtp_pass']);
$smtp = new Swift_Mailer($transport);
Mail::setSwiftMailer($smtp);
Mail::queue(....);
Messages are added to the queue but never dispatched. I guess that since the "real" send is asyncronous it uses default SMTP configuration, and not the transport set before Mail::queue().
So, the question is: how to change mail transport when using Mail::queue()?
Instead of using Mail::queue, try creating a queue job class that handles sending the email. That way the transport switching code will be executed when the job is processed.
The Job Class Structure Documentation actually uses a mailing scenario as an example, which receives a Mailer instance that you can manipulate. Just use your code in the class's handle method:
public function handle(Mailer $mailer)
{
$transport = Swift_SmtpTransport::newInstance($mailConfig['smtp_host'], $mailConfig['smtp_port'], 'ssl');
$transport->setUsername($mailConfig['smtp_user']);
$transport->setPassword($mailConfig['smtp_pass']);
$smtp = new Swift_Mailer($transport);
$mailer->setSwiftMailer($smtp);
$mailer->send('viewname', ['data'], function ($m) {
//
});
}
Best way from notifications since Laravel 7 : https://laravel.com/docs/9.x/notifications#customizing-the-mailer
public function toMail($notifiable)
{
return (new MailMessage)
->mailer('postmark')
->line('...');
}
I am working on a test where I need 300 different mailers (to fill Mailtrap Enterprise) so having 300 different configs was not good for me.
I am posting the simple solution I found looking at Illuminate\Mail\Mailer class
$transport = Transport::fromDsn('smtp://user:pass#host:port');
Mail::setSymfonyTransport($transport);
Mail::to('to#email.com')
->send((new LaravelMail())
->subject('Subject')
);
Having this you can do some string manipulation to switch between transport configurations
I use Mandrill mail driver for tests. I have a remote staging, that I seed after deploy. And during seeding I try to disable email sends, that are linked to certain events.
Placing this in seeder:
Config::set('mail.driver', 'log');
Config::set('mail.pretend', true);
Has no effect. I don't understand why. I place this in root DatabaseSeeder#run or/and in child seeders — the same. Calls to Mandrill are still performed.
Is there a solution for this problem?
The reason your
Config::set('mail.driver', 'log');
Config::set('mail.pretend', true);
aren't working is because the mail object doesn't check these values before sending mail. Whaaaaaaaa?. If you take a look at the sendSwiftMessage method in the mailer class
#File: vendor/laravel/framework/src/Illuminate/Mail/Mailer.php
protected function sendSwiftMessage($message)
{
if ($this->events)
{
$this->events->fire('mailer.sending', array($message));
}
if ( ! $this->pretending)
{
$this->swift->send($message, $this->failedRecipients);
}
elseif (isset($this->logger))
{
$this->logMessage($message);
}
}
You can see the class checks $this->pretending, and not the configuration, before deciding if it should send the mail or not. So what sets pretending? That's in the MailServiceProvider class's register method.
public function register()
{
//...
$pretend = $app['config']->get('mail.pretend', false);
$mailer->pretend($pretend);
//...
}
When Laravel boots up and registers each service provider, it eventually registers the mail service provider and that's when it reads the configuration, and then tells the mailer if it should "pretend" or not. By the time you're calling this in your seeder, the mailer's already loaded it's configuration value.
Fortunately, there's a pretty easy solution. The mailer object is a singleton/shared service, and has public methods available to control if it should pretend or not. Just call the pretend method yourself instead of setting configuration values
Mail::pretend(true); //using the `Mail` facade to access the mailer object.
you should be able to turn the mailer off programatically.
This is an answer for Laravel 5.7, because pretend doesn't exists:
If you want to disable mail while seeding the database, you could simply 'abuse'
Mail::fake()
I think in two possibilities, you can try:
You can set the command to enable the mail pretend on-the-fly:
Mail::pretend();
The db seed are running with more than one request:
As is write here:
Configuration values that are set at run-time are only set for the
current request, and will not be carried over to subsequent requests.
So you can try set this config over requests, like a session, than finish in the end of the seeding.