I have an Event that fires off a Welcome email whenever someone registers a new account on my website.
My problem is, I needed to create a "Create User" page on my admin section. Now every time I create a user from the admin section the email still fires off an email welcoming the new user.
This would be fine, but I need that email to say something else.
I don't want the Welcome email to fire when creating a user from the admin panel.
How can I control this Event from sending the email?
Code pretty much goes in this order:
1. Event code: NewUser.php
namespace App\Events;
... irrelevant classes
use Illuminate\Foundation\Events\Dispatchable;
use App\User;
class NewUser
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $user;
public function __construct(User $user)
{
$this->user = $user;
}
}
2. Listener: SendWelcomeEmail.php
namespace App\Listeners;
use App\Events\NewUser;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Mail;
use App\Mail\NewUserWelcome;
class SendWelcomeEmail
{
public function __construct()
{
//
}
public function handle(NewUser $event)
{
Mail::to($event->user->email)->send(new NewUserWelcome($event->user));
}
}
3. Mail: NewUserWelcome.php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
use App\User;
class NewUserWelcome extends Mailable
{
use Queueable, SerializesModels;
public $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function build()
{
return $this->subject('Welcome To The Website')->markdown('emails.user.newuserwelcome');
}
}
4. Markdown email would be next.
#component('mail::message')
# Greetings bla bla bla
5. EventServiceProvider: is making the call like this:
protected $listen = [
'App\Events\NewUser' => [
'App\Listeners\SendWelcomeEmail',
],
];
6. User model I have the following relevant code:
class User extends Authenticatable {
use Notifiable;
protected $dispatchesEvents = [
'created' => Events\NewUser::class
];
In my ADMIN SAVE USER FUNCTION | UserController This is what I'm using to SAVE the New User From Admin Panel: (No Event classes)
use Illuminate\Http\Request;
use App\User;
use Illuminate\Support\Facades\Hash;
class UserController extends Controller
{
public function adminUserStore(Request $request){
$newsupporter = User::create([
'name'=> $request->name,
'email' => $request->email,
'password' => Hash::make($quickpass),
]);
return back()->with('success','The user has been created and a password reset email has been sent to them.');
}
Any help would be appreciated, I've been battling this one for quite some time.
You can try by adding a new nullable column for your user model, which would check if the user was added by admin or someone else;
$newsupporter = User::create([
'name'=> $request->name,
'email' => $request->email,
'password' => Hash::make($quickpass),
'added_by' => 'admin',
]);
And then create a check and send email only when the user was not added my admin,
public function handle(NewUser $event)
{
if(!$event->user->added_by == 'admin'){
Mail::to($event->user->email)->send(new NewUserWelcome($event->user));
}
}
Personally I would introduce two events instead of the one. Something along the lines of AccountCreatedByUser and AccountCreatedByAdmin. These events can then be handled by separate listeners, which will send separate e-mails. This will mean you'll have to fire these events manually instead of depending on the built-in created event. This can be done like so:
event(new AccountCreatedByUser($user));
Documentation for firing events manually can be found here
Related
I want to have 2 notifications when new user registered:
inform admin that we have new user for moderation
send welcome email to new user
I created 2 events, put them in EventServiceProvider, but as result I have 2 emails to admin. I already tried to register 4 users with different emails, but none of them receive welcome email. It is always sent to admin config('mail.to.address')
For some reason welcome email sent to admin, instead of new user. What's wrong?
My code is below.
EventServiceProvider
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
SendWelcomeEmail::class,
SendNewUserRegisteredEmail::class,
],
];
SendWelcomeEmail
<?php
namespace App\Listeners;
use Mail;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Auth\Events\Registered;
class SendWelcomeEmail
{
public function __construct()
{
}
public function handle(Registered $event)
{
$data = array(
'name' => $event->user->name,
'email' => $event->user->email
);
Mail::send('emails.welcome', $data, function($message) use ($data) {
$message->to($data['email'])
->subject('Добро пожаловать на сайт '.config('app.name'). '.');
$message->from(config('mail.from.address'));
});
}
}
SendNewUserRegisteredEmail
<?php
namespace App\Listeners;
use Mail;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Auth\Events\Registered;
class SendNewUserRegisteredEmail
{
public function __construct()
{
}
public function handle(Registered $event)
{
$data = array(
'name' => $event->user->name,
'email' => $event->user->email
);
Mail::send('emails.registered', $data, function($message) use ($data) {
$message->to(config('mail.to.address'))
->subject('Новый пользователь на сайте '.config('app.name'). '.');
$message->from(config('mail.from.address'));
});
}
}
After a huge search in the internet and in the forum, I just gave up...
I am develping a rest api using Laravel 8 and I am trying since week to make the email verification working using the officiel documentation for that, the email is always sent successfully once the user is registered event(new Registered($user));
The problem is that once I click on the link in the received email, I got redirected to the login page (which in this case is a post call)..
Here my routes/api.php:
Route::group(['namespace' => 'App\Http\Controllers', 'middleware' => ['api'], 'prefix' => 'auth'], function ($router) {
Route::post('login', 'AuthController#login')->name('login');
Route::post('register', 'AuthController#register');
Route::post('logout', 'AuthController#logout');
Route::post('profile', 'AuthController#profile')->middleware('verified');
Route::post('refresh', 'AuthController#refresh');
});
Route::group(['namespace' => 'App\Http\Controllers', 'middleware' => ['api']],function ($router) {
Route::get('/email/verify/{id}/{hash}', 'VerificationController#verify')->middleware(['auth', 'signed'])->name('verification.verify');
Route::get('/email/resend', 'VerificationController#resend')->middleware(['auth', 'throttle:6,1'])->name('verification.send');
});
And here my VerificationController:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Foundation\Auth\EmailVerificationRequest;
class VerificationController extends Controller
{
public function resend(Request $request)
{
$request->user()->sendEmailVerificationNotification();
return response()->json(['message' => __('auth.email_sent')], Response::HTTP_NO_CONTENT);
}
public function verify(EmailVerificationRequest $request)
{
$request->fulfill();
return response()->json(['message' => __('auth.user_verified_successfully')], Response::HTTP_RESET_CONTENT);
}
}
Last but not least, I added the LogVerifiedUser event to EventServiceProvider as required.
Any suggestion plz? I tried to remove the middleware auth from verify route, but it doesn't help me...
PS: I am using JWT for authentication
I had to develop exactly the same functionality for my rest laravel 8 api, I share my work with you, hoping to be able to help you.
To begin, your problem is that the user is redirected to the login page after clicking on the verification link. But the question is has the user been marked as verified in the database when he click ?
If it is marked as verified in the database after the click, the functionality is working but the problem is the redirection. Because if you are using a Rest API you would probably want the user to be redirected to a login or success page of your frontend application.
The last problem is your middleware. First in the api.php file the middleware for the connection is 'auth:api' instead of 'auth'. But for once you do not have to put middleware on the verification route otherwise you will have to have the user connect so that he validates his email and since you go through an API route it is pretty boring ...
Finally here is the solution I opted for :
1. In your app/Models/User.php implements MustVerifyEmail (Normally, from what I understood, that you already did, but I prefer to put it in case if other people go through this topic)
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable implements MustVerifyEmail
{
use HasFactory, Notifiable, HasApiTokens;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}
2. In your app/Http/Controllers/AuthController.php add event on registered user (Normally, from what I understood, that you already did, but I prefer to put it in case if other people go through this topic)
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Auth\Events\Registered;
class AuthController extends Controller
{
public function register(Request $request)
{
$validatedData = $request->validate([
'name' => 'required|max:55',
'email' => 'email|required|unique:users',
'password' => 'required|confirmed'
]);
$validatedData['password'] = bcrypt($request->password);
$user = User::create($validatedData);
event(new Registered($user));
$accessToken = $user->createToken('authToken')->accessToken;
return response(['user' => $user, 'access_token' => $accessToken]);
}
public function login(Request $request)
{
$loginData = $request->validate([
'email' => 'email|required',
'password' => 'required'
]);
if (!auth()->attempt($loginData)) {
return response(['message' => 'Invalid Credentials']);
}
$accessToken = auth()->user()->createToken('authToken')->accessToken;
return response(['user' => auth()->user(), 'access_token' => $accessToken]);
}
}
3. In your routes/api.php defines this routes :
// Verify email
Route::get('/email/verify/{id}/{hash}', [VerifyEmailController::class, '__invoke'])
->middleware(['signed', 'throttle:6,1'])
->name('verification.verify');
// Resend link to verify email
Route::post('/email/verify/resend', function (Request $request) {
$request->user()->sendEmailVerificationNotification();
return back()->with('message', 'Verification link sent!');
})->middleware(['auth:api', 'throttle:6,1'])->name('verification.send');
4. Create app/Http/Controllers/VerifyEmailController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Auth\Events\Verified;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use App\Models\User;
class VerifyEmailController extends Controller
{
public function __invoke(Request $request): RedirectResponse
{
$user = User::find($request->route('id'));
if ($user->hasVerifiedEmail()) {
return redirect(env('FRONT_URL') . '/email/verify/already-success');
}
if ($user->markEmailAsVerified()) {
event(new Verified($user));
}
return redirect(env('FRONT_URL') . '/email/verify/success');
}
}
Explanations:
With this solution we keep all the operation of checking the official documentation by email. Except that instead of checking if the user is connected to retrieve it and put his email in verified. We launch a method in a controller which will find the corresponding user to put it in verified.
I hope I was understandable and that it can help you :)
That's because in register() method you don't logged in the user immediately after registering the user, when the user click the link in the email, laravel auth middleware detect that current user who visit the link is not authenticated, so it redirect the user to login route. To solve this problem refer to #Matthieu Gelle answer but customizes it as follows:
in step number 2 just add this code
Auth::login($user);
below event(new Registered($user));
in step 3 use this middleware:
->middleware(['auth', 'signed'])->name('verification.verify');
for those who use sanctum:
->middleware(['auth:sanctum', 'signed'])->name('verification.verify');
and change method name from '__invoke' to 'verifyEmail'
in step 4 use this method:
public function verifyEmail(\Illuminate\Foundation\Auth\EmailVerificationRequest $request)
{
$request->fulfill();
return response()->json(['code' => 200, 'message' => "Verified successfully"], 200);
}
I want to create an audit trail in my model. I already installed owen-it/laravel-auditing package via Composer. My question is that how can I implement it in my Model or controller. Please see my code for controller and Model below. Thanks
My Controller :
<?php
namespace App\Http\Controllers;
use App\Events\Test;
use App\Letter;
use App\Notifications\LetterNotification;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Validator;
class LetterController extends Controller
{
public function viewLetter()
{
return view('welcome');
}
/**
* Saves email into database
*
* #param array $data
* #return Letter
*/
protected function create(array $data)
{
$letter = Letter::create([
'email' => $data['email']
]);
$this->letterNotify($letter);
return $letter;
}
/**
* Validates email
*/
public function createLetter(Request $request)
{
$this->validate($request,[
'email' => 'required|email|max:255|unique:letters'
],[
'email.required' => 'Email is required.',
'email.unique' => 'Already registered.',
'email.email' => 'Please put a valid Email address'
]);
$this->create($request->all());
return redirect('/')->with('info','You are now registered.');
}
protected function letterNotify($letter)
{`enter code here`
Notification::send($letter, new LetterNotification($letter));
}
}
For my Model:
<?php
namespace App;
use OwenIt\Auditing\Auditable;
use OwenIt\Auditing\Contracts\Auditable as AuditableContract;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
class Letter extends Model implements AuditableContract
{
use Notifiable;
use Auditable;
protected $fillable = ['email'];
protected $table = 'letters';
}
Like I stated in my comment, the Laravel Auditing package only triggers an audit on a database operation involving an Eloquent model and event (by default, created, updated, deleted, restored).
Having said that, here's a list of steps to create an audit when logging in/out:
Create a listener for the Illuminate\Auth\Events\Login event;
Once fired, update a column in the users table that keeps track of the latest login date/time (latest_login_at, for example);
(Optional) update a column with the previous login date/time (last_login_at, for example);
By doing those updates to the users table, the Auditor kicks in;
You can also listen for the OwenIt\Auditing\Events\Auditing or OwenIt\Auditing\Events\Audited events and apply more logic if needed;
Follow the same steps for the Illuminate\Auth\Events\Logout event;
Hey guys I'm trying to learn PHP frameworks as well as OOP and I'm using Laravel 5.1 LTS.
I have the following code in my AuthController
<?php
namespace App\Http\Controllers\Auth;
use App\Verification;
use Mail;
use App\User;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
class AuthController extends Controller
{
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
private $redirectTo = '/home';
public function __construct()
{
$this->middleware('guest', ['except' => 'getLogout']);
}
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:users',
'password' => 'required|confirmed|min:6',
]);
}
protected function create(array $data){
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
// generate our UUID confirmation_code
mt_srand((double)microtime()*15000);//optional for php 4.2.0 and up.
$charid = strtoupper(md5(uniqid(rand(), true)));
$uuid = substr($charid, 0, 8)
.substr($charid, 8, 4)
.substr($charid,12, 4)
.substr($charid,16, 4)
.substr($charid,20,12);
$data['confirmation_code'] = $uuid;
// pass everything to the model here
$setVerification = new Verification();
$setVerification->setVerificationCode($data['email'], $data['confirmation_code']);
// send email for confirmation
Mail::send('email.test', $data, function ($m) use ($data){
$m->from('test#test.com', 'Your Application');
$m->to($data['email'])->subject('Thanks for register! Dont forget to confirm your email address');
});
return $user;
}
}
my error message Class 'Models\Verification' not found is coming from this piece of code here
// pass everything to the model here
$setVerification = new Verification();
$setVerification->setVerificationCode($data['email'], $data['confirmation_code']);
which looks right to my beginner's eyes, but it's clearly wrong.
Here is my Verification class that has the setVerificationCode method
<?php
namespace App\Http\Controllers;
use App\User;
use DB;
use App\Http\Controllers\Controller;
class Verification {
/**
* This method will update the confirmation_code column with the UUID
* return boolean
**/
protected function setVerificationCode($email, $uuid) {
$this->email = $email;
$this->uuid = $uuid;
// check to see if $email & $uuid is set
if (isset($email) && isset($uuid)) {
DB::table('users')
->where('email', $email)
->update(['confirmation_code' => $uuid]);
return TRUE;
} else {
return FALSE;
}
}
/**
* This method will validate if the UUID sent in the email matches with the one stored in the DB
* return boolean
**/
protected function verifyConfirmationCode() {
}
}
Please give the following in AuthController
use App\Http\Controllers\Verification;
instead of
use App\Verification;
If we give use App\Verification , it will check if there is any model named Verification.
its seems that, you are missing something, which, Extend your Model with eloquent model
use Illuminate\Database\Eloquent\Model;
class Verification extends Model
{
and the rest is seems fine.
also share your verification model code
Updated
instead of your this line
use App\Verification;
do this
use App\Models\Verification;
as you created custom directory for your Models then its better to auto load it in your composer.json file. add this line "app/Models" in your "autoload" section. follow this
"autoload": {
"classmap": [
"database",
"app/Models"
],
and after that, run this command in your project repo composer dump-autoload
In code with the comment "THIS IS NOT WORKING", I can't authorize.
I get user object and use Auth::login($user) but it doesn't work.
User in this moment creates in database.
public function login(Request $request)
{
if(Auth::check()) {
return redirect('home');
}
if($request->isMethod('post')) {
if($request->has('username') && $request->has('password')) {
$inputs = $request->except('_token');
if(!Auth::attempt(['username' => $inputs['username'], 'password' => $inputs['password']])) {
$user = $this->registerUser($inputs);
if($user) {
Auth::login($user);
}
}
}
return (Auth::check()) ? redirect()->route('home') : redirect()->back()->with('form_error', true);
}
return view('pages.auth.login');
}
User.php maybe here is something wrong?
namespace App\Models\system;
use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Foundation\Auth\Access\Authorizable;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class User extends Model implements AuthenticatableContract,
AuthorizableContract,
CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword;
protected $primaryKey = 'uid';
protected $table = 'eltk.dbo.system_users';
protected $guarded = [];
protected $hidden = ['password', 'remember_token'];
}
Let's start step by step.
Why did you create user on a method where you need to handle just login logic?
What's the name of your database? Is it eltk or dbo or what? Define it in config/database.php under 'database' or in .env and use in model just table name, not name of the whole table, is not necessary.
In login method you need something like this:
public function processLogin(Request $request){
if(Auth::attempt(['email' => $request->input('email'), 'password' => $request->input('password'), 'active' => 1])){
return redirect('/');
}else{
return redirect('login')->withMessage('User with this email and/or password does not exist or your account is not active.');
}
}
And btw, when you in if-else statement login user afterward you need to redirect him on some route.