Validation issue in laravel update function - php

I'm having a admin dashboard in my laravel application
From the dashboard Admin can edit the user profile contents.
Here is my current usercontroller (only the update function included)
public function update(Request $request, $id)
{
$this->validate($request, [
'name' => ['required', 'alpha','min:2', 'max:255'],
'last_name' => ['required', 'alpha','min:2', 'max:255'],
'email' => ['required','email', 'max:255', 'unique:users,email,'.$id],
'password' => ['same:confirm-password','regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[#$!%*?&])[A-Za-z\d#$!%*?&]{12,}$/'],
'mobile'=>['required', 'regex:/^\+[0-9]?()[0-9](\s|\S)(\d[0-9]{9})$/','numeric','min:9'],
'username'=>['required', 'string', 'min:4', 'max:10', 'unique:users,username,'.$id],
'roles'=>['required'],
//'user_roles'=>['required'],
]);
$input = $request->all();
if(!empty($input['password'])){
$input['password'] = Hash::make($input['password']);
}else{
$input = array_except($input,array('password'));
}
$user = User::find($id);
$user->update($input);
DB::table('model_has_roles')->where('model_id',$id)->delete();
$user->assignRole($request->input('roles'));
if($input['roles']=='customer'){
return redirect()->route('customers.index')
->with('success','Customer has been updated successfully');
}
else{
return redirect()->route('users.index')
->with('success','User has been updated successfully');
}
}
Now my problem is,
When ever admin updates an user without changing the password fields, the password fields get validated and throw me a regex error related to the password...
How can I avoid that issue and validate password field only if they are being changed.

With your current validation rules password field will always be checked with the regex. Adding nullable rule in first position of your password validation rules could help you solving the issue.

Related

how to make a update api in laravel using autherntication token

i am trying to make a update API that updates fileds using header auth token
I am new to larvel
this is what I have tried
Route::patch('/update', function (\Illuminate\Http\Request $request) {
$data = $request->validate([
'name' => '|max:255',
'phone'=> 'max:255',
'email' => '|email|unique:users',
'period'=> 'max:255',
'babyname'=> 'max:255',
'baby_date'=> 'max:255',
])}) return new \Illuminate\Http\JsonResponse(['user' => $user] , 200);
})->middleware('auth:api');
Changes
Removed typo. 'name' => '|max:255' to 'name' => 'max:255' and 'email' => '|email|unique:users' to 'email' => 'email|unique:users'
Condition based unique() check added 'email|unique:users,email,' . $request->user()->id,. This will be used to Skip the Current record.
return should be placed inside the Route(), not outside.
Used $user = $request->user(); to update the record.
Route::patch('/update', function (\Illuminate\Http\Request $request) {
$user = $request->user();
$data = $request->validate([
'name' => 'max:255', # remove "|"
'phone'=> 'max:255',
'email' => 'email|unique:users,email,' . $request->user()->id, # remove "|"
'period'=> 'max:255',
'babyname'=> 'max:255',
'baby_date'=> 'max:255',
]);
$user->update($data);
return new \Illuminate\Http\JsonResponse(['user' => $user] , 200);
})->middleware('auth:api');
Or for the in-detail update.(Without update())
$user = $request->user();
$user->name = $data['name'];
$user->phone = $data['phone'];
$user->email = $data['email'];
$user->period = $data['period'];
$user->babyname = $data['babyname'];
$user->baby_date = $data['baby_date'];
$user->save();
Few recommendations:
Use resource route instead of single routes -> https://laravel.com/docs/9.x/controllers#resource-controllers
Read more about validation rules -> https://laravel.com/docs/9.x/validation#available-validation-rules
You can customize the response status:
200 Success
404 Not Found (page or other resource doesn't exist)
401 Not authorized (not logged in)
403 Logged in but access to requested area is forbidden
400 Bad request (something wrong with URL or parameters)
422 Unprocessable Entity (validation failed)
500 General server error
API Route
//In the api route header
use App\Http\Controllers\UserController;
//The route method
Route::patch('/update/{user}', [UserController::class, 'update'])->middleware('auth:api');
UserController
/**
* Update the specified model.
*
* #param \Illuminate\Http\Request $request
* #param User $user
* #return \Illuminate\Http\Response
*/
public function update(Request $request, User $user)
{
//Please read: https://laravel.com/docs/9.x/validation#available-validation-rules
//For more information about validation rules and how they work.
$data = $request->validate([
'name' => '|max:255',
'phone'=> 'max:255',
'email' => '|email|unique:users',
'period'=> 'max:255',
'babyname'=> 'max:255',
'baby_date'=> 'max:255',
]);
$user->update($data);
//You can pass a response status to handle it in your view/javascript as required
return response()->json([
'user' => $user->toArray(),
], 200);
}
Please let me know if you require further help or clarifications, always happy to help.
Fix your route; it is not found; you have to supply the ID inside the route to reach the controller:
Route::patch('/update/{id}', function (\Illuminate\Http\Request $request) {
// ...

update api not updating db in laravel

i tryed to make a update api in larvel in post man i am gettng 200 but the reponce of api is same as the old data and api does not update
what i have tryed is this
Route::patch('/update', function (\Illuminate\Http\Request $request) {
// Validate the incoming data
$data = $request->validate([
'name' => '|max:255',
'phone'=> 'max:255',
'email' => '|email|unique:users',
'period'=> 'max:255',
'babyname'=> 'max:255',
'baby_date'=> 'max:255',
]);
// Retrieve the authenticated user
$user = auth()->user();
// Update the user's fields with the new data
$user->update($data);
// Return a JSON response with the updated user data
return new \Illuminate\Http\JsonResponse(['user' => $user] , 200);
})->middleware('auth:api');
as u can see in screen shot the data although it is 200 the responce is same old data and data is not updated in db
Route::patch('/update', function (\Illuminate\Http\Request $request) {
// Validate the incoming data
$data = $request->validate([
'name' => '|max:255',
'phone'=> 'max:255',
'email' => '|email|unique:users',
'period'=> 'max:255',
'babyname'=> 'max:255',
'baby_date'=> 'max:255',
]);
// Retrieve the authenticated user
$user = auth()->user();
$data = $request->all();
$user->fill($data);
// Update the user's fields with the new data
$user->save();
// Return a JSON response with the updated user data
return new \Illuminate\Http\JsonResponse(['user' => $user] , 200);
})->middleware('auth:api');

How to access currently logged in user globally in Laravel, Auth::user() returning null

When a user is logged in, session('person_id') is set, but Auth::user() returns null.
This means I have to do this everywhere I need access to properties or methods of the user:
$person = Person::where('id', session('person_id'))->firstOrFail();
What is a better solution for this? Could I set $person in the BaseController then access the user via $this->user when I need it?
I don't want to do a DB query for every request on every page. Using Laravel 8 with PHP 8.
Here are my current login and signup functions:
/**
* Handles user login
*
* #param Request $request
* #return \Illuminate\Http\RedirectResponse
*/
public function login(Request $request)
{
$credentials = $request->validate([
'email' => ['required', 'email'],
'password' => ['required'],
]);
if (Auth::attempt($credentials, request('remember'))) {
$request->session()->regenerate();
return redirect()->intended('/account')->with('status', 'Logged in');
}
return back()->withErrors([
'email' => 'The provided credentials do not match our records.',
]);
}
/**
* Saves a new unverified user, sends code to their email and redirects to verify page
*
* #param Request $request
*/
public function signUp(Request $request)
{
// #todo Move to SignUpRequest file
$request->validate([
'email' => 'required|email|unique:people',
'password' => 'required',
]);
$person = new Person;
$person->email = $request->email;
$person->password = Hash::make($request->password);
if (!$person->save()) {
return back()->with('status', 'Failed to save the person to the database');
}
$request->session()->put('person_id', $person->id);
$verification = new Verification;
$verification->person_id = $person->id;
$verification->code = rand(111111, 999999);
$verification->valid_from = Carbon::now();
$verification->expires_at = Carbon::now()->addDay();
if (!$verification->save()) {
return back()->with('status', 'Failed to save the verification to the database');
}
// email stuff
return redirect('/verify')->with('status', 'Successfully created account, please verify to continue');
}
It seems your fighting with framework default authentication you're using another model instead of User
I recommend reading the official documentation
You can take a look at laravel breeze to see how they implemented authentication
if you check the laravel breeze you'll see you missed the Auth::login($user)
public function store(Request $request)
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
event(new Registered($user));
Auth::login($user);
return redirect(RouteServiceProvider::HOME);
}
Laravel ships with a the global helper auth() and you can access the logged user with auth()->user()

Laravel - Validator::make fails my unique rule

I'm trying to use a validation on 'name' as unique, but to be ignored if the objected that has that same name is the same object being updated.
The Validation keeps failing, can you help me figuring out why?
Validator Function - I'm using same validator function to both create and update and only need to apply the rule on the update.
protected function validator(array $data, Wharehouse $wharehouse = null)
{
//different validations for create and edit
if($wharehouse != null){
return Validator::make($data, [
'name' => ['required', 'string', 'max:255', Rule::unique('wharehouses')->ignore($wharehouse)],
'espacoTotal' => ['required', 'numeric', 'max:60000']
]);
}else{
return Validator::make($data, [
'name' => ['required', 'string', 'max:255', Rule::unique('wharehouses')],
'espacoTotal' => ['required', 'numeric', 'max:60000']
]);
}
}
Validation Call
protected function editById(Request $request)
{
$wharehouse = Wharehouse::find($request->wharehouse_id);
$validation = $this->validator($request->all(),$wharehouse);
if ($validation->fails()) {
return Redirect::to('/wharehouses')->withInput()->withErrors($validation);
} else {
$wharehouse->name = $request->input('name');
$wharehouse->espacoTotal = $request->input('espaco');
$wharehouse->save();
return back()->with('create.success','Armazem actualizado com sucesso.');
}
}
Append the id of the instance currently being updated to the validator.
Pass the id of your instance to ignore the unique validator.
In the validator, use a parameter to detect if you are updating or
creating the resource.
If updating, force the unique rule to ignore a given id: unique:table,column,except,idColumn
//rules
'name' => ['required', 'string', 'max:255', 'unique:users,name,' . $userId],

How to validate current, new, and new password confirmation in Laravel 5?

I have created the password route, view and method in UserController#getProfilePassword and UserController#postProfilePassword
At the moment, if I fill out the new_password field, it gets hashed and submitted to the database correctly, then I can login with the new password.
But I need to be able to validate the new_password and new_password_confirm to make sure they're the same and validate the user's current password as well.
How can I do that?
EDIT: I added $this->validate to the method, but now I keep getting the error The password confirmation confirmation does not match. even though they do match as I am using a simple password. Also I think I need to check against the current password manually as validator won't do it for me.
public function getProfilePassword(Request $request) {
return view('profile/password', ['user' => Auth::user()]);
}
public function postProfilePassword(Request $request) {
$user = Auth::user();
$this->validate($request, [
'old_password' => 'required',
'password' => 'required|min:4',
'password_confirmation' => 'required|confirmed'
]);
$user->password = Hash::make(Input::get('new_password'));
$user->save();
}
And this is the view
<form action="{{ route('profile/updatepassword') }}" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="name">Current Password</label>
<input type="password" name="old_password" class="form-control" id="old_password">
</div>
<div class="form-group">
<label for="name">Password</label>
<input type="password" name="password" class="form-control" id="password">
</div>
<div class="form-group">
<label for="name">New Password</label>
<input type="password" name="password_confirmation" class="form-control" id="password_confirmation">
</div>
<button type="submit" class="btn btn-primary">Change Password</button>
<input type="hidden" value="{{ Session::token() }}" name="_token">
</form>
There's a Hash::check() function which allows you to check whether the old password entered by user is correct or not.
usage
if (Hash::check("param1", "param2")) {
//add logic here
}
param1 - user password that has been entered on the form
param2 - old password hash stored in database
it will return true if old password has been entered correctly and you can add your logic accordingly
for new_password and new_confirm_password to be same, you can add your validation in form request like
'new_password' => 'required',
'new_confirm_password' => 'required|same:new_password'
If you only need the functionality of a custom rule once throughout your application, you may use a Closure instead of a rule object. The Closure receives the attribute's name, the attribute's value, and a $fail callback that should be called if validation fails
$request->validate([
'new_password' => 'required|confirmed|min:4',
'current_password' => ['required', function ($attribute, $value, $fail) use ($user) {
if (!\Hash::check($value, $user->password)) {
return $fail(__('The current password is incorrect.'));
}
}],
]);
https://laravel.com/docs/5.6/validation#using-closures
In Laravel 6 there is a new rule called password ,according to docs
The field under validation must match the authenticated user's password. You may specify an authentication guard using the rule's first parameter:
'password' => 'password:api'
so the validation rules can be as simple as :
'current_password' => 'required|password',
'password' => 'required|string|min:8|confirmed',
You can do this by creating a custom validation rule (for this example I'm using current_password and new_password as the input names).
Put this in AppServiceProvider::boot():
Validator::extend('current_password', function ($attribute, $value, $parameters, $validator) {
$user = User::find($parameters[0]);
return $user && Hash::check($value, $user->password);
});
Now you can use the following in your controller:
$user = auth()->user(); // or pass an actual user here
$this->validate($request, [
'current_password' => 'required_with:new_password|current_password,'.$user->id,
]);
Using laravel 5.8/6.0, here is what i do(without much additional code)
Step 1: Validate
$data = request()->validate([
'firstname' => ['required', 'string', 'max:255'],
'lastname' => ['required', 'string', 'max:255'],
'username' => ['bail', 'nullable', 'string', 'max:255', 'unique:users'],
'email' => ['bail', 'nullable', 'string', 'email:rfc,strict,dns,spoof,filter', 'max:255', 'unique:users'],
'new_password' => ['nullable', 'string', 'min:8'],
'confirm_new_password' => ['nullable', 'required_with:new_password', 'same:new_password'],
'current_password' => ['required', function ($attribute, $value, $fail) {
if (!\Hash::check($value, Auth::user()->password)) {
return $fail(__('The current password is incorrect.'));
}
}]
]);
Step 2: If validation is passed
Create array , checking each input value (but not the ones with the required tag in validation) for presence or null OR do something that you want.
For example:
if(request(input)){
$data += ['input' => request(input)];
}
Update database using the created array
For example:
Auth::user()->account->update($data);
A complete function which will check everything. You just need to send old_password, new_password and confirm_password.
public function changePassword(Request $request) {
try {
$valid = validator($request->only('old_password', 'new_password', 'confirm_password'), [
'old_password' => 'required|string|min:6',
'new_password' => 'required|string|min:6|different:old_password',
'confirm_password' => 'required_with:new_password|same:new_password|string|min:6',
], [
'confirm_password.required_with' => 'Confirm password is required.'
]);
if ($valid->fails()) {
return response()->json([
'errors' => $valid->errors(),
'message' => 'Faild to update password.',
'status' => false
], 200);
}
// Hash::check("param1", "param2")
// param1 - user password that has been entered on the form
// param2 - old password hash stored in database
if (Hash::check($request->get('old_password'), Auth::user()->password)) {
$user = User::find(Auth::user()->id);
$user->password = (new BcryptHasher)->make($request->get('new_password'));
if ($user->save()) {
return response()->json([
'data' => [],
'message' => 'Your password has been updated',
'status' => true
], 200);
}
} else {
return response()->json([
'errors' => [],
'message' => 'Wrong password entered.',
'status' => false
], 200);
}
} catch (Exception $e) {
return response()->json([
'errors' => $e->getMessage(),
'message' => 'Please try again',
'status' => false
], 200);
}
}
Laravel Check Old Password and Updating a New Password | More
public function updatePassword(Request $request)
{
$this->validate($request, [
'old_password' => 'required',
'new_password' => 'required|min:6',
'confirm_password' => 'required|same:new_password',
]);
$data = $request->all();
if(!\Hash::check($data['old_password'], auth()->user()->password)){
return back()->with('error','You have entered wrong password');
}else{
here you will write password update code
}
}
Validation rules for laravel 8.*
Default
'current_password' => 'required|current_password',
'password' => 'required|min:8|confirmed',
Custom
php artisan make:rule MatchOldPassword
//inside MatchOldPassword
public function passes($attribute, $value)
{
return Hash::check($value, auth()->user()->password);
}
'current_password' => ['required', new MatchOldPassword()],
'password' => 'required|min:8|confirmed',
Validation rules for Laravel 8.*
use default available validation rules:
[
'current_password' => ['required', 'string', 'current_password'],
'password' => ['required', 'string', 'min:4', 'confirmed']
]
current_password based on Laravel Document check parameter to be equaled to authenticated user password
confirmed based on Laravel
Document check parameter to equaled with new parameter named:
{parameter}_confirmation
You can add confirmed as it's to confirm old password.
And 'required|confirmed' you change to 'required|same:password' to compare password and password confirmation
'old_password' => 'required|confirmed',
'password' => 'required|min:4',
'password_confirmation' => 'required|same:password'
Good luck!
In Laravel 8.x you can use this method in UserController.php:
public function ChangePasswordStore(Request $request, $user_id)
{
$user = User::findOrFail($user_id);
$request->validate([
'password' => 'required|confirmed|string|min:8',
'current_password' => ['required', function ($attr, $password, $validation) use ($user) {
if (!\Hash::check($password, $user->password)) {
return $validation(__('The current password is incorrect.'));
}
}],
]);
User::where('id', $user_id)->update([
'password' => Hash::make($request->input('password')),
]);
return redirect()->back();
}
In web.php:
Route::post('/user/{user_id}/changepassword', [
App\Http\Controllers\Admin\UserController::class,
'changepasswordStore'
])->name('users.changepassword.store');

Categories