I have a question about my code for confirmation email guests. I have a comment system. When guest read a post, he can write a comment. When he add a comment, guest need confirm email (on comment form, I have field for email). How I can correctly do email confirmation? How In Laravel, when user register.
Now I have table "guests" and a model Guest, with columns: name, email, email_token and email_verified_at (how in users table).
In model guest I have function:
public function sendEmailNotification($token)
{
$this->notify((new ReviewEmailNotification($token)));
}
When comment is created, I call observed function created and send email:
public function created(Review $review)
{
Auth::guest() ? $review->sendEmailNotification($review->email_token) : '';
}
My notification:
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Support\Facades\Lang;
class ReviewEmailNotification extends Notification
{
use Queueable;
public $token;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct($token)
{
$this->token = $token;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->subject(Lang::getFromJson('Verify comment'))
->line(Lang::getFromJson('Please confirm your Email:'))
->action(Lang::getFromJson('Confirm E-Mail'), url(config('app.url') . route('revemail', $this->token)))
->line(Lang::getFromJson('...'));
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
My function for confirm email:
public function activateEmail($token = null)
{
$guest = Guest::where('email_token', $token)->first();
if ($review) {
$guest->email_verified_at = now();
$guest->save();
}
return redirect()->route('home');
}
And when email is verified I create record on guests table.
This solution is working. But how I can go away from email_token? In users table I have only email_verified_at without column token. And I want do token with expire date, how in laravel. Can I use default laravel email confirmation for guests?
Simple modify your sendEmailNotification currently which is accepting $token change it to email and in your function where you are using token change it to email like this:
open this function ReviewEmailNotification
modify like this Review::where('email',$email)
want more help then post the complete code of ReviewEmailNotification
finally i understand your query, you have to change the code.... Dont use the user table for that because in every post/article will publish new comment/review so guest table will be good and add post_id, user_id for email confirmation , so your new function look like this
public function created(Review $review)
{
Auth::guest() ? $review->sendEmailNotification($review-> post_id,$review->user_id,) : '';
}
public function activateEmail($token = null)
{
$guest = Guest::where('post_id', $post_id)->where('user_id', $user_id)->first();
if ($review) {
$guest->email_verified_at = now();
$guest->save();
}
return redirect()->route('home');
}
public function sendEmailNotification($user_id,$post_id)
{
$this->notify((new ReviewEmailNotification($post_id,$user_id )));
}
Related
I am using Laravel's default notifications system. However, I need an extra column in the notifications table to check if the user already has the same unread-notification or not.
For example: If user's profile is not complete then on every login he/she will be reminded until the profile is complete. But if the previously generated notification is still unread then the new notification will not be generated.
Table: notifications
default_fields
notify
...
...
profile_incomplete
...
...
password_change_overdue
...
Notifications class
class NotifyToCompleteProfileNotification extends Notification
{
public function __construct()
{
$this->notify = 'profile_incomplete';
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['database'];
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return [
'data' => '...',
];
}
}
You can use custom notification channel to make this works. Let's assume you want to add a group field to notifications. First, add this field to table, then make file GroupedDbChannel.php:
namespace App\Notifications;
use Illuminate\Notifications\Notification;
class GroupedDbChannel
{
public function send($notifiable, Notification $notification)
{
$data = $notification->toDatabase($notifiable);
return $notifiable->routeNotificationFor('database')->create([
'id' => $notification->id,
'group' => $notification->group,
'type' => get_class($notification),
'data' => $data,
'read_at' => null,
]);
}
}
Next, you need to define custom group and channel for notification:
namespace App\Notifications;
use Illuminate\Notifications\Notification;
class TestNotification extends Notification {
/*
* Here you define additional value that will
* be used in custom notification channel.
*/
public string $group = 'incomplete-profile';
public function via($notifiable) {
return [GroupedDbChannel::class];
}
public function toArray($notifiable) {
return [
// notification data
];
}
/*
* It's important to define toDatabase method due
* it's used in notification channel. Of course,
* you can change it in GroupedDbChannel.
*/
public function toDatabase($notifiable): array
{
return $this->toArray($notifiable);
}
}
And it's done. Use notifications as standard.
$user->notify(new TestNotification());
Now, value incomplete-profile from $group field goes to notifications table to group column.
I have a weird issue in my website. I have several Notifications such as Email Verification and Password Reset that are sending properly. However, I made my own notification that sends an url with a UUID to the user and unfortunately, it doesn't send.
I tried every way: 'Notification::route', notify the user directly, nothing works. Actually, notifying the user directly would be bad since I need to send it to an email address not attached to any model.
Anyway, here's the code for the notification. Keep in mind the other notifications work, so I doubt it is the issue.
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class NewEmail extends Notification
{
use Queueable;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct($uuid)
{
$this->uuid = $uuid;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->line(__('messages.newEmailAsked'))
->action(__('messages.newEmailConfirm'), config('app.frontend_url') . '/verify-new-email?code=' . $this->uuid)
->line(__('messages.newEmailIgnore'));
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
public function addNewEmail($email) {
if(User::where('email', $email)->count() === 0) {
$uuid = Str::uuid();
NewEmail::create(['email' => $email, 'unique_code' => $uuid, 'user_id' => $this->id]);
Notification::route('mail', $email)->notify(new \App\Notifications\NewEmail($uuid));
} else {
return 'Email already exists.';
}
}
I really don't get why this notification isn't sent while the other are...
Weirdly enough, the problem fixed itself when I did the actual path the user would take instead of using Tinker.
Just had to $user->addNewEmail($newEmailHere) and it worked properly...
You have not defined the $uuid variable in your Queueable class
I'm new to Laravel. I just created a custom login with laravel 5.7. When I tried to reset password I'm getting this error:
"Declaration of
App\Employee::sendEmailVerificationNotification($token) should be
compatible with
Illuminate\Foundation\Auth\User::sendEmailVerificationNotification()"
Does anyone know how to resolve this error?
You may do something like this
class Employee extends Model implements MustVerifyEmail {
public function sendEmailVerificationNotification()
{
$this->notify(new VerifyEmail);
}
}
if you want to call it like Employee::sendEmailVerificationNotification() and if you want to verify the token you should extend the VerifyEmail notification something like
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\Lang;
use Illuminate\Auth\Notifications\VerifyEmail;
class VerifyEmailNotification extends VerifyEmail
{
use Queueable;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct($token)
{
//verify token
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
if (static::$toMailCallback) {
return call_user_func(static::$toMailCallback, $notifiable);
}
return (new MailMessage)
->subject(Lang::getFromJson('Verify Email Address'))
->line(Lang::getFromJson('Please click the button below to verify your email address'))
->action(
Lang::getFromJson('Verify Email Address'),
$this->verificationUrl($notifiable)
)
->line(Lang::getFromJson('If you did not create an account, no further action is required.'));
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}
Then in Employee model
public function sendEmailVerificationNotification($token)
{
$this->notify(new VerifyEmailNotification($token)); // your custom notification
}
You have to follow the same method signature if you want to override it-
You are overriding this method-
Illuminate\Foundation\Auth\User::sendEmailVerificationNotification()
to this-
App\Employee::sendEmailVerificationNotification($token)
If you notice the difference, you have passed $token in the method while the original method definition does not support that.
Create a different method if you need a different signature from the original method.
I'm trying to send a notification with a mention of a user in a general channel. This is what I have:
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class WeeklyTasksResponsible extends Notification
{
use Queueable;
protected $employee;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct(\App\Employee $employee)
{
$this->employee = $employee;
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['slack'];
}
/**
* Get the Slack representation of the notification.
*
* #param mixed $notifiable
* #return SlackMessage
*/
public function toSlack($notifiable)
{
return (new SlackMessage)
->content('Reponsible for this week is: ' . $this->employee->slack_name);
}
}
This will sent a weekly notification in the general slack channel of our company. The message is "Responsible for this week is: nameofuser". The problem is the user doesn't see a notification of this.
I've also tried do this:
public function toSlack($notifiable)
{
return (new SlackMessage)
->content('Reponsible for this week is: #' . $this->employee->slack_name);
}
But it isn't the same as mentioning someone myself in the channel.
How can I do this?
As mentioned by #HCK you can enable matching of usernames for #username mentions in chat.postMessages by setting the optional parameter link_names to true.
However, creating mentions with usernames is deprecated and should no longer be used.
The recommended approach is to create mentions with the user ID, which will work by default.
Example:
<#U12345678>
See the post A lingering farewell to the username from Slack for details about username deprecation.
I just found the following in the Laravel 6.18.0 source code:
/**
* Find and link channel names and usernames.
*
* #return $this
*/
public function linkNames()
{
$this->linkNames = 1;
return $this;
}
vendor/laravel/slack-notification-channel/src/Messages/SlackMessage.php
So you can use it like this:
public function toSlack($notifiable)
{
return (new SlackMessage)
->linkNames()
...
}
As #erik-kalkoken pointed out, use the Slack user ID, enclosed by <> and with the # sign. You can find it in your profile in the Slack App:
Actually, I tried that way and it's works pretty well.
$content .= "New order!\r\n #person";
return (new SlackMessage)
->content($content)->linkNames();
You would need to add the # before name of person or team and in order for slack to recognize them as mentions not just text, you would need to chain the content with linkNames
How to remake Laravel 5.7 Email Verification for Rest API?
Or is it worth doing everything from scratch?
This case works for me. Full project code here.
1) Redesigned VerificationController controller
Removed redirects and made response()->json(...) responses.
<?php
namespace App\Http\Controllers\API\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\VerifiesEmails;
use Illuminate\Http\Request;
use Illuminate\Auth\Events\Verified;
class VerificationController extends Controller
{
use VerifiesEmails;
/**
* Show the email verification notice.
*
*/
public function show()
{
//
}
/**
* Mark the authenticated user's email address as verified.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function verify(Request $request)
{
// ->route('id') gets route user id and getKey() gets current user id()
// do not forget that you must send Authorization header to get the user from the request
if ($request->route('id') == $request->user()->getKey() &&
$request->user()->markEmailAsVerified()) {
event(new Verified($request->user()));
}
return response()->json('Email verified!');
// return redirect($this->redirectPath());
}
/**
* Resend the email verification notification.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function resend(Request $request)
{
if ($request->user()->hasVerifiedEmail()) {
return response()->json('User already have verified email!', 422);
// return redirect($this->redirectPath());
}
$request->user()->sendEmailVerificationNotification();
return response()->json('The notification has been resubmitted');
// return back()->with('resent', true);
}
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware('signed')->only('verify');
$this->middleware('throttle:6,1')->only('verify', 'resend');
}
}
2) Added my Notification:
I made it so that the link in the email message led to my frontend and contained a temporarySignedRoute link for the request.
use Illuminate\Auth\Notifications\VerifyEmail as VerifyEmailBase;
class VerifyEmail extends VerifyEmailBase
{
// use Queueable;
/**
* Get the verification URL for the given notifiable.
*
* #param mixed $notifiable
* #return string
*/
protected function verificationUrl($notifiable)
{
$prefix = config('frontend.url') . config('frontend.email_verify_url');
$temporarySignedURL = URL::temporarySignedRoute(
'verification.verify', Carbon::now()->addMinutes(60), ['id' => $notifiable->getKey()]
);
// I use urlencode to pass a link to my frontend.
return $prefix . urlencode($temporarySignedURL);
}
}
3) Added config frontend.php:
return [
'url' => env('FRONTEND_URL', 'http://localhost:8080'),
// path to my frontend page with query param queryURL(temporarySignedRoute URL)
'email_verify_url' => env('FRONTEND_EMAIL_VERIFY_URL', '/verify-email?queryURL='),
];
4) Added to User model:
use App\Notifications\VerifyEmail;
and
/**
* Send the email verification notification.
*
* #return void
*/
public function sendEmailVerificationNotification()
{
$this->notify(new VerifyEmail); // my notification
}
5) Added routes
The following routes are used in Laravel:
// Email Verification Routes...
Route::get('email/verify', 'Auth\VerificationController#show')->name('verification.notice');
Route::get('email/verify/{id}', 'Auth\VerificationController#verify')->name('verification.verify');
Route::get('email/resend', 'Auth\VerificationController#resend')->name('verification.resend');
They are added to the application if used Auth::routes();.
As far as I understand the email/verify route and its method in the controller are not needed for Rest API.
6) On my frontend page /verify-email(from frontend.php config) i make a request to the address contained in the parameter queryURL
The received URL looks like this:
"http://localhost:8000/api/email/verify/6?expires=1537122891&signature=0e439ae2d511f4a04723a09f23d439ca96e96be54f7af322544fb76e3b39dd32"
My request(with Authorization header):
await this.$get(queryURL) // typical get request
The code perfectly verify the email and I can catch the error if it has already been verified. Also I can successfully resend the message to the email.
Did I make a mistake somewhere? Also I will be grateful if you improve something.
I tried Илья Зеленько answer but I must modify VerificationController construct method as follow
public function __construct()
{
$this->middleware('auth')->except(['verify','resend']);
$this->middleware('signed')->only('verify');
$this->middleware('throttle:6,1')->only('verify', 'resend');
}
otherwise laravel need autentication to access verify and resend routes