I'm building a Laravel 5.3 app and using the basic auth of laravel (artisan make:auth). Now, the "Forgot Password" functionality works fine, so if a user can't login because he doesn't know his password, he can get a mail to reset it. But now I want that logged in users can change their password as well. I found that, but this doesn't really help me. I also know that there's a ResetsPasswords trait but how do I use it? And is there already a view as well I can use?
Can somebody help me here?
You don't actually need to use the default password controller to achieve this, you can write your own function to get the same result, for example:
public function postUpdatePassword() {
$user = Auth::user();
$password = $this->request->only([
'current_password', 'new_password', 'new_password_confirmation'
]);
$validator = Validator::make($password, [
'current_password' => 'required|current_password_match',
'new_password' => 'required|min:6|confirmed',
]);
if ( $validator->fails() )
return back()
->withErrors($validator)
->withInput();
$updated = $user->update([ 'password' => bcrypt($password['new_password']) ]);
if($updated)
return back()->with('success', 1);
return back()->with('success', 0);
}
As you can see I registered a new custom validation rule to check if the new passowrd match the old one, to register the rule just go to "app/Providers/AppServiceProvider.php" and add to the boot function the next lines:
Validator::extend('current_password_match', function($attribute, $value, $parameters, $validator) {
return Hash::check($value, Auth::user()->password);
});
Now the validation rule works but you won't get the error message, to add an error message to the new rule you just created you will have to modify these lines in "resources/lang/en/validation.php":
'custom' => [
'current_password' => [
'current_password_match' => 'Current password is incorrect.',
],
],
That's it, now you can use this function to change your the current user password :)
If you want to keep your AppServiceProvider.php file clean of closures (for whatever reason; personally I like these small files neat and tidy) you can add perform the following two things:
1) Add the following to the boot() method of the AppServiceProvider.php
Validator::extend('current_password_match', 'App\Validators\PasswordMatch#check');
2) Add a new file 'app/Validators/PasswordMatch.php' in line with the closure mentioned above.
<?php
namespace App\Validators;
use Hash;
use Auth;
class PasswordMatch
{
public function check($attribute, $value, $parameters, $validator){
return Hash::check($value, Auth::user()->password);
}
}
You can then also add the validation rule messages to your extended FormRequest class messages() method like:
'current_password_match' => 'Current password is incorrect',
Related
I would like to make a validator through the usage of Laravel requests, and validation is working fine. But if I don't do them in the controller I can't return back to view with errors if validator fails.
Is it possible to implement this:
if ($validator->fails()) {
return redirect('post/create')
->withErrors($validator)
->withInput();
}
within the custom request? Something like this maybe:
public function rules()
{
return [
'name' => 'required',
'email' => 'required|email|unique:users',
'password' => 'required|min:6|confirmed',
]; // ->withErrors()
}
FormRequest by default returns the user back to the previous page with the errors and input. You don't have to specify it manually. Just set the rules and use the newly created FormRequest in your controller method instead of using the Request object.
This is what happens under the hood.
return $this->redirector->to($this->getRedirectUrl())
->withInput($this->except($this->dontFlash))
->withErrors($errors, $this->errorBag);
I've got a registration-form that is posting to a controller which validates the request.
// Validate request
$this->validate($request, [
'email' => 'email|required|unique:users,email,NULL,id,deleted_at,NULL',
'zipcode' => 'digits:5|numeric|exists:zipcodes,zipcode,lat,NOT_NULL',
]);
I'd like to do things if the email already is taken (unique), rather than just send an error-message back (only for unique, not other errors).
What I want to do in the end is to open a modal after the redirect back to previous page (where the form is).
I appreciate any help you can provide.
You can create custom validation rule and try to do your stuff in a closure:
public function boot()
{
Validator::extend('foo', function($attribute, $value, $parameters, $validator) {
// Do your stuff.
return $value == 'foo';
});
}
There are several similar questions but all of them seem incomplete as they are referring to not existing functions.
I am referring to:
Check for active user state with laravel
Login only if user is active using Laravel
extend laravel 5 built-in authentication to login only "if user == active"
In all of them there are presented solutions mostly to alter functions from AuthController, however those functions are not there.
I am using latest version of Laravel (5.2) so my default mentioned file looks like: https://github.com/laravel/laravel/blob/master/app/Http/Controllers/Auth/AuthController.php
Now, how do I implement this functionality? I have tried copying public function postLogin() (as suggested in those other mentioned posts) into that AuthController file. Nothing changed.
I am clearly missing something here.
Please someone help!
Edit:
The function that I have added is:
public function postLogin(Request $request)
{
$this->validate($request, [
'email' => 'required|email', 'password' => 'required',
]);
$credentials = $this->getCredentials($request);
// This section is the only change
if (Auth::validate($credentials)) {
$user = Auth::getLastAttempted();
if ($user->active) {
Auth::login($user, $request->has('remember'));
return redirect()->intended($this->redirectPath());
} else {
return redirect($this->loginPath()) // Change this to redirect elsewhere
->withInput($request->only('email', 'remember'))
->withErrors([
'active' => 'You must be active to login.'
]);
}
}
return redirect($this->loginPath())
->withInput($request->only('email', 'remember'))
->withErrors([
'email' => $this->getFailedLoginMessage(),
]);
}
Most functionality in AuthController is added using traits. Note this line in the beginning of the class:
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
If you look at AuthenticatesAndRegistersUsers you can see that it does have a postLogin method.
As of why implementing this method doesn't work: I think you are missing the $request parameter in the method signature.
Add a larger code snippet to your question if this isn't the case.
EDIT: for future debugging: php artisan route:list gives you a list of which routes call which methods. This can give you a hint to which method to override.
So, for everyone facing the same problem on Laravel 5.2 with Authentication provided by php artisan make:auth.
First important thing is to understand that Route::auth(); points to login function and not postLogin(Thank you #lagbox!)
Then you will have to update AuthController:
Add on top of the file:
use Auth;
use Illuminate\Http\Request;
Add somewhere after use AuthenticatesAndRegistersUsers, ThrottlesLogins; :
/**
* Where to redirect users if login is unsuccessufull.
*
* #var string
*/
protected $loginPath = '/login';`
And the updated login function:
public function login(Request $request)
{
$this->validate($request, [
'email' => 'required|email', 'password' => 'required',
]);
$credentials = $this->getCredentials($request);
// This section is the only change
if (Auth::validate($credentials)) {
$user = Auth::getLastAttempted();
if ($user->active) {
Auth::login($user, $request->has('remember'));
return redirect()->intended($this->redirectPath());
} else {
return redirect($this->loginPath) // Change this to redirect elsewhere
->withInput($request->only('email', 'remember'))
->withErrors([
'active' => 'You must be active to login.'
]);
}
}
return redirect($this->loginPath)
->withInput($request->only('email', 'remember'))
->withErrors([
'email' => $this->getFailedLoginMessage(),
]);
}
Then login.blade.php should be updated to show the new error #if ($errors->has('active'))
Thank you guys for help!
I'm trying to create a custom validation rule within laravel 4 and am struggling to get it to work or understand what I'm doing.
Currently - when I try to submit a form and validate it i get the following in my browser:
/**/
Which I'm taking as something is broken!
I have created a class in app/validators/customValidate.php
class CustomValidate extends Illuminate\Validation\Validator
{
public function uniqueMailchimp($attribute, $value, $parameters)
{
$mailchimp = MailchimpWrapper::lists()
->memberInfo(
'xxxxxxx',
array('emails'=>array(
'email'=>$value
)
));
//dd($$mailchimp['success_count']);
return ($mailchimp['success_count'] > 0 ? false : true );
}
I have run composer dump-autload
In my controller I am doing the following:
$rules = array(
'email' =>'email|uniqueMailchimp',
);
$messages = array(
'uniqueMailchimp'=>'The email provided has already been used.'
);
$validator = Validator::make($data, $rules, $messages);
}
I am then checking for a valid form with:
if($validator->passes()) {
# code
}
if validation fails the controller should redirect to the view:
return Redirect::route('members.create')
->withInput()
->withErrors($validator)
->with('errMessage', $message);
I've probably missed a step. I've seen posts about registering the rule in global.php but I'm not sure what I'm doing.
Any help appreciated
You are quite right - you need to register your rule with the validator. In any bootstrap-like file (a service provider is the best candidate if you have one, else app/start/global.php is good, and routes.php wouldn't be crazy either) you need the following code:
Validator::extend('foo', 'CustomValidate#uniqueMailchimp');
However, if you take a look at the docs you'll see that you don't need a whole class for this - you can just do it as a closure. A class is useful if you want the automatic IoC DI carried out for you though (although you don't appear to use that in your validator).
I copied an example from the laravel documentation:
public function postResetPassword() {
$credentials = array('email' => Input::get('email'));
return Password::reset($credentials, function($user, $password) {
$user->password = Hash::make($password);
$user->save();
return Redirect::to('/');
});
}
But it seems that returning Redirect::to('/') doesn't work, because instead of home page I get an error which tells that controller method is not found.
But if I write the code this way:
$credentials = array('email' => Input::get('email'));
Password::reset($credentials, function($user, $password) {
$user->password = Hash::make($password);
$user->save();
});
return Redirect::back();
It works, though I can't understand how do I get session flash variables (actually I get them).
Another question is where are the rules about password length (6 chars) are written? Can I change them?
To answer why your first code example doesn't work is because if your look at your app/routes.php file you should see something along the lines of Route::get('/', 'HomeController#index');. The part that comes before the # symbol is the name of your controller while the part after it is the method that is being called in your controller when the route is requested. Make sure that method is defined.
After looking at the following. I think you should put the redirect inside of the closure you give as a return statement.
Then how you retrieve data that has been flashed to the session after redirecting the user you use the following Session::get('key');.
For your last question look at the following documentation.
Example:
$validator = Validator::make(
array('email' => Input::get('email'), 'password' => Input::get('password'), 'password_confirm' => Input::get('password_confirm')),
array('email' => 'required|unique:users,email|email', 'password' => 'required|min:3|max:20|same:password_confirm')
);
The second array passed is where you can modify the rules for the validator.
To answer your second question regarding changing the rules for password validation.
The Password facade extends PasswordBroker.php which has this function on line 208:
/**
* Set a custom password validator.
*
* #param \Closure $callback
* #return void
*/
public function validator(Closure $callback)
{
$this->passwordValidator = $callback;
}
Therefore, to override the default password validator, simply make this call from your controller:
Password::validator(function(){
//validator in here
});