Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

mallorca's avatar

Help updating user email and password

I can create users just fine, but I'm having some problems when updating them.

The problem is that I can't update having that request. It blocks me and says that the email is already taken (by the same user), and if I don't enter anything in the password fields, it automatically removes the password. What would be a good solution to solve this? Thanks!

My user edit form looks something like this:

 {!! Form::model($users, ['method' => 'patch', 'action' => ['UsersController@update', $users->id], 'files' => true]) !!}

    <div class="form-group">
    {!! Form::label('email', 'Email:') !!}
    {!! Form::text('email', null, ['class' => 'form-control']) !!}
    </div>

    <div class="form-group">
    {!! Form::label('password', 'New Password:') !!}
    {!! Form::password('password',['class' => 'form-control']) !!}
    </div>

    <div class="form-group">
    {!! Form::label('password', 'Confirm New Password:') !!}
    {!! Form::password('password_confirmation', ['class' => 'form-control']) !!}
    </div>

{!! Form::close() !!}

My UserRequest:

    public function rules()
    {
        return [
            'name'        => 'required',
            'email' => 'required|email|max:255|unique:users',
            'password' => 'required|confirmed|min:6'
        ];
    }

My UsersController:

  public function update(UserRequest $request, $id)
    {
$user = User::findOrFail($id);
        $user->update($request->all());

In my User model:

public function setPasswordAttribute($password)
    {   
        $this->attributes['password'] = bcrypt($password);
    }
0 likes
28 replies
ohffs's avatar

I seem to remember that you can put a ",$id" at the end of your email rule with the users id - then the rule will skip that particular record.

mallorca's avatar

@ohffs could you please provide an example? Also, do you know a solution for the password situation?

jekinney's avatar

The password should be a separate form. Generally you have the user put in their old password to verify. If the password match the database then you validate and set the new password. Look up authentication in the docs or use hash facade to decrypt the passed. I prefer a auth check method to recheck credentials my self.

On the validation to skip a row for unique there is a couple of paragraphs in the validation docs explaining how to do that. Pretty self explanatory.

mallorca's avatar

@jekinney Thank you for your answer. I tried but couldn't get it to work earlier using the docs. I would really appreciate it if you could provide some code examples for my case? Thanks!

skliche's avatar
skliche
Best Answer
Level 42

@bobeta The unique validator is used to ensure that the given value does not already exist in the specified table. Use the exists validator instead:

'email' => 'required|email|max:255|exists:users',
2 likes
jekinney's avatar

email => 'required | email | unique:users,email,'.auth()->id;

1 like
jekinney's avatar

Unique goes: Parameter 1 (users) is the table. Parameter 2 is the column. Parameter 3 in this case is the row to NOT check. The current logged in user. Notice the period to concatenate the variable out side the apostrophe.

mallorca's avatar

Thanks @skliche and @jekinney I managed to fix the email part. However, I'm still not sure how to do with the change password. Does it really need to be a separate form? Could someone please provide an example of how you did it / would do it? Thanks!

skliche's avatar

@bobeta A few more thoughts on this. A user should only be allowed to change the password after logging in. So you don't need the field email.

You don't need a separate form to handle the password.

I use the following rules:

'oldpassword' => 'required|min:8|validpassword',
'password' => 'required|min:8|confirmed'

validpassword is a custom validator to check the old password against the password of the user that is currently logged in:

Validator::extend( 'validpassword', function( $attribute, $value, $parameters ) {
    return ( \Hash::check( $value, \Auth::user()->password ) );
});

Not entering a new password results in a validation error.

mallorca's avatar

@skliche Thank you for this example. Where do you place the validpassword part? Also, I have a case where I want the admin to be able to add a new password without knowing the old one. How would you do in such a situation?

skliche's avatar

@bobeta Put it in the boot() method of /app/Providers/AppServiceProvider.php. You might want to move it into a dedicated service provider later.

In the admin case you don't need the validation, just skip it:

public function rules() {
        $rules = [
            'password' => 'required|min:8|confirmed'
        ];

        if ( ! \Auth::user()->isAdmin() ) {
            $rules['oldpassword'] = 'required|min:8|validpassword';
        }

        return $rules;
    }

Additionally, don't even show the field in the form if an admin is logged in.

mallorca's avatar

@skliche Thanks once again for your great help. The problem I have left now is that if I don't enter anything in the password fields (if I let's say only want to change email or name), I get a validation error. How can I solve this?

skliche's avatar

@bobeta You'd have to dynamically generate the rules and remove the empty password fields from the request:

public function rules() {
    $rules = [
        'email' => 'required|email|max:255|exists:users'
    ];

    if ( $this->request->has( 'password' ) && 0 == strlen( $this->request->get( 'password' ) ) ) {
        $this->request->remove( 'password' );
        $this->request->remove( 'password_confirmation' );
    } else {
        $rules['password'] = 'required|min:8|confirmed';
    }

    if ( ! \Auth::user()->isAdmin() ) {
        $rules['oldpassword'] = 'required|min:6|validpassword';
    }

    return $rules;
}

You could make the email field optional if the user is not an admin and has submitted password values.

You should evaluate all your use cases including the admin cases. It might be simpler to separate these use cases into different forms, actions, and form requests. It's still ok but stealthily approaching the point of turning into a mess ...

skliche's avatar

@bobeta I just had another look at the code. If the user is trying to change the e-mail address, the validation should be different:

'email' => 'required|email|max:255|unique:users'

The other one is only useful to check if the admin needs to update a user's data. So now it get's messy if you want to keep all of those use cases in one place ...

Gotta go now, but I'll be back tomorrow.

mallorca's avatar

@skliche Thank you once again, as always, you've been of great help.

When I change the email to unique:users, then I can't update the user. It says that the email is already taken (it's taken by that user). Do I need to add the $id there somehow?

Snapey's avatar

try;

'email' => 'required|email|max:255|unique:users,email,'.$this->id,

... assuming that the user's ID is passed as part of the request , eg /user/15/update

3 likes
skliche's avatar

@bobeta Yes, you have to differentiate who tries to do what. Something like this:

public function rules() {
        $rules = array();

        if ( $this->request->has( 'password' ) && 0 == strlen( $this->request->get( 'password' ) ) ) {
            // no new password has been submitted, so the user doesn't want to change the password
            $this->request->remove( 'password' );
            $this->request->remove( 'password_confirmation' );
        } else {
            $rules['password'] = 'required|min:8|confirmed';
        }

        if ( \Auth::user()->isAdmin() ) {
            // an admin always has to provide an existing e-mail address
            $rules['email'] = 'required|email|max:255|exists:users';
            return $rules;
        }

        // always require the current (old) password when the request has not been submitted by an admin
        $rules['oldpassword'] = 'required|min:6|validpassword';

        if ( $this->request->has( 'email' ) && 0 == strlen( $this->request->get( 'email' ) ) ) {
            // no e-mail address has been submitted
            $this->request->remove( 'email' );
        } else {
            // a new e-mail address has been submitted, make sure it doesn't already exist
            $rules['email'] = 'required|email|max:255|unique:users';
        }

        return $rules;
    }

But as I said, now it's a mess.

Snapey's avatar

Better to have a separate password reset function (and form) rather than present an empty field to the user and hope they don't put something in it and mess up their password.

mallorca's avatar

@Snapey & @skliche Thanks for all your efforts. I will separate the password reset function & form.

However, I'm still having problems getting the email right.

If I use exists:users, then if I want to change a users email, I get the following error: The selected email is invalid. (because it doesn't exist).

If I use unique:users (or unique:users,email,'$this->id), then I get that the email is already taken (if I change other fields and not the email).

I also tried with:

if ( $this->request->has( 'email' ) && 0 == strlen( $this->request->get( 'email' ) ) ) {
            // no e-mail address has been submitted
            $this->request->remove( 'email' );
        } else {
            // a new e-mail address has been submitted, make sure it doesn't already exist
            $rules['email'] = 'required|email|max:255|unique:users';
        }

But I still get "The email has already been taken." if I change some other field & not email and submit.

I want it to be unique, but I want it also to be updateable, in case someone changes their email address.

skliche's avatar

@bobeta Only use exists:users when your admin uses your form. When a "regular" user submits the request use unique:users.

You could separate all those use cases:

  1. user wants to change the e-mail address (use unique:users)
  2. users wants to change the password (no need for an e-mail field)
  3. admin wants to change a user's e-mail address (use exists:users; no need for password fields)
  4. admin wants to change a user's password (use exists:users)

Create separate forms, form requests, routes, and controller actions. Make sure that the admin use cases cannot be used by a regular user.

mallorca's avatar

@skliche Thank you for the detailed answer. That's what I'm looking to do. However, I get 2 problems trying to solve this using your suggestions.

Problem 1: when I use exists:users in the admin edit request, I as a admin can't change the users email. I get the following error: The selected email is invalid. I suppose that's because the email doesn't exist in the table? What if I want to change it to a email that doesn't exist in the table?

Problem 2: When I use unique:users in user form and I don't make a change in the email field and submit, as a user I get the following error: The email has already been taken.

skliche's avatar

@bobeta Re problem 1
The form used by an admin to change an e-mail address should contain two fields:

  1. current e-mail address (use exists:users)
  2. new e-mail address (use unique:users)

Re problem 2
The form the user can use to change the e-mail address? If you want to accept that value as valid you can use

$rules['email'] = 'required|email|max:255|unique:users,email,' . \Auth::user()->id;
Snapey's avatar

You are making this sooo hard.

Just present the user with a form with their current username and email address.

Allow them to change either or both and submit.

Check the rules using the unique validator but excepting the user themselves, as per the previous example rule from me.

Then just write the name and the email to the database for the user with the given ID

Snapey's avatar

Routes.php (part)

    Route::get('/user/{id}/edit', 'User\UserController@edit');
    Route::post('/user/{id}/update', 'User\UserController@update');

User Controller

class UserController extends Controller
{
    /**
     * Show the form for editing the user's settings.
     *
     * @param  int  $id
     * @return Response
     */
    public function edit($id)
    {

        //guard - only the user themself or admin can edit

        if($this->user->is_admin || $this->user->id == $id )
        {
            $account = User::find($id);
            return view('user.edit')->with(compact('account'));
        }
        throw new PermissionDenied();
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  EditUserRequest  $request
     * @param  int  $id
     * @return Response
     */
    public function update(EditUserRequest $request, $id)
    {

        $user = User::findOrFail($id);

        $user->email = $request->email;
        $user->name = $request->name;
        $user->save();

        flash()->success("Your changes to your account have been saved",'Saved:');

        return redirect()->route('dashboard');
    }

}

EditUserRequest.php

<?php

namespace App\Http\Requests;

use App\Http\Requests\Request;
use Auth;


class EditUserRequest extends Request
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        //can be the owner of the record or an admin
        return (Auth::user()->is_admin || Auth::user()->id === $this->id );
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'name' => 'required|max:255',
            'email' => 'required|email|max:255|unique:users,email,'.$this->id,
        ];
    }
}

edit form (part)

                    {!! Form::model($user,['method'=>'POST', 'action'=>['User\UserController@update',$user->id],'class' => 'form-horizontal']) !!}
                      <div class="form-group">
                        <label for="name" class="col-sm-2 control-label">Name:</label>
                        <div class="col-sm-6">
                          {!! Form::text('name',null,['class'=>'form-control']) !!}
                          <span>{!! $errors->first('name') !!}</span>
                        </div>
                      </div>
                    
                      <div class="form-group">
                        <label for="email" class="col-sm-2 control-label">Email:</label>
                        <div class="col-sm-6">
                          {!! Form::text('email',null,['class'=>'form-control']) !!}
                          <span>{!! $errors->first('email') !!}</span>
                        </div>
                      </div>

                      <div class="form-group">
                        <div class="col-sm-offset-2 col-sm-10">
                          <button type="submit" class="btn btn-primary">Save Changes</button>
                        </div>
                      </div>
                    {!! Form::close() !!}

2 likes
vknyvz's avatar

accepted answer is actually wrong; it should be

'email' => 'required|email|unique:users,email,' . $this->id,

if you put exists you will have mysql errors during 'updating' event, which is ran during a model update.

3 likes

Please or to participate in this conversation.