How to specify from on the fly in laravel 8 mail::send - laravel

From the contact form on my website, I send an confirmation email to the visitor and an email to the website admin. Both email are coming to the email address defined in .env.
How can I change the from field for the email sent to admin?
My current code where the second Mail:: gives an error.
// send html email to user
Mail::to(request('email'))
->send(new ContactWebsite($emailFields));
// send html email to admin
Mail::to("newemail#address")
->from(request('email'))
->send(new ContactWebsite($emailFields));
Daniel solution is good but how do I implement it in my case?
In the Contact Contoller store function, I put the fromAddress in the $emailFields object:
$emailFields = (object) [
'fromName' => $fullnameUser,
'fromEmail' => request('email'),
'fromAddress' => $fullnameUser.' <'.request('email').'>',
'subject' => '...',
'body' => request('body')
];
Then in the Mailable:
public function __construct($emailFields) {
$this->emailFields = $emailFields;
$this->fromAddress = $emailAddress['fromAddress'];
}
public function build() {
return $this->markdown('emails.contact-confirm-user');
}
Is the syntax correct in the __construct function?
And how do I pass the $this->fromAddress in the build function?

You are supposed to define the ->from() part in your Mailable class's build() function (in your case ContactWebsite) either as part of the $emailFields, or as a second parameter. Then you just use it in the build function:
class ContactWebsite extends Mailable
{
use Queueable, SerializesModels;
private $fromAddress = 'default#value.com';
public function __construct($emailFields, $fromAddress = null)
{
if ($fromAddress) {
$this->fromAddress = $fromAddress;
}
}
public function build()
{
return $this->from($this->fromAddress)
// Whatever you want here
->send()
}
}

Related

Laravel error when sending email notification

Laravel Version: 8.78.1
PHP Version: 8.0.10
I've created a custom command to run on a schedule and email a notification.
My Command class handle method:
public function handle()
{
$sql = "SELECT * FROM Licences WHERE (Expired = 1)";
$list = DB::select($sql);
return (new NotifyExpiredLicences($list))->toMail('me#gmail.com');
}
My notification method:
public function toMail($notifiable)
{
return (new MailMessage)
->subject('Clients with Expired Licences')
->markdown('vendor/notifications/expiredlicences',
['clients' => $this->list, 'toname' => 'Me']);
}
Whenever I test this by running it manually with php artisan email:expired-licences I get the following error Object of class Illuminate\Notifications\Messages\MailMessage could not be converted to int from my command class in the handle method.
However, the preview of my email works fine & displays as expected:
Route::get('/notification', function () {
return (new SendExpiredLicences())->handle();
});
If I remove the return statement from my handle() method, then although I get no errors, neither in my console or in storage\logs, also the preview stops working.
At this point I'm sure I've missed something important from the way this is supposed to be done, but after going through the Laravel docs and looking at online tutorials/examples, I've no idea what.
I've got everything working - though not entirely sure it's the "Laravel way".
If anyone's got suggestions for improving it - add a comment or new answer and I'll try it out.
Console\Kernel.php:
protected function schedule(Schedule $schedule)
{
$schedule->command('email:expired-licences')
->weekdays()
->at('08:30');
}
App\Console\Commands\SendExpiredLicences.php:
class SendExpiredLicences extends Command
{
protected $signature = 'email:expired-licences';
protected $description = 'Email a list of expired licences to Admin';
private $mail;
public function _construct()
{
$clients = DB::select("[Insert SQL here]");
$this->mail = (new NotifyExpiredLicences($clients))->toMail('admin#example.com');
parent::__construct();
}
public function handle()
{
Mail::to('admin#example.com')->send($this->mail);
return 0;
}
public function preview()
{
return $this->mail;
}
}
App\Notifications\NotifyExpiredLicences.php:
class NotifyExpiredLicences extends Notification
{
public function __construct(protected $clients)
{
}
public function via($notifiable)
{
return ['mail'];
}
public function toMail($notifiable)
{
return (new Mailable($this->clients));
}
}
App\Mail\ExpiredLicences.php:
class ExpiredLicences extends Mailable
{
public function __construct(private $clients)
{
}
public function build()
{
return $this
->subject('Clients with Expired Licences')
->markdown('emails/expiredlicences',
['clients' => $this->clients, 'toname' => 'Admin']);
}
}
resources\views\emails\expiredlicences.blade.php:
#component('mail::message')
# Hi {!! $toname !!},
#component('mail::table')
| Client | Expired |
| ------------- | --------:|
#foreach ($clients as $client)
|{!! $client->CompanyName !!} | {!! $client->Expired !!}|
#endforeach
#endcomponent
<hr />
Thanks, {!! config('app.name') !!}
#endcomponent
For previewing with the browser routes\web.php:
Route::get('/notification', function () {
return (new SendExpiredLicences())->preview();
});
Ok just to save more commenting, here's what I'd recommend doing. This is all based on the Laravel docs, but there are multiple ways of doing it, including what you've used above. I don't really think of them as "right and wrong," more "common and uncommon."
Console\Kernel.php: I'd keep this mostly as-is, but pass the email to the command from a config file, rather than having it fixed in the command.
use App\Console\Commands\SendExpiredLicences;
…
protected function schedule(Schedule $schedule)
{
$recipient = config('myapp.expired.recipient');
$schedule->command(SendExpiredLicences::class, [$recipient])
->weekdays()
->at('08:30');
}
config/myapp.php:
<?php
return [
'expired' => [
'recipient' => 'admin#example.com',
],
];
App\Console\Commands\SendExpiredLicences.php: update the command to accept the email address as an argument, use on-demand notifications, and get rid of preview() method. Neither the command or the notification need to know about the client list, so don't build it yet.
<?php
namespace App\Console\Commands;
use App\Console\Command;
use App\Notifications\NotifyExpiredLicences;
use Illuminate\Support\Facade\Notification;
class SendExpiredLicences extends Command
{
protected $signature = 'email:expired-licences {recipient}';
protected $description = 'Email a list of expired licences to the given address';
public function handle()
{
$recip = $this->argument('recipient');
Notification::route('email', $recip)->notify(new NotifyExpiredLicences());
}
}
App\Notifications\NotifyExpiredLicences.php: the toMail() method should pass the notifiable object (i.e. the user getting notified) along, because the mailable will be responsible for adding the To address before the thing is sent.
<?php
namespace App\Notifications;
use App\Mail\ExpiredLicenses;
use Illuminate\Notifications\Notification;
class NotifyExpiredLicences extends Notification
{
public function via($notifiable)
{
return ['mail'];
}
public function toMail($notifiable)
{
return (new ExpiredLicenses($notifiable));
}
}
App\Mail\ExpiredLicences.php: since the mail message actually needs the list of clients, this is where we build it. We get the recipient here, either from the user's email or the anonymous object.
<?php
namespace App\Mail;
use App\Models\Client;
use Illuminate\Notifications\AnonymousNotifiable;
class ExpiredLicences extends Mailable
{
private $email;
public function __construct(private $notifiable)
{
// this allows the notification to be sent to normal users
// not just on-demand
$this->email = $notifiable instanceof AnonymousNotifiable
? $notifiable->routeNotificationFor('mail')
: $notifiable->email;
}
public function build()
{
// or whatever your object is
$clients = Client::whereHas('licenses', fn($q)=>$q->whereExpired(1));
return $this
->subject('Clients with Expired Licences')
->markdown(
'emails.expiredlicences',
['clients' => $clients, 'toname' => $this->notifiable->name ?? 'Admin']
)
->to($this->email);
}
}
For previewing with the browser routes\web.php:
Route::get('/notification', function () {
// create a dummy AnonymousNotifiable object for preview
$anon = Notification::route('email', 'no#example.com');
return (new ExpiredLicencesNotification())
->toMail($anon);
});

How to send a plain text email in Laravel 8 without using a blade?

How can I send a plain text email in Laravel 8 without using a blade?
All our email messages and templates are stored in a database, so using a blade is not applicable to us. I just want to pass in text to send.
Thanks.
Mail::raw('This is a simple text', function ($m) {
$m->to('toemail#abc.com')->subject('Email Subject');
});
Don't forget to include
use Mail;
This is what I ended up doing, and I think it's a good solution.
I created a generic Mailable class called app\Mail\Message.php.
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class Message extends Mailable {
use Queueable, SerializesModels;
private $theSubject;
private $htmlBody;
private $fromAddress;
private $theAttachments;
private $replyToEmail;
public function __construct($subject, $htmlBody, $fromAddress, $replyTo=null, $attachments=null) {
$this->theSubject = $subject;
$this->htmlBody = $htmlBody;
$this->fromAddress = $fromAddress;
$this->theAttachments = $attachments;
$this->replyToEmail = $replyTo;
}
public function getBody() {
return $this->htmlBody;
}
public function build() {
$this->subject($this->theSubject);
$this->from($this->fromAddress);
$this->html($this->htmlBody);
if ($this->replyToEmail != null) {
$this->replyTo($this->replyToEmail);
}
// more info here: https://laravel.com/docs/8.x/mail#attachments
if ($this->theAttachments != null) {
foreach($this->theAttachments as $attachment) {
if (is_array($attachment)) {
$this->attach($attachment[0], [ // $attachment[0] = temp file path
'as' => (isset($attachment[1]) ? $attachment[1]:null), // $attachment[1] = filename
'mime' => (isset($attachment[2]) ? $attachment[2]:null), // $attachment[2] = mime type
]);
} else {
$this->attach($attachment); // $attachment = temp file path
}
}
}
return $this;
}
}
To send an email, I do this:
$message = new Message($subject, $fullBody, $fromAddress, $replyTo, $attachments);
Mail::to($address)->send($message);
To test sending email, I do this:
Mail::fake();
Mail::assertNothingSent();
// do something that sends an email...
Mail::assertSent(Message::class, function($email) {
$token = // get something that should be found in the body
$email->build();
$allGood = true;
$allGood &= $email->subject === 'Password Recovery';
$allGood &= str_contains($email->getBody(), $token);
return $allGood;
});
I think you might be better of creating a Notification class that sends via mail. You can generate a notification with
php artisan make:notification OrderShipped
And by default its via method will send by mail, and will use Laravels default mail templates. In the toMail you can send whatever is needed. Then for your tests you can do:
public function testUserGetNotifiedIfOrderShipped())
{
Notification::fake();
// Hit some endpoints thats will send notification
// Assert Admin gets notification
Notification::assertSentTo(User::first(), OrderShipped::class);
}

In Laravel with ReCaptcha V3, how do I access the "score" returned from a rule?

In Laravel, I am setting up Google ReCaptcha V3 which now returns a "score". I've managed to setup a validation rule to allow my form to submit (all working), but this is only returning true or false to pass the validation.
How do I base this on the score instead?
I'm using this composer package to help me out - https://github.com/google/recaptcha
This is in my controller (where I am sending the token to validate with the server):
// validation
$this->validate( $request, array(
'g_recaptcha_response' => ['required', 'string', new Captcha()]
));
This is the rule:
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use ReCaptcha\ReCaptcha;
class Captcha implements Rule
{
public function __construct()
{
//
}
public function passes($attribute, $value)
{
$recaptcha = new ReCaptcha('SECRET');
$response = $recaptcha->verify($value, $_SERVER['REMOTE_ADDR']);
return $response->isSuccess();
}
public function message()
{
return 'Are you a robot?';
}
}
Can I access the class somehow from the controller? I can see in the package that I need to use ->getScore() but I don't know how to access it?
As you are doing in the validation rule, you can also get the score in controller :
public function something(YourRequest $request){
$recaptcha = new ReCaptcha('SECRET');
$response = $recaptcha->verify($request->g_recaptcha_response, $request->ip());
$score = $response->getScore();
}
More available methods of response can be found here

Laravel : How to customize title of email when sending email with Mailable?

I write a mailable to send email when user registers,I do it following the document(https://laravel.com/docs/5.5/mail):
first,generating a mailable:
php artisan make:mail UserRegistered
Ok,thus there is a UserRegistered.php file in the app/Mail directory,and I write the build() method like this:
public function build()
{
return $this->view('emails.activate-user')
->with([
'name' => $this->user->name,
'url' => route('activateUser',['token'=>$this->user->confirmation_token])
]);
}
The email can be sent successfully,the title of the email is User Registered,I want to customize the title ,how to do it?
You have to use subject method
public function build()
{
return $this->view('emails.activate-user')
->subject("My mail title")
->with([
'name' => $this->user->name,
'url' => route('activateUser',['token'=>$this->user->confirmation_token])
]);
}
Or update your mails class's constructor
public function __construct()
{
$this->subject('Sample title');
}
If you want to change mail title you need to change it in .env file. Same Like as given below:
You need to change it here for mail title.
And if you just want to change only subject then you can you use subject() function while sending mail to add you subject.
For Example:
public function build()
{
return $this->subject('Reset Password')
->view('emails.forgotpass');
}
and the final result will be like this where my APP_NAME is Laravel.
Result 1:
Result 2:

Customise Reset Password Email and pass User Data in Laravel 5.3

I am using Laravel 5.3 and customizing the Password Reset Email Template. I have done the following changes to create my own html email for the notification using a custom Mailable class. This is my progress so far:
ForgotPasswordController:
public function postEmail(Request $request)
{
$this->validate($request, ['email' => 'required|email']);
$response = Password::sendResetLink($request->only('email'), function (Message $message) {
$message->subject($this->getEmailSubject());
});
switch ($response) {
case Password::RESET_LINK_SENT:
return Response::json(['status' => trans($response)], 200);
case Password::INVALID_USER:
return Response::json(['email' => trans($response)], 400);
}
}
User Model:
public function sendPasswordResetNotification($token)
{
Mail::queue(new ResetPassword($token));
}
ResetPassword Mailable Class:
protected $token;
public function __construct($token)
{
$this->token = $token;
}
public function build()
{
$userEmail = 'something'; // How to add User Email??
$userName = 'Donald Trump'; // How to find out User's Name??
$subject = 'Password Reset';
return $this->view('emails.password')
->to($userEmail)
->subject($subject)
->with([
'token' => $this->token
'userEmail' => $userEmail,
'userName' => $userName
]);
}
If you noticed above, I am not sure how do I pass the user's name and find out the user's email address. Do I need to send this data from the User Model or do I query it from the Mailable class? Can someone show me how I can do that please?
Usually you ask for the user email in order to send a reset password email, that email should come as a request parameter to your route controller.
By default, L5.3 uses post('password/email) route to handle a reset password request. This route execute sendResetLinkEmail method which is defined in the 'SendsPasswordResetEmails' trait used by the App\Http\Controllers\Auth\ForgotPasswordController.
From here you can take one of 2 options:
1st: You could overwrite the route to call another function in the same controller (or any other controller, in this case could be your postEmail function) which search for the user model by the email you received, then you can pass the user model as function parameter to the method which execute the queue mail action (this may or may not require to overwrite the SendsPasswordResetEmails, depends on how you handle your reset password method).
This solution would looks something like this:
In routes/web.php
post('password/email', 'Auth\ForgotPasswordController#postEmail')
in app/Mail/passwordNotification.php (for instance)
protected $token;
protected $userModel;
public function __construct($token, User $userModel)
{
$this->token = $token;
$this->userModel = $userModel;
}
public function build()
{
$userEmail = $this->userModel->email;
$userName = $this->userModel->email
$subject = 'Password Reset';
return $this->view('emails.password')
->to($userEmail)
->subject($subject)
->with([
'token' => $this->token
'userEmail' => $userEmail,
'userName' => $userName
]);
}
in app/Http/Controllers/Auth/ForgotPasswordController
public function postEmail(Request $request)
{
$this->validate($request, ['email' => 'required|email']);
$userModel = User::where('email', $request->only('email'))->first();
Mail::queue(new ResetPassword($token));
//Manage here your response
}
2nd: You could just overwirte the trait SendsPasswordResetEmails to search for the user model by the email and use your customized function in sendResetLinkEmail function. There you could use your function but notice that you still have to handle somehow an status to create a response as you already have it on ForgotPasswordController.
I hope it helps!

Resources