Alura > Cursos de Programação > Cursos de PHP > Conteúdos de PHP > Primeiras aulas do curso Laravel: e-mails, eventos assíncronos, uploads e testes

Laravel: e-mails, eventos assíncronos, uploads e testes

Envio de e-mail - Apresentação

Boas-vindas à Alura, sou o Vinícius Dias e vou guiar vocês nesse treinamento de Laravel, no treinamento anterior já finalizamos com uma aplicação bem interessante e funcional, mas vamos incrementá-la e para isso, vamos aprender novas funcionalidades do Laravel.

A primeira coisa que vamos aprender é sobre como enviar e-mails utilizando o Laravel, seja criando o próprio layout ou usando facilidades do Laravel para criar um layout bonito de e-mail. Falando sobre e-mails, vamos analisar uma ferramenta, que não está diretamente ligada ao Laravel, mas que é bem interessante para testarmos e-mails e ter esse tipo de funcionalidade.

Com o e-mail criado vamos clicar no botão "Ver série" e outras funcionalidades, como mostrar a página da série com as temporadas, vamos enviar e-mails para vários usuários, no final do treinamento estou apenas com dois, mas conforme o treinamento for avançando teremos vários usuários e isso vai gerar um grande problema de performance.

Quando chegarmos nesse problema vamos aprender um conceito fila de processamento, então, no momento de enviar o e-mail vamos adicionar em uma fila para ser processado depois. Em seguida, vamos aprender sobre eventos e event listener (ou em português, ouvinte de eventos) do Laravel. Então, vamos entender como realizar esse processamento, fazer log, tudo isso de forma assíncrona.

Temos na aplicação agora uma imagem, assim, também vamos aprender sobre como manipular upload de arquivos utilizando Laravel e suas facilidades. No final, para garantir que tudo isso funcione, vamos comentar bem rápido sobre alguns conceitos de testes. Serei bem incisivo no ponto que esse treinamento não é de testes, visto que temos outros específicos para isso.

Mas, nesse treinamento vamos aprender algumas facilidades que o Laravel traz nesse assunto de testes automatizados. Enfim, tem bastante conteúdo, espero que você aproveite bastante e se em algum ponto ficar com alguma dúvida, não hesite em abrir um tópico no fórum, eu tento responder pessoalmente sempre que possível, se não, a comunidade está disponível para te ajudar. Talvez você consiga ajuda pelo nosso servidor no Discord, que te convido também a fazer parte.

Enfim, mais uma vez sejam muito bem-vindos e bem-vindas, espero que você goste bastante desse treinamento e te espero no próximo vídeo para já começar o treinamento.

Envio de e-mail - Criando um e-mail

Bem-vindo de volta. Antes de efetivamente começarmos a escrever o código nesse treinamento, vamos recapitular alguns conceitos de como você pode começar a partir desse treinamento.

Então, se já realizou os cursos anteriores de Laravel, sem problemas e é só continuar, mas, se está começando agora é preciso baixar esse projeto inicial que vamos disponibilizar e na pasta desse projeto, em um terminal, você vai rodar o comando composer install para ser baixado todas as dependências, instale o Laravel e tudo que ele depende e em seguida, executar o comando php artisan migrate, que vai criar o banco de dados com a estrutura que temos.

Tendo feito isso, já temos a estrutura, só digitar o comando php artisan serve e temos o servidor rodando, então, vamos conseguir acessar o "localhost:8000/series" e temos o sistema rodando. Agora, você vai registrar algum usuário, eu já tenho um usuário registrado que é o e-mail "email@example.com", senha "123456" e vou selecionar o botão "Log in" para realizar o login.

Fazendo o login o que queremos é que ao adicionar uma nova série, selecionando o botão "Adicionar" na parte superior esquerda da tela, queremos criar e enviar um e-mail informando que uma nova série foi cadastrada no sistema.

Vamos começar pelo básico, interrompendo o servidor do artisan visto que queremos criar algo que seja enviado por e-mail, o que o Laravel chama de mailable (em português, enviável), então, podemos usar o artisan para criar essa classe que vai representar algo que é enviável, vou chamar de mensagem ou e-mail. Assim, vamos criar o e-mail, podemos usar o make:mail que vamos chamar de "SeriesCreated", informando que uma série foi criada.

php artisan make:mail SeriesCreated

Então, vai ser criado esse e-mail e no terminal vai ser exibido a mensagem "Mail created successfully", então no projeto vamos em "app > Mail > SeriesCreated.php" e dentro de SeriesCreated temos uma classe qualquer, só que além de estender o Mailable ela também possui algumas straights que o Laravel nos fornece, no caso Queueable e SerializesModels, inicialmente não serão necessárias, mas mais para frente analisamos no que elas podem nos ajudar.

A classe SeriesCreated estende um Mailable, isso significa que ela é enviável e possui alguns métodos como o view. O que acontece com os mailablesm, os e-mails que criamos? Eles possuem um construtor() para recebermos algum parâmetro que seja necessário, vamos utilizar e um build() que basicamente constrói o e-mail, define qual o template, se é preciso passar alguma coisa, se tiver alguma regra para criar esse e-mail podemos inserir nesse build(), mas isso normalmente não é necessário.

Então, esse método view, return $this->view('view.name');, vai carregar um HTML e transformá-lo no e-mail que queremos enviar. Assim, vamos criar esse HTML em "resources > views" e em views vamos criar uma nova pasta de e-mail selecionando o botão direito do mouse nas opções "New > PHP File" e no campo "File name" inserir "mail/serires-created.blade.php" que vai ser o mesmo nome da classe.

Em series-created.blade.php temos o componente de e-mail e vamos simplesmente chamá-lo "E-mail", por enquanto. Agora em SeriesCreated, podemos ir em view.name e chamar a view mail visto que está dentro da pasta mail e .series-create.

public function build()
    {
        return $this->markdown('mail.series-created');
    }

Pronto, já temos o e-mail configurado e em series-created.blade.php podemos informar "Uma nova série foi criada." e poderíamos adicionar o link pedindo para conferir ou algo nesse sentido.

Série criada e queremos visualizar esse e-mail antes de enviar para garantir que está tudo certo, e como o e-mail por trás possui um HTML ou um texto, pelo menos, conseguimos visualizar no navegador. Então, vamos criar uma rota só para teste que vamos remover mais adiante em "routes > web.php" só para visualizar este e-mail.

Em web.php vamos inserir um Route:get('/email') e teremos um function() que vai retornar a classe de SeriesCreated(), visto que quando retornamos um e-mail o Laravel já sabe que ele deve fazer é pegar a view, simples assim.

Route::get('/email', function () {
    return new \App\Mail\SeriesCreated();
});

Agora vamos subir o servidor do artisan novamente no terminal, então, php artisan serve --host=0.0.0.0. Após finalizar, vamos à aplicação e acessar a URL "http://localhost:800/email" e será exibida a mensagem "Uma nova série foi criada".

A princípio está tudo certo e dentro de series-created.blade.php podemos escrever o HTML do e-mail, mas, escrever e-mails não é uma tarefa tão simples, não é toda propriedade CSS que é aceita, às vezes um cliente de e-mail aceita uma propriedade que outro não, assim, pode ficar bonito no hotmail, mas não no gmail. Então, o Laravel nos traz alguns componentes já personalizados e testados para envio de e-mail.

Se você quiser utilizar isso, vamos dar uma analisada rápida na Documentação do Laravel , nessa página já vamos entender sobre configurações, mas, no momento vamos analisar como usar markdown para as views dos e-mails em "Markdown Mailables".

Mas por qual motivo usar markdown? Quando criamos um e-mail usando markdown, além da facilidade de escrever no formato markdown que é um pouco mais simples que o HTML, e caso não conheça todas as páginas do GitHub são escritas usando o formato markdown, mas enfim, além dessa facilidade conseguimos também usar alguns componentes do próprio Laravel, como o @component('mail::message'), e vamos fazer exatamente isso.

Em SeriesCreated ao invés de usar o método view no e-mail vamos inserir o markdown, ou seja, vai identificar que esse arquivo de view que estamos criando não vai ser um HTML qualquer, vai estar no formato de markdown.

SeriesCreated:

 public function build()
    {
        return $this->markdown('mail.series-created');
    }

Então, em series-created.blade.php podemos usar o componente de mensagem do Laravel, @component('mail::message') e após a mensagem "Uma nova série foi criada." temos um @endcomponent.

series-created.blade:

@component('mail::message')

    Uma nova série foi criada.
@endcomponent

Com isso, se atualizarmos na aplicação com a URL "http://localhost:800/email" repare que está bem mais interessante, a mensagem foi para o meio da tela e claro, possui alguns detalhes do Laravel como a mensagem "2022 Laravel. All rights reserved." que poderíamos nos preocupar com isso, mas para o nosso cenário isso é perfeito, visto que ele já cria um layout interessante para o e-mail e que vai funcionar para todos os clientes, já foi melhor testado.

Em series-created.blade.php podemos usar markdown, então, por exemplo, se quisermos criar uma lista vamos incluir os itens 1 e 2.

series-created.blade:

@component('mail::message')

# Uma nova série foi criada.

- Item 1
- Item 2

@endcomponent

Voltando para a aplicação e atualizando no canto superior esquerdo, vai ser exibida a mensagem com o item 1 e 2 listados na página, o que possui uma cerquilha (#) é o título e o restante é uma lista, isso já é renderizado corretamente.

Ainda em series-created.blade podemos usar um botão para enviar para a série, então, vamos copiar da documentação na parte "Writing Markdown Messages" a parte do botão e colar no arquivo. Mas antes, vamos colocar como título desse e-mail o nome da série criada, `# {{ $nomeSerie }} criada e informar que a série com tantas temporadas e episódios foi criada, acesse aqui e agora sim, adicionar o botão.

No botão vamos fazer algumas alterações removendo "View Order" e incluindo "Ver série" e no lugar de $url incluir a rota para seasons.index e passar o ID da série.

series-created.blade:

@component('mail::message')

# {{ $nomeSerie }} criada

A série {{ $nomeSerie }} com {{ $qtdTemporadas }} temporadas e {{ $episodiosPorTemporada }} episódios por temporada foi criada.

Acesse aqui: 

@component('mail::button', ['url' => route('seasons.index', $idSerie)])
    Ver série
@endcomponent

@endcomponent

Estamos usando várias variáveis, mas onde elas serão encontradas? Podemos utilizar tanto quando fazemos com uma view, passando os dados em um colchetes em SeriesCreated no return do function build() ou podemos ter as propriedades da classe de e-mail públicas, tudo que for público a view vai acessar, se for privado não.

Então podemos, ou ter as propriedades públicas ou passar no return do function build(). Assim, vamos receber isso tudo e transformar em propriedades públicas, então, em public function __construct () teremos uma string que vai ser o nome da série, o ID da série,a quantidade de temporadas e episódios.

public function __construct(
        public string $nomeSerie,
        public int $idSerie,
        public int $qtdTemporadas,
        public int $episodiosPorTemporada,
    )

Temos todas essas propriedades públicas e agora já conseguimos acessá-las em series-created.blade, então, web.php na rota vamos passar esses parâmetros, série de teste, com o ID "1" que é inválido, mas tudo bem, com "5" temporadas e "10" episódios por temporada.

Route::get('/email', function () {
    return new \App\Mail\SeriesCreated(
        'Série de teste',
        1,
        5,
        10,
    );
});

Teoricamente essas alterações funcionam e o e-mail deve ser exibido corretamente, então na URL vamos inserir "http://localhost:800/email" e vai ser exibido a mensagem "Série de teste criada: A série de teste com 5 temporadas e 10 episódios por temporada foi criada. Acesse aqui" e em seguida um botão "Ver série" que ao clicar somos redirecionados para a URL "http://localhost:800/series/1/seasons" que é correta, mas, que não existe essa série com o ID "1".

Conseguimos criar, vamos alterar o ID "1" para "19" apenas para testarmos o botão "Ver série", agora sim, está funcionando e somos redirecionados para as temporadas da série.

Então, repare que já temos o e-mail criado e é relativamente simples criar um e-mail. Mas, por enquanto, estamos apenas visualizando o HTML, acessando uma rota e retornando esse e-mail. Como podemos fazer para efetivamente enviar esse e-mail? Vamos começar a aprender sobre isso no próximo vídeo.

Envio de e-mail - Enviando e-mails

Bem-vindo de volta. Vamos fazer uma revisão bem rápida, para enviar um e-mail em Laravel, primeiro, criamos a classe que representa a mensagem que vamos enviar. Então, criamos a classe SeriesCreated(), que significa série criada, visto que esse e-mail é para informar que uma série foi criada.

Um e-mail possui dois métodos padrão, um construtor() e o build(), este é responsável por construir o e-mail e, para isso, vamos usar alguma view, que pode ser em HTML ou em markdown, que nos traz algumas facilidades, dentre elas é usar alguns componentes do Laravel como em series-created.blade para criar esses e-mails. Não é preciso usar ela, é opcional.

Além disso, vimos também que para acessar informações na view de e-mail, temos duas opções. A primeira seria em SeriesCreated, no return() passar como qualquer outra view, por exemplo, 'nomeSerie' => '' e passar o nome da série que recebemos.

Exemplo da primeira opção:

public function build()
    {
        return $this->markdown('mail.series-created'[
                'nomeSerie' => ''
                ]);
    }

A segunda opção é fazer como já estamos fazendo, que é salvar em uma propriedade pública, se você não conhece essa sintaxe vou deixar um "Para saber mais" sobre promoção de propriedades no construtor, isso não é do Laravel e sim do PHP.

Agora que já temos o e-mail, vamos fazer o envio. Vamos no controller de séries, quando uma série é criada, em "app > http > controllers > SeriesController" no método store() que já estamos chamando o repositório para adicionar uma série, $serie = $this->repository->add($request);, e agora queremos enviar o e-mail e para isso vamos usar novamente uma facade do Laravel, ou seja, uma das classes facilitadoras para um sistema mais complexo, no caso, de envio de e-mails.

Existe uma classe chamada Mail, repare no namespace dela nas importações na parte superior do código, "Illuminate\Support\Facades\Mail". Nessa classe Mail chamamos o método to() e informar para quem queremos enviar o e-mail, podemos enviar agora para o usuário logado, temos algumas formas de pegar o usuário logado, que já vimos, por exemplo, auth::user(): Mail::to(Auth::user()).

Mas podemos pegar também direto do request, então, podemos pegar o user(), só para mostrar uma forma diferente de pegar o usuário logado. Com isso, podemos enviar, ou seja, fazer um send() de algum e-mail, que vamos criar. Então, $email= new SeriesCreated() que precisa de alguns parâmetros, primeiro, na série o nome e id e no request a quantidade de temporadas e episódios.

SeriesController:

//código omitido

    public function store(SeriesFormRequest $request)
    {
        $serie = $this->repository->add($request);

                $email = new SeriesCreated(
                        $serie->nome,
                        $serie->id,
                        $request->seasonsQty,
                        $request->episodesPerSeason,
                );
        Mail::to($request->user())->send($email);

                 return to_route('series.index')
            ->with('mensagem.sucesso', "Série '{$serie->nome}' adicionada com sucesso");
    }
//código omitido

Estamos criando o e-mail com as informações da série que acabamos de criar e com as informações recebidas da requisição, que é quantidade de episódios e temporadas.

Teoricamente, é isso que precisamos para enviar uma série por e-mail, só que quando vamos enviar um e-mail usando PHP, precisamos enviar de alguma forma. Pode ser utilizando a função mail() do PHP, o que ela faz é chamar um binário que temos que ter no sistema operacional, chamado sendmail, mas precisaríamos configurar no próprio servidor e computador.

Então, ao invés de usar o sendmail é muito comum chamar algum servidor SMTP (Simple Mail Transfer Protocol ou, em português Protocolo de Transferência de Correio Simples) que é o protocolo de envio de e-mails, assim, podemos usar a nossa conta do gmail para enviar esse e-mail. Basta usar as credenciais SMTP que você consegue buscar no gmail e configurar.

Podemos configurar em "config > mail.php" que é onde temos as configurações e repare na linha 16 que podemos usar vários mailers, temos outras possibilidades além do SMTP, mas vai ser o que vamos utilizar. Na linha 31 e 32 já nos mostra algumas opções, por exemplo, ses que é um serviço da AWS, o sendmail, entre outros.

Mas vamos usar o SMTP e podemos passar as informações, o servidor SMTP, qual a porta, se vai usar criptografia, usuário e senha, enfim, podemos passar todas essas informações diretamente em SMTP ou, o que é mais correto, é usar as variáveis de ambiente.

Em ambiente local de desenvolvimento usamos o arquivo .env, na linha 29 temos algumas informações de SMTP e repare que já tem configurado um servidor chamado mailtrap, ele já está configurado para isso, foram alguns testes que fiz, então, vamos analisar o que é esse mailtrap.

No navegador, vamos digitar o site do Mailtrap neste link e criar uma conta caso ainda não tenha, logado temos uma caixa de e-mail, em "My Inbox". Esse mailtrap é um serviço que intercepta todos os e-mails que enviamos, então, se configurarmos o servidor como sendo mailtrap e tentar enviar para o e-mail pessoal, esse e-mail não vai ser enviado e só vão cair nessa caixa.

Então, isso é fenomenal para o ambiente de desenvolvimento e ainda já vemos como configurar o Laravel em um cenário real, visto que em ambiente de produção também utilizaríamos um SMPT para um servidor mais complexo ou um gmail, por exemplo.

Em "integrations" que são as integrações ele já nos mostra como fazer com o Laravel se trocarmos o "cURL" por "Laravel 7+", basta copiarmos essas configurações todo que ele já criou um usuário e senha, o seu usuário e senha que você vai criar vai ser diferente desse.

Você até pode copiar o meu usuário e senha, mas isso pode gerar limitações visto que vou enviar alguns e-mails aqui e isso vai sair da sua conta, como é de graça, crie uma conta no mailtrap e copia essas configurações.

Exemplo de configuração de usuário do mailtrap:

MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=a61b0f38eef3a
MAIL_PASSWORD=5d7013d8c3e134
MAIL_ENCRYPTION=tls

Vamos colar essas configurações em qualquer lugar do arquivo .env e repare que ele já possui detalhes como encryption, usuário e senha, a porta, o host, só que ele não tem as informações das linhas 26 e 27, MAIL_FROM_ADDRESS e MAIL_FROM_NAME que já vem por padrão no Laravel.

Nós podemos até remover, mas acabei deixando o padrão, que é de quem esse e-mail vai ser enviado, então basicamente no campo "from" podemos ter um valor padrão, podemos informar na hora de enviar o e-mail, vamos para SeriesController e na linha 44 adicionar no e-mail poderia dizer from alguém, ->send($email->from()).

Mas se quisermos ter um padrão, em .env que inserimos que todos os e-mails enviados pelo sistema são de "contato@example.com", então, MAIL_FROM_ADDRESS="contato@example.com" vai ser utilizado e o nome do remetente, de quem enviou que podemos colocar "Sistema de séries", mas ele está pegando o APP_NAME lá na linha 1, que no caso está sendo chamado de Laravel, vamos alterar para "Sistema de séries".

MAIL_FROM_ADDRESS="contato@example.com"
MAIL_FROM_NAME="APP_NAME"

A princípio já temos toda a configuração e o código também está pronto, teoricamente está tudo pronto para acessarmos na aplicação a criação de séries. Mas, antes vamos subir o artisan no terminal usando o comando php artisan serve --host-0.0.0.0, agora sim vamos para a aplicação e selecionar o botão "Entrar" no canto superior direito da tela para fazer o login com o e-mail "email@example", senha "123456" e em seguida clicar no botão "Log in".

Pronto, estamos logados e vamos acessar a página de séries, "/series", e adicionar uma nova séries clicando no botão "Adicionar", no campo "nome" vamos inserir "Nova", que possui um episódio e uma temporada e novamente selecionar o botão "Adicionar". Repare que a adição está demorando um tempo a mais, visto que temos que enviar esse e-mail e quando vamos no mailtrap, a série foi enviada.

O assunto do e-mail foi enviado como "Series Created", pois é o nome da classe, mas poderíamos alterar esse nome. Analisando o e-mail repare que veio de "Sistemas de séries" que é exatamente para quem queríamos enviar e para, no meu caso, Vinícius Dias que é o usuário cadastrado com esse e-mail.

Embora o e-mail "email@example" não seja válido, como estamos usando o mailtrap conseguimos verificar e está tudo certo. Outra coisa interessante é que o mailtrap permite visualizar como isso ficaria em uma tela de celular, tablet e notebook, basta selecionar o ícone na parte superior do e-mail, mostra o HTML que foi gerado em "HTML Source", exibe em "HTML Check" quais os problemas que teríamos com outros clientes e isso ajuda bastante.

Vamos melhorar adicionando um assunto no e-mail, podemos fazer isso de algumas formas e uma delas é em SeriesController e fazer um $email->subject = 'Nova série criada'; ou direto no SeriesCreated() no construtor e adicionar $this->subject = "Série $nomeSerie criada";.

 {
        $this->subject = "Série $nomeSerie criada";
 }

Agora esse é o assunto do e-mail, então na aplicação vamos criar novamente uma nova série chamada "Outra" com duas temporadas e cinco episódios e selecionando o botão "Adicionar" que está demorando um pouco mais devido à solicitação de requisição para o servidor e pronto, foi exibida a mensagem "Série 'Outra' adicionada com sucesso".

Vamos voltar para o mailtrap e perceba que no e-mail está escrito "Série Outra criada" no assunto, então temos o e-mail sendo enviado. Agora quero deixar um desafio para você que já volto para corrigir, que é ao invés de enviar somente para o usuário logado, cadastre sete usuários e no momento de enviar esse e-mail em SeriesController envie para todos os sete usuários.

Então, no próximo vídeo vamos executar exatamente isso, mas, faça sem a minha ajuda para analisar se vai gerar algum problema e se você vai conseguir desenvolver e no próximo vídeo vamos implementar isso juntos.

Sobre o curso Laravel: e-mails, eventos assíncronos, uploads e testes

O curso Laravel: e-mails, eventos assíncronos, uploads e testes possui 142 minutos de vídeos, em um total de 52 atividades. Gostou? Conheça nossos outros cursos de PHP em Programação, ou leia nossos artigos de Programação.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda PHP acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas