Ícone do site Mailtrap

Tutorial do Programador: Como Enviar Emails no Laravel com SMTP e API do Gmail

Se você planeja usar o Laravel para enviar emails através do Gmail em seu projeto Laravel atual ou futuro, você veio ao lugar certo.

Neste artigo, mostro como fazer isso passo a passo!

Para pular a configuração e ir direto ao envio com SMTP, clique aqui. Para pular direto para o envio com API, clique aqui.

Configurando o serviço de email do Laravel antes de enviar emails

Nota: Os métodos descritos são compatíveis com Laravel 8, Laravel 9 e Laravel 10. Se você estiver usando versões anteriores, pode ser necessário fazer modificações adicionais no código.

Construindo sua aplicação

Se você ainda não possui um aplicativo Laravel, pode seguir os seguintes passos para criar um.

Primeiro, utilizando o terminal, crie um novo projeto Laravel executando laravel new app_email_in_laravel ou o comando composer create-project laravel/app_email_in_laravel (naturalmente, dentro dos comandos, substitua “app_email_in_laravel” pelo nome do seu aplicativo).

Em seguida, utilize o comando cd app_email_in_laravel para mudar para um diretório chamado app_email_in_laravel (certifique-se de que este nome corresponde ao seu diretório real).

Esta etapa é essencial para executar o comando Laravel que cria classes de mailables (mais detalhes na próxima seção).

Por fim, para visualizar seu aplicativo Laravel em um navegador web, execute o comando php artisan serve.

Configuração

No framework Laravel, você pode configurar os serviços de email no arquivo config/mail.php. Este arquivo contém uma série de configurações de mailers com entradas de configuração de exemplo para vários drivers/transportes de email suportados pelo Laravel.

O valor de configuração padrão neste arquivo determina o mailer padrão ao enviar um email do seu aplicativo Laravel.

Graças aos múltiplos mailers que podem ser configurados no arquivo config/mail.php file, você pode utilizar diferentes serviços de envio de emails para diferentes tipos de emails.

Gerando e escrevendo mailables

Vários tipos de emails no Laravel são representados por uma classe chamada “mailable”, que é armazenada no diretório app/Mail.

Este diretório não está presente por padrão em sua aplicação, mas é gerado automaticamente quando você cria sua primeira classe mailable usando este comando Artisan CLI:

php artisan make:mail MailableName

Após criar uma classe mailable, você pode visualizar seu conteúdo e configurar a classe usando os seguintes métodos:

Configuração do remetente

Para especificar o remetente – o endereço de email e o nome do “from” – você tem duas opções: usar o objeto Envelope da mensagem ou definir um endereço global “from”.

use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Envelope;
 
/**
* Obter o envelope da mensagem.
*
* @return \Illuminate\Mail\Mailables\Envelope
*/
public function envelope()
{
   return new Envelope(
       from: new Address('example@example.com', 'Test Sender'),
       subject: 'Test Email',
   );
}
'from' => ['address' => 'example@example.com', 'name' => 'App Name']

Nota: Se você quiser usar o mesmo endereço “from” para todos os emails enviados pela sua aplicação, é recomendado usar o método do endereço global “from”. Esta abordagem é conveniente pois elimina a necessidade de chamar o método from em cada uma das suas classes mailable, e serve como o endereço “from” padrão se nenhum outro endereço for especificado.

Como enviar emails no Laravel usando o SMTP Gmail?

O passo inicial para enviar emails usando o servidor SMTP do Gmail é adicionar sua configuração SMTP do Gmail ao arquivo .env da sua aplicação.

MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=465
MAIL_USERNAME=mygoogle@gmail.com 
MAIL_PASSWORD=*************
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=mygoogle@gmail.com
MAIL_FROM_NAME="${APP_NAME}"

Após a configuração do SMTP ser concluída, é hora de gerar uma dessas classes mailable que mencionamos anteriormente usando o seguinte comando.

php artisan make:mail MyTestEmail

Ao executar o comando no Artisan CLI, uma nova classe chamada “MyTestEmail” será gerada no diretório app/Mail.

Se desejar, você pode inspecionar o código da classe abaixo:

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class MyTestEmail extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Criar uma nova instância da mensagem.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Obter o envelope da mensagem.
     *
     * @return \Illuminate\Mail\Mailables\Envelope
     */

    public function envelope()
    {
        return new Envelope(
            subject: 'My Test Email',
        );
    }
 /**
     * Obter a definição do conteúdo da mensagem.
     *
     * @return \Illuminate\Mail\Mailables\Content
     */

    public function content()
    {
        return new Content(
            view: 'view.name',
        );
    }

    /**
     * Obter os anexos para a mensagem.
     *
     * @return array
     */
    public function attachments()
    {
        return [];
    }
}

Como evidente no código acima, o método content() retornará uma view. Portanto, você precisa navegar até o diretório resources/views, criar uma nova pasta e, dentro dela, um arquivo blade.php.

Quando o arquivo tiver sido gerado, pode adicionar conteúdo.

// resources/views/mail/test-email.blade.php
Oi, 
Seu app Laravel já consegue enviar emails? 😉 
Funny Coder

Depois, volte ao método content() e atualize o nome da view retornada com o nome do novo arquivo criado.

Se desejar adicionar conteúdo dinâmico, você pode usar o atributo with para incluir o nome do destinatário em seu arquivo de email template/blade.php.

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

class MyTestEmail extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Criar uma nova instância de mensagem.
     *
     * @return void
     */
    public function __construct(private $name)
    {
        //
    }

    /**
     * Obter o envelope da mensagem.
     *
     * @return \Illuminate\Mail\Mailables\Envelope
     */
    public function envelope()
    {
        return new Envelope(
            subject: 'My Test Email',
        );
    }

    /**
     * Obter a definição do conteúdo da mensagem.
     *
     * @return \Illuminate\Mail\Mailables\Content
     */
    public function content()
    {
        return new Content(
            view: 'mail.test-email',
            with: ['name' => $this->name],
        );
    }
}

Você também precisará fazer uma pequena modificação no arquivo view test-email.blade.php e fazer com que ele aceite a variável $name como parâmetro.

// resources/views/mail/test-email.blade.php

Oi {{$name}}, 
Seu app Laravel já consegue enviar emails? 😉 
Mailtrap

Como etapa final, adicione uma rota no arquivo routes/web.php usando o seguinte código:

<?php

use Illuminate\Support\Facades\Route;
use App\Mail\MyTestEmail;
use Illuminate\Support\Facades\Mail;

Route::get('/testroute', function() {
    $name = "Funny Coder";

    // O envio do email é feito usando o método to na facade Mail
    Mail::to('testreceiver@gmail.com')->send(new MyTestEmail($name));
});

Para testar a funcionalidade, você pode executar o comando php artisan serve e então acessar seu navegador web. No navegador, cole o URL da rota que você criou.

Assim que tiver feito isso, se tudo estiver funcionando corretamente, um email deve ser enviado para o endereço “to” que você especificou.

Para aprender como enviar um email no formato HTML, com anexo ou com múltiplos destinatários, leia nosso artigo Como Enviar Emails no Laravel: Um Guia Completo de SMTP & API.

Enviar email para múltiplos destinatários

Para enviar emails para múltiplos destinatários, via servidor de email SMTP, você pode passar uma série de endereços de email para o método to(). Este é um método rápido e simples, mas note que os destinatários verão uns aos outros.

Route::get('/sendToMultiple', function() {
    $emails = ['first@example.com', 'second@example.com', 'third@example.com'];
    $name = "Funny Coder"; // Supondo que você queira enviar o mesmo conteúdo para todos

    Mail::to($emails)->send(new MyTestEmail($name));
});

No trecho acima, $emails é uma série de endereços de email para os quais o email será enviado. A MyTestEmail é a classe mailable que constrói o email, e, como mencionado, todos os destinatários estão listados no método to().

Se você quiser gerenciar diferentes níveis de visibilidade, pode usar cc() e bcc(). Aqui está o trecho de código.

Route::get('/sendWithCCandBCC', function() {
    $mainRecipients = ['main1@example.com', 'main2@example.com'];
    $ccRecipients = ['cc1@example.com', 'cc2@example.com'];
    $bccRecipients = ['secret1@example.com', 'secret2@example.com'];
    $name = "Funny Coder"; // Conteúdo dinâmico

    Mail::to($mainRecipients)
        ->cc($ccRecipients)
        ->bcc($bccRecipients)
        ->send(new MyTestEmail($name));
});

Os $mainRecipients são os destinatários principais que se verão uns aos outros. Os $ccRecipients também podem ver uns aos outros e sabem sobre os destinatários principais. Finalmente, os destinatários $bccRecipients são ocultos de todos os outros destinatários.

Enviar email com anexos

Adicionar anexos é relativamente simples, e começa com modificar a classe mailable. Vou cobrir isso aqui, mas note que existem outros métodos também.

  1. Primeiro, modifique a classe MyTestEmail para lidar com dados de anexos.
namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
Use Illuminate\Mail\Mailables\Attachment;

class MyTestEmail extends Mailable
{
    use Queueable, SerializesModels;

    public $name;
    public $attachment;

    public function __construct($name, $attachment = null)
    {
        $this->name = $name;
        $this->attachment = $attachment;
    }

    public function envelope()
    {
        return new Envelope(
            subject: 'My Test Email',
        );
    }

    public function content()
    {
        return new Content(
            view: 'mail.test-email',
            with: [
                'name' => $this->name
            ],
        );
    }

    public function attachments()
    {
        if ($this->attachment) {
            return [$this->attachment];
            return [
                Attachment::fromPath($this->attachment),
            ];
        }

        return [];
    }
}
  1. Anexe os arquivos ao instanciar a classe mailable para enviar o email.
use Illuminate\Support\Facades\Route;
use App\Mail\MyTestEmail;
use Illuminate\Support\Facades\Mail;

Route::get('/sendWithAttachment', function() {
    $name = "Funny Coder";
    $filePath = 'path/to/your/file.pdf';  // Certifique-se de que o caminho está correto

    // O envio do email é feito usando o método to na fachada de Mail
    Mail::to('testreceiver@gmail.com')->send(new MyTestEmail($name, $filePath));
});

A variável $filePath deve ser definida para o caminho do arquivo que você deseja anexar ao email. Certifique-se de que o arquivo existe e está acessível pela sua aplicação Laravel. Depois, o método attachments na classe Mailable verifica se há um anexo presente e o inclui no email.

Há algumas coisas a serem consideradas ao enviar anexos:

Enviar email HTML

Usarei a classe Mailable para enviar um email HTML. Confira o fluxo exemplificado abaixo.

  1. Começarei com um template Blade HTML que servirá como o corpo do email. Você deve criar ou modificar um arquivo Blade no diretório resources/views/mail. Por exemplo, vou chamá-lo de <strong>html-email.blade.php</strong>.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Email</title>
    <style>
        body {
            font-family: 'Arial', sans-serif;
            margin: 0;
            padding: 0;
            color: #333;
        }
        .content {
            padding: 20px;
            background-color: #f4f4f4;
        }
    </style>
</head>
<body>
    <div class="content">
        <h1>Oi, {{$name}}!</h1>
        <p>Este é um email de teste HTML enviado pelo app Laravel.</p>
        <p style="color: blue;">Enjoy coding!</p>
    </div>
</body>
</html>
  1. Vou ajustar a classe MyTestEmail para poder usar o template HTML. O ponto crítico aqui é garantir que o método content()  aponta para a view correta.
public function content()
{
    return new Content(
        view: 'mail.html-email',
        with: [
            'name' => $this->name
        ],
    );
}
  1. Quando você enviar o email usando a rota que configurou, ele renderizará o html-email.blade.php como um email HTML. Veja como a função de envio pode ser:
Route::get('/sendHtmlEmail', function() {
    $name = "Funny Coder";  // Assuming this is the dynamic content

    Mail::to('testreceiver@gmail.com')->send(new MyTestEmail($name));
});

Você pode escrever HTML padrão e CSS inline diretamente em seu arquivo view Blade. Evite usar folhas de estilo externas porque nem todos os provedores de email as suportam. Além disso, certifique-se de passar quaisquer dados dinâmicos necessários usando o atributo with no método content() de sua classe Mailable.

Pro Tip

Você pode escrever CSS na tag <style> no cabeçalho, mas, para uma melhor compatibilidade com provedores de email e renderização imediata, considere colocar os seus estilos CSS em inline. Desta forma, seu email não perderá os estilos se o cliente remover as tags <head> e <body> do HTML recebido.

Existem ferramentas e pacotes do Laravel disponíveis que podem automatizar esse processo. Por exemplo, a opção popular é o pacote fedeisas/laravel-mail-css-inliner do Laravel.

Envio de emails em fila de espera

Enviar emails em fila no Laravel é uma maneira eficiente de lidar com a entrega de emails sem atrasar o tempo de resposta de sua aplicação web. Isso é particularmente útil para aplicações que precisam enviar um grande volume de emails ou quando o tempo de entrega do email não é crítico para a ação imediata do usuário.

Aqui está como configurá-lo.

  1. Certifique-se de que seu ambiente Laravel está configurado para usar um driver de fila de espera. O Laravel suporta vários drivers, como Redis, Database, SQS, etc. Você pode definir isso no seu arquivo .env, e vou usar o Redis para este exemplo.
QUEUE_CONNECTION=redis
  1. Instale o módulo composer predis em seu projeto Laravel executando composer require predis/predis
    • Nota: se você fizer tudo o que foi dito acima e ainda ver o erro, tente adicionar isto à configuração do seu arquivo .env do Laravel: REDIS_CLIENT=predis
  2. Supondo que você já tenha uma classe mailable como MyTestEmail dos exemplos anteriores, ela precisa implementar a interface ShouldQueue para permitir o enfileiramento. Veja como modifiquei a classe MyTestEmail:
namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

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

    public $name;

    public function __construct($name)
    {
        $this->name = $name;
    }

    public function envelope()
    {
        return new Envelope(subject: 'My Test Email');
    }

    public function content()
    {
        return new Content(view: 'mail.test-email', with: ['name' => $this->name]);
    }
}
  1. Para enviar o email para a fila, você precisa modificar o controlador ou o método da rota.
use Illuminate\Support\Facades\Route;
use App\Mail\MyTestEmail;
use Illuminate\Support\Facades\Mail;

Route::get('/queueEmail', function() {
    $name = "Funny Coder";
    Mail::to('testreceiver@gmail.com')->queue(new MyTestEmail($name));
    print(“Email Sent Successfully”);
});

No envio enfileirado, a interface ShouldQueue diz ao Laravel que este mailable deve ser enviado usando o sistema de fila. Então, o facade Mail usa o método queue() em vez do método send().

Pro Tips:

Limitações e possíveis problemas do SMTP do Gmail

O Gmail, embora amplamente utilizado, vem com limitações e problemas que não são frequentemente discutidos.

Portanto, para ajudar a manter seu projeto Laravel no caminho certo e evitar possíveis problemas, agora vamos passar por essas limitações e problemas um a um.

As definições de segurança do Google também exigem configuração adequada de certos aspetos, como a autenticação de dois fatores, para evitar o bloqueio do envio de emails.

Como enviar emails no Laravel usando a API Gmail?

Para enviar via API da sua conta do Google, você precisa configurar credenciais OAuth 2.0 no Google Cloud Console. O processo leva alguns passos e vou começar com isso.

Nota importante: O Google está descontinuando as opções de “aplicativo menos seguro”, então você precisa do OAuth.

1. Crie um projeto no Google Cloud Console

2. Habilite a API Gmail

3. Crie credenciais OAuth 2.0

Esta ação tem vários subpassos, então estruturei-os separadamente para facilitar o acompanhamento.

Configuração da tela de consentimento OAuth

Crie credenciais

Baixe as credenciais 

4. Integre credenciais OAuth no Laravel

Armazene o client ID e o client secret no seu arquivo .env do Laravel:

GOOGLE_CLIENT_ID=seu-client-id-do-google
GOOGLE_CLIENT_SECRET=seu-client-secret-do-google
GOOGLE_REDIRECT_URI=http://localhost:8000/callback

Esta configuração permitirá que sua aplicação Laravel autentique com o Google e envie emails através da API Gmail. Nas próximas seções, cobrirei como usar essas credenciais para enviar emails para múltiplos destinatários, com anexos, como emails HTML e através de jobs em fila.

Enviar emails para múltiplos destinatários

Enviar para múltiplos destinatários via API Gmail, requer que você construa uma mensagem MIME que especifique todos os destinatários nos cabeçalhos To, Cc ou Bcc. Mas isso não é tudo, já que você também precisará configurar um controlador. De qualquer forma, vou listar todos os passos abaixo.

  1. O controlador mencionado lida com a lógica de envio. Vou criar um método no controlador para enviar um email para múltiplos destinatários. Mas uma nota antes disso:
    • Certifique-se de que você configurou o Google Client no controlador. Isso envolve carregar o cliente com as credenciais e definir o token de acesso, que deve ser recuperado do seu armazenamento (como um banco de dados ou sessão, dependendo do fluxo de autenticação da sua aplicação).
    • Se for a primeira vez que você obtém um token de acesso para a API Gmail, você precisará ser redirecionado para se autenticar no Google API no navegador. Nesse caso, no projeto Laravel, você precisa adicionar o manipulador para o URL de callback do autenticador do Google. E no manipulador, podemos obter o código de acesso analisando o URL.
use Google\Client;
use Google\Service\Gmail;
use Google\Service\Gmail\Message;

class EmailController extends Controller
{
    private $client;

    public function __construct()
    {
        $this->client = new Client();
        $this->client->setClientId(env('GOOGLE_CLIENT_ID'));
        $this->client->setClientSecret(env('GOOGLE_CLIENT_SECRET'));
        $this->client->setRedirectUri(env('GOOGLE_REDIRECT_URI'));
        $this->client->addScope(Gmail::GMAIL_SEND);
  1. Vou usar uma biblioteca PHP padrão para criar uma mensagem MIME. É conveniente, pois inclui todos os cabeçalhos necessários e o corpo do email.

public function handleCallback(Request $request)
    {
        if (!$request->has('code')) {
            return redirect('/')->with('error', 'Authorization code not available');
        }

        $token = $this->client->fetchAccessTokenWithAuthCode($request->query('code'));
        $this->client->setAccessToken($token);
        // Arquive o token na sessão ou base de dados do usuário para fazer chamadas autenticadas mais tarde

        $this->sendEmailToMultipleRecipients();

public function redirectToAuthUrl()
{
    $authUrl = $this->client->createAuthUrl();
        return redirect($authUrl);
}

public function sendEmailToMultipleRecipients()
{
    $to = ['recipient1@example.com', 'recipient2@example.com'];
    $subject = 'Oi da API do Gmail';
    $messageText = 'Este é um email de teste enviado para vários destinatários usando a API do Gmail através do app Laravel.';

    $message = new Message();
    
    $rawMessageString = "To: " . implode(', ', $to) . "\r\n";
    $rawMessageString .= "Subject: {$subject}\r\n";
    $rawMessageString .= "MIME-Version: 1.0\r\n";
    $rawMessageString .= "Content-Type: text/html; charset=utf-8\r\n";
    $rawMessageString .= "Content-Transfer-Encoding: quoted-printable\r\n\r\n";
    $rawMessageString .= "<p>{$messageText}</p>";

    // URL-safe base64 codifica a mensagem
    $rawMessage = base64_encode($rawMessageString);
    $rawMessage = str_replace(['+', '/', '='], ['-', '_', ''], $rawMessage); // URL-safe

    $message->setRaw($rawMessage);

    $service = new Gmail($this->client);
    try {
        $service->users_messages->send('me', $message);
        return 'Email sent successfully to multiple recipients.';
    } catch (\Exception $e) {
        return 'An error occurred: ' . $e->getMessage();
    }
}
  1. Finalmente, defina uma rota no web.php ou api.php que aciona o método de envio de email:
Route::get('/send-email', [EmailController::class, redirectToAuthUrl]);
Route::get('/callback', [EmailController::class, 'handleCallback']);

É bom saber:

Enviar emails com anexos

Enviar com anexos requer que você construa uma mensagem MIME multipart que inclua tanto a mensagem base quanto os anexos.

Confira o código abaixo:

use Google\Client;
use Google\Service\Gmail;
use Google\Service\Gmail\Message;

class EmailController extends Controller
{
    // Código de configuração existente...

    public function sendEmailWithAttachments()
    {
        $subject = 'Com Anexos';
        $to = 'recipient@example.com';
        $messageText = 'Este é um email de teste enviado para vários destinatários usando a API do Gmail através do app Laravel.';

        // Construir a mensagem MIME com anexo
        $boundary = uniqid(rand(), true);
        $subjectCharset = $charset = 'utf-8';

        $messageBody = "--{$boundary}\r\n";
        $messageBody .= "Content-Type: text/plain; charset={$charset}\r\n";
        $messageBody .= "Content-Transfer-Encoding: 7bit\r\n\r\n";
        $messageBody .= "{$messageText}\r\n";

        // Anexos
        $filePath = '/path/to/your/file.pdf'; // Example file path
        $fileName = 'example.pdf'; // Example file name
        $fileData = file_get_contents($filePath);
        $base64File = base64_encode($fileData);

        $messageBody .= "--{$boundary}\r\n";
        $messageBody .= "Content-Type: application/pdf; name={$fileName}\r\n";
        $messageBody .= "Content-Description: {$fileName}\r\n";
        $messageBody .= "Content-Disposition: attachment; filename={$fileName}; size=".filesize($filePath)."\r\n";
        $messageBody .= "Content-Transfer-Encoding: base64\r\n\r\n";
        $messageBody .= "{$base64File}\r\n";
        $messageBody .= "--{$boundary}--";

        $rawMessage = "To: {$to}\r\n";
        $rawMessage .= "Subject: =?{$subjectCharset}?B?" . base64_encode($subject) . "?=\r\n";
        $rawMessage .= "MIME-Version: 1.0\r\n";
        $rawMessage .= "Content-Type: multipart/mixed; boundary=\"{$boundary}\"\r\n\r\n";
        $rawMessage .= $messageBody;

        $rawMessage = base64_encode($rawMessage);
        $rawMessage = str_replace(['+', '/', '='], ['-', '_', ''], $rawMessage); // URL-safe

        $gmailMessage = new Message();
        $gmailMessage->setRaw($rawMessage);

        $service = new Gmail($this->client);
        try {
            $service->users_messages->send('me', $gmailMessage);
            return 'Email with attachments sent successfully.';
        } catch (\Exception $e) {
            return 'An error occurred: ' . $e->getMessage();
        }
    }
}

Aqui, a mensagem é construída com várias partes separadas por limites. Cada parte tem cabeçalhos apropriados para o tipo de conteúdo e codificação. Além disso, cada anexo é lido na memória, codificado em base64 e depois adicionado à mensagem MIME.

Os cabeçalhos para cada anexo incluem detalhes como Content-Disposition e Content-Type para identificar corretamente o arquivo no email. E por último, toda a mensagem MIME é codificada em base64 e modificada para ser URL-safe antes de ser enviada para o Gmail.

Pro Tips:

Enviar emails em HTML

Agora, estamos entrando no reino da formatação e estilização, e aqui está como configurar tudo.

  1. Continuarei usando o EmailController e adicionarei um método especificamente para enviar conteúdo HTML.
use Google\Client;
use Google\Service\Gmail;
use Google\Service\Gmail\Message;

class EmailController extends Controller
{
    // Código de configuração existente...

    public function sendHtmlEmail()
    {
        $to = 'recipient@example.com';
        $subject = 'Oi, Email em HTML!';
        $htmlContent = '<h1>Bem-vindo ao Nosso Serviço</h1><p>Este é um email <strong>HTML</strong>, enviado via a <em>API Gmail</em>.</p>';

        // Mensagem MIME em Type
        $boundary = uniqid(rand(), true);
        $subjectCharset = $charset = 'utf-8';

        $messageBody = "--{$boundary}\r\n";
        $messageBody .= "Content-Type: text/html; charset={$charset}\r\n";
        $messageBody .= "Content-Transfer-Encoding: quoted-printable\r\n\r\n";
        $messageBody .= "{$htmlContent}\r\n";
        $messageBody .= "--{$boundary}--";

        $rawMessage = "To: {$to}\r\n";
        $rawMessage .= "Subject: =?{$subjectCharset}?B?" . base64_encode($subject) . "?=\r\n";
        $rawMessage .= "MIME-Version: 1.0\r\n";
        $rawMessage .= "Content-Type: multipart/alternative; boundary=\"{$boundary}\"\r\n\r\n";
        $rawMessage .= $messageBody;

        $rawMessage = base64_encode($rawMessage);
        $rawMessage = str_replace(['+', '/', '='], ['-', '_', ''], $rawMessage); // URL-safe

        $gmailMessage = new Message();
        $gmailMessage->setRaw($rawMessage);

        $service = new Gmail($this->client);
        try {
            $service->users_messages->send('me', $gmailMessage);
            return 'HTML email sent successfully.';
        } catch (\Exception $e) {
            return 'An error occurred: ' . $e->getMessage();
        }
    }
}
  1. Adicione uma nova rota para acionar o envio HTML.
Route::get('/send-html-email', [EmailController::class, 'sendHtmlEmail']);

O conteúdo HTML é incluído como parte de uma mensagem MIME com múltiplas partes (multipart). É importante definir o Content-Type para text/html e especificar o conjunto de caracteres correto (utf-8 neste caso).

A mensagem inclui conteúdo HTML e usa um limite para separar diferentes partes do email. Isso é crucial ao enviar mensagens multipart, que podem incluir alternativas de texto simples ou anexos, além do conteúdo HTML.

Como qualquer conteúdo enviado via API Gmail, a mensagem precisa ser codificada em base64 e tornada URL-safe para garantir que seja transmitida corretamente.

Pro Tips:

Envio de emails em fila de espera

Enfileirar tarefas de envio de emails permite que sua aplicação lide com cargas pesadas e processe emails em segundo plano, melhorando os tempos de resposta para interações dos usuários.

Enfileiramento com API não é para os fracos de coração, mas eu vou te guiar.

1. Configuração do Laravel

O projeto Laravel precisa estar configurado para usar filas. Assim como no método SMTP, os drivers suportados incluem banco de dados, Redis, SQS, etc.

Para simplificar as coisas e focar no fluxo de trabalho, vou usar o driver de fila de banco de dados.

Então, primeiro, preciso editar o arquivo .env para usar o banco de dados como conector da fila.

QUEUE_CONNECTION=database

Além disso, se você ainda não configurou a tabela de jobs para a fila de banco de dados, execute esses comandos Artisan.

php artisan queue:table
php artisan migrate

2. Crie jobs de envio de email

Comece executando o comando Artisan para criar uma nova classe de job.

php artisan make:job SendGmailApiEmail

Então, edite a classe de job gerada para enviar um email usando a API Gmail.

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Google\Client;
use Google\Service\Gmail;
use Google\Service\Gmail\Message;

class SendGmailApiEmail implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $to;
    protected $subject;
    protected $messageText;

    public function __construct($to, $subject, $messageText)
    {
        $this->to = $to;
        $this->subject = $subject;
        $this->messageText = $messageText;
    }

    public function handle()
    {
        $client = new Client();
        $client->setClientId(env('GOOGLE_CLIENT_ID'));
        $client->setClientSecret(env('GOOGLE_CLIENT_SECRET'));
        $client->setRedirectUri(env('GOOGLE_REDIRECT_URI'));
        $client->setAccessToken($this->getAccessToken()); // Garanta que este método retorna um token válido

        $gmail = new Gmail($client);
        $gmailMessage = new Message();

        $rawMessageString = "To: {$this->to}\r\n";
        $rawMessageString .= "Subject: {$this->subject}\r\n";
        $rawMessageString .= "Content-Type: text/plain; charset=utf-8\r\n\r\n";
        $rawMessageString .= $this->messageText;

        // Base64 encode and make it URL-safe
        $rawMessage = base64_encode($rawMessageString);
        $rawMessage = str_replace(['+', '/', '='], ['-', '_', ''], $rawMessage);
        $gmailMessage->setRaw($rawMessage);

        $result = $gmail->users_messages->send('me', $gmailMessage);
        return $result;
    }
}

3. Expeça o job da sua aplicação

SendGmailApiEmail::dispatch($to, $subject, $messageText);

Eu aconselho a aproveitar ao máximo o sistema de jobs. Por exemplo, o sistema de fila de espera do Laravel permite que você adie a execução de uma tarefa, como sendmail, até um momento posterior, reduzindo a carga no seu servidor web e melhorando a experiência do usuário.

E não se esqueça do tratamento de erros. Configure a tentativa automática de jobs que falhem por um número de vezes definido por si.

Pro Tips:

Limitações e possíveis problemas da API Gmail

As limitações são bastante semelhantes entre a API Gmail e os serviços SMTP tradicionais em termos de limites de taxa e restrições de tamanho de mensagens.

No entanto, a API Gmail fornece um controle mais granular e um mecanismo de feedback detalhado através de seu sistema de unidades de cota, o que pode ser uma vantagem e uma complexidade, dependendo das necessidades da aplicação.

Vou cobrir as principais limitações e fazer uma leve comparação com as limitações do SMTP discutidas anteriormente.

Disclaimer: Os limites cobertos abaixo são válidos no momento da escrita, mas podem estar sujeitos a alterações com base nas políticas do Google.

A API Gmail impõe um limite de taxa per-user de 250 unidades de cota por usuário por segundo, em média, permitindo curtos bursts de envios. Este limite é calculado com base no número de unidades de cota consumidas por cada método da API.

Os serviços SMTP geralmente têm limites de taxa baseados no número de emails enviados por dia ou por hora. Mas, geralmente, não há um sistema de cota complexo baseado em tipos de operações, como na API Gmail.

Diferentes operações consomem diferentes quantidades de unidades de cota. Por exemplo, enviar um email (messages.send) custa 100 unidades, enquanto recuperar um email (messages.get) custa apenas 5 unidades.

Essa contabilidade detalhada das operações pode impactar como você gerencia as interações de email da sua aplicação, especialmente em operações (de marketing) em massa. E não há um equivalente comparável ao SMTP devido às diferenças fundamentais entre os dois métodos.

A API Gmail está sujeita a um limite de uso diário, que se aplica a todos os pedidos feitos pela sua aplicação (ex: 1000 emails por dia para o Gmail, e 10 mil para contas G Suite). É crucial monitorar seu uso para evitar exceder esses limites, dado que podem resultar em sua aplicação sendo temporariamente bloqueada de enviar emails.

Os serviços SMTP oferecem um limite fixo no número de emails por dia (ex: 500 emails por dia para 100 endereços no Gmail, e 2 mil emails por dia em contas Google Workspace).

Mas, geralmente, o serviço não será desativado ao atingir o limite. Em vez disso, seus emails excedentes podem ser colocados em fila de espera para entrega futura. Embora, você possa precisar reiniciar o envio manualmente.

A API Gmail, assim como o SMTP, terá limitações no tamanho das mensagens e anexos. Tipicamente, o Gmail permite até 25 MB por email, e qualquer coisa acima disso requer o uso de links do Google Drive.

A documentação aconselha sobre como lidar com erros de limite de forma eficaz, o que é crucial para manter uma aplicação robusta. Implementar estratégias adequadas de tratamento de erros e de backoff é essencial para gerenciar esses limites e garantir um serviço contínuo.

Os serviços SMTP também exigem tratamento de falhas de entrega, muitas vezes gerenciadas através de mensagens de retorno e de mecanismos de reenvio, que são menos transparentes do que o feedback baseado em API.

Existe uma alternativa à infraestrutura de email do Gmail?

Depois de apresentar as limitações e possíveis problemas que vêm com o uso do Gmail, é natural que a pergunta “Existe uma alternativa?” surja em sua cabeça. E a resposta é: sim, existe!

Junto com as alternativas comuns, mas um tanto básicas, como Outlook SMTP, Yahoo Mail SMTP, etc. à sua disposição, você também tem o servidor SMTP do Mailtrap Email Sending, uma solução de envio que oferece às equipas de desenvolvimento uma infraestrutura com altas taxas de entrega por design.

Mailtrap Email Sending também facilita a manutenção e resolução de problemas através de alertas (de entregabilidade e críticos) e ferramentas de monitoramento únicas, que incluem dashboards de visão geral, relatórios detalhados com estatísticas sobre provedores de email (Gmail, Google Workspace, O365, e Outlook), categorias de email e histórico alargado de emails, cujos dados incluem até 60 dias de logs de email.

Outros benefícios da solução de envio são a configuração descomplicada e segura; a possibilidade de usar uma API de email ou serviço SMTP; e um tempo de entrega de email de cerca de 1 segundo, o que permite que você alcance as caixas de entrada dos destinatários no momento certo.

Nas seções seguintes, cobrirei os métodos SMTP e API. Além disso, mostrarei como enviar emails em massa com o Mailtrap, já que permitimos fluxos transacionais e em massa no mesmo plano de envio (Transactional e Bulk streams).

SMTP

Para encontrar as credenciais SMTP do Mailtrap Email Sending, você primeiro precisa criar uma conta Mailtrap e fazer login.

Em seguida, na sua conta, navegue até Sending Domains, onde você precisa adicionar e verificar seu domínio.

O processo é descrito detalhadamente no vídeo abaixo:

Após a verificação do domínio, você irá dar à página SMTP/API Settings, onde você pode copiar as credenciais SMTP que precisa colar em seu aplicativo/projeto Laravel para começar a usar o servidor SMTP do Mailtrap.

Claro, certifique-se de escolher o Stream Transacional ou de Massa com base nas suas necessidades.

API

Como mencionado anteriormente, uma API de email também está disponível no Mailtrap Email Sending, para aqueles que desejam automatizar o processo de envio e ter melhor flexibilidade.

Para integrar essa API de email em sua aplicação Laravel, você pode usar a documentação do GitHub em Mailtrap PHP SDK, que torna a integração mais fácil e eficaz do que escrever o código de integração manualmente para seu projeto.

Estes são os passos para usar o SDK:

composer require railsware/mailtrap-php symfony/http-client nyholm/psr7
<?php

return [
    /*
    |--------------------------------------------------------------------------
    | Configurações do Mailer
    |--------------------------------------------------------------------------
    */
    'mailers' => [
        // início do transporte mailtrap
        'mailtrap' => [
            'transport' => 'mailtrap'
        ],
        // fim do transporte mailtrap
    ]
];
MAIL_MAILER="mailtrap"
MAILTRAP_HOST="send.api.mailtrap.io"
MAILTRAP_API_KEY="SUA_API_KEY_AQUI"
MAILTRAP_INBOX_ID=1000001
php artisan make:mail WelcomeMail
<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Attachment;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Mail\Mailables\Headers;
use Illuminate\Queue\SerializesModels;
use Mailtrap\EmailHeader\CategoryHeader;
use Mailtrap\EmailHeader\CustomVariableHeader;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Header\UnstructuredHeader;

class WelcomeMail extends Mailable
{
    use Queueable, SerializesModels;

    private string $name;

    /**
     * Criar uma nova instância de mensagem.
     */
    public function __construct(string $name)
    {
        $this->name = $name;
    }

    /**
     * Obter o envelope da mensagem.
     */
    public function envelope(): Envelope
    {
        return new Envelope(
            from: new Address('jeffrey@example.com', 'Jeffrey Way'),
            replyTo: [
                new Address('taylor@example.com', 'Taylor Otwell'),
            ],
            subject: 'Email de boas-vindas',
            using: [
                function (Email $email) {
                    // Cabeçalhos
                    $email->getHeaders()
                        ->addTextHeader('X-Message-Source', 'example.com')
                        ->add(new UnstructuredHeader('X-Mailer', 'Mailtrap PHP Client'));

                    // Variáveis Personalizadas
                    $email->getHeaders()
                        ->add(new CustomVariableHeader('user_id', '45982'))
                        ->add(new CustomVariableHeader('batch_id', 'PSJ-12'));

                    // Categoria (deve ser apenas uma)
                    $email->getHeaders()
                        ->add(new CategoryHeader('Integration Test'));
                },
            ]
        );
    }

    /**
     * Obter a definição do conteúdo da mensagem.
     */
    public function content(): Content
    {
        return new Content(
            view: 'mail.welcome-email',
            with: ['name' => $this->name],
        );
    }

    /**
     * Obter os anexos para a mensagem.
     *
     * @return array<int, \Illuminate\Mail\Mailables\Attachment>
     */
    public function attachments(): array
    {
        return [
            Attachment::fromPath('https://mailtrap.io/wp-content/uploads/2021/04/mailtrap-new-logo.svg')
                ->as('logo.svg')
                ->withMime('image/svg+xml'),
        ];
    }

    /**
     * Obter os cabeçalhos da mensagem.
     */
    public function headers(): Headers
    {
        return new Headers(
            'custom-message-id@example.com',
            ['previous-message@example.com'],
            [
                'X-Custom-Header' => 'Custom Value',
            ],
        );
    }
}
Oi, {{$name}} e bem-vindo 😉

<br>
Funny Coder
<?php

use App\Mail\WelcomeMail;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Mail;

/*
|--------------------------------------------------------------------------
| Rotas de Console
|--------------------------------------------------------------------------
|
*/

Artisan::command('send-welcome-mail', function () {
    Mail::to('testreceiver@gmail.com')->send(new WelcomeMail("Jon"));
    // Além disso, você pode usar um mailer específico se seu mailer padrão não for "mailtrap", mas você quiser usá-lo para emails de boas-vindas
    // Mail::mailer('mailtrap')->to('testreceiver@gmail.com')->send(new WelcomeMail("Jon"));
})->purpose('Send welcome mail');
php artisan send-welcome-mail

Agora, deixe-me mostrar como integrar o Bulk stream para enviar emails segmentados de marketing e newsletters, por exemplo.

Bulk

Para ajustar sua configuração Laravel e enviar emails em massa usando a API Bulk Email do Mailtrap, você precisará alterar a configuração do host. O método é muito próximo ao que foi abordado acima, e eu guiarei você em cada passo.

1. Atualize a configuração .env

Você precisa mudar o MAILTRAP_HOST para apontar para o host da API Bulk Email.

MAILTRAP_HOST="bulk.api.mailtrap.io"

2. Atualize a classe Mailable

Se a API de bulk email usa uma estrutura semelhante para enviar emails, pode ser que nenhuma mudança seja necessária na sua classe mailable WelcomeMail. No entanto, você deve garantir que quaisquer cabeçalhos ou parâmetros específicos exigidos pela API bulk estejam incluídos.

Verifique a documentação da API Mailtrap para quaisquer cabeçalhos ou parâmetros adicionais necessários especificamente para operações em massa. Se esses parâmetros forem necessários, você modificaria o método envelope() na sua classe WelcomeMail para incluir esses cabeçalhos.

Por exemplo, vou supor que existam cabeçalhos específicos para lidar melhor com os pedidos em massa. Portanto, adicionarei o seguinte dentro da função lambda using no método envelope.

function (Email $email) {
    // Cabeçalhos existentes
    $email->getHeaders()
        ->addTextHeader('X-Message-Source', 'example.com')
        ->add(new UnstructuredHeader('X-Mailer', 'Mailtrap PHP Client'));

    // Cabeçalhos específicos para bulk
    $email->getHeaders()
        ->addTextHeader('X-Bulk-Email', 'true')  // Hypothetical header for bulk processing
        ->addTextHeader('X-Campaign', 'SummerSaleCampaign');
}

3. Verifique a configuração do mail

Certifique-se de que seu config/mail.php está configurado para usar o mailtrap como o mailer padrão ou especificá-lo ao enviar o email.

// Use o mailer Mailtrap padrão
Mail::to('testreceiver@gmail.com')->send(new WelcomeMail("Jon"));

// Ou especifique o mailer Mailtrap explicitamente se não estiver configurado como padrão
Mail::mailer('mailtrap')->to('testreceiver@gmail.com')->send(new WelcomeMail("Jon"));

4. Teste a configuração

Execute o comando Artisan fornecido para enviar um email através do stream de massa.

php artisan send-welcome-mail

E é isso! A API de Email Sending do Mailtrap agora está integrada ao seu aplicativo Laravel.

Testar emails antes de enviar: por quê e como?

Para alguns, o processo de configurar a funcionalidade de envio de emails em seu aplicativo Laravel acaba quando o código de envio de emails foi escrito. Para outros, que seguem a rota mais cautelosa, esse processo também envolve adicionar o teste desses mesmos emails como uma etapa crucial antes do envio.

Através do teste de emails, você pode verificar como seus emails são renderizados pelos navegadores da web e quão responsivos serão. Você também pode verificar a pontuação de spam, procurar seu domínio/IP em listas de permissões, e muito mais – se tiver a ferramenta de teste de email adequada.

Uma ferramenta de teste de email que vem com todos os recursos mencionados e outros quantos, como a criação de caixas de entrada virtuais para diferentes projetos e estágios de projeto, ou visão detalhada das informações técnicas (cabeçalhos de email e dados de transações SMTP), é o Mailtrap Email Testing.

O Mailtrap Email Testing é uma solução que permite inspecionar e depurar emails em ambientes de staging, desenvolvimento e QA antes de enviá-los para os destinatários. Dessa forma, cria um ambiente seguro para testes de email que não apresenta risco de enviar spam para os destinatários no processo.

A solução vem com integrações prontas para uso em mais de 20 linguagens de programação, gerenciamento de usuários e SSO.

E como é começar a usar o Mailtrap Email Testing? Isso leva apenas cinco minutos e consiste nos seguintes passos:

Nota: Ao testar, você não precisa usar endereços de email reais de remetente ou destinatário, pois os emails de teste estão sendo enviados para caixas de entrada virtuais, e a verificação de email não está sendo feita.

O Mailtrap Email Testing também oferece credenciais SMTP para cada uma de suas caixas de entrada virtuais, que podem ser acessadas clicando em “Show Credentials” na página “SMTP Settings”. Portanto, se você preferir usar credenciais em vez de snippets de código para integrar esta solução de teste, basta copiar e colar essas credenciais em seu script de envio de email, configurações do MTA, configurações do provedor de email ou qualquer outro sistema que as suporte.

API

Para integrar a API de teste de email Mailtrap, você precisa criar uma classe de serviço para encapsular a funcionalidade da API e adaptar o cliente HTTP do Laravel. Aqui estão os passos:

  1. Use Artisan para criar uma nova classe de serviço.
php artisan make:service MailtrapService

Nota: Se make:service não estiver disponível, basta criar um arquivo PHP no diretório app/Services.

  1. Edite o novo MailtrapService.php para incluir a funcionalidade da API Mailtrap.
namespace App\Services;

use Illuminate\Support\Facades\Http;

class MailtrapService
{
    protected $baseUrl = 'https://sandbox.api.mailtrap.io/api/send';
    protected $apiKey = '123'; // É melhor mover isso para seu arquivo .env

    public function sendEmail(array $data)
    {
        $response = Http::withHeaders([
            'Accept' => 'application/json',
            'Api-Token' => $this->apiKey,
            'Content-Type' => 'application/json',
        ])->post("{$this->baseUrl}/inbox_id", $data);

        if ($response->failed()) {
            return 'Error: ' . $response->body();
        }

        return $response->body();
    }
}
  1. Adicione sua chave API e possivelmente a URL base ao seu arquivo .env para melhor segurança e flexibilidade.
MAILTRAP_API_KEY=SUA_API_KEY_DO_MT
MAILTRAP_BASE_URL=https://sandbox.api.mailtrap.io/api/send
  1. Atualize a classe de serviço para usar os valores do .env.
protected $baseUrl;
protected $apiKey;

public function __construct()
{
    $this->baseUrl = env('MAILTRAP_BASE_URL', 'https://sandbox.api.mailtrap.io/api/send');
    $this->apiKey = env('MAILTRAP_API_KEY');
}
  1. Use o serviço em um controlador ou comando para testar emails.
use App\Services\MailtrapService;

class EmailController extends Controller
{
    protected $mailtrap;

    public function __construct(MailtrapService $mailtrap)
    {
        $this->mailtrap = $mailtrap;
    }

    public function sendTestEmail()
    {
        $data = [
            // Sua estrutura de dados de email aqui
        ];

        $result = $this->mailtrap->sendEmail($data);
        return response()->json(['message' => $result]);
    }
}
  1. Defina uma rota para acionar o método de teste de email.
Route::get('/test-mailtrap', [EmailController::class, 'sendTestEmail']);

Concluindo

O Laravel e o Gmail têm duas coisas principais em comum: uso difundido e muitos recursos educacionais. Espero que este artigo agora sirva como seu principal recurso para aprender como usar tanto o Laravel para enviar email através do Gmail, como a nossa ótima alternativa Mailtrap Email Sending.

Para expandir ainda mais seu conhecimento sobre como usar este framework para envio de emails, teste, verificação e mais, não se esqueça de conferir alguns de nossos outros artigos:

Sair da versão mobile