user742736
user742736

Reputation: 2729

Laravel: Validation unique on update

I know this question has been asked many times before but no one explains how to get the id when you're validating in the model.

'email' => 'unique:users,email_address,10'

My validation rule is in the model so how do I pass the ID of the record to the validation rule.

Here is my models/User

protected $rules_update = [
    'email_address' => 'required|email|unique:users,email_address,'.$id,
    'first_name' => "required",
    'last_name' => "required",
    'password' => "required|min:6|same:password_confirm",
    'password_confirm' => "required:min:6|same:password",
    'password_current' => "required:min:6"
];

models/BaseModel

    protected $rules = array();

public $errors;

/*
    * @data: array, Data to be validated
    * @rules: string, rule name in model 
*/

public function validate($data, $rules = "rules") {

    $validation  = Validator::make($data, $this->$rules);

    if($validation->passes()) {
        return true;
    }

    $this->errors = $validation->messages();

    return false;
}

Upvotes: 129

Views: 328482

Answers (30)

Saptarshi Dey
Saptarshi Dey

Reputation: 205

use Illuminate\Validation\Rule;
Rule::unique('leads')->whereNull('deleted_at')->ignore($this->id);

Use this, it will ignore the record that you want to update.

ignore($this->id)

Upvotes: 0

e sadeghi
e sadeghi

Reputation: 43

Hi in my case when i convert $id to integer type its work correctly so we have

'email_address'=>'unique:users,email_address,'.intval($id)

Upvotes: 0

Md Zahid
Md Zahid

Reputation: 1202

Most answers to this question refer to email_address, but in Laravel's inbuilt authentication system, the email field name is just email. Here is an example of validating a unique field, i.e. an email on the update:

Form Requests look like this:

public function rules()
    {
      return [
          'email' => [ 'required','email', Rule::unique('users')->ignore($this->id ?? 0)]];
    }

?? 0 If you use this then if hare id does not exist this request will not give you an error

Save Whenever you access the id property of $this->user, you may encounter an undefined property if you haven't injected the User model into your route. If that is the case, use:

public function rules()
    {
      return [
          'email' => 'required|email|unique:users,email,'.$this->user()->id ?? 0,
      ];
    }

?? 0 If you use this then if hare id does not exist this request will not give you an error

Upvotes: 3

Sonam Tomar
Sonam Tomar

Reputation: 131

Test below code:

'email' => 'required|email|unique:users,email_address,'. $id .'ID'

Where ID is the primary id of the table

Upvotes: 13

Nazmul Hoque
Nazmul Hoque

Reputation: 970

Do One step in controller

Works Fine with Laravel 9

$request->validate([
        'name'=>'required|unique:categories,name,'.$id,
    ]);

Upvotes: 7

Neeraj Tangariya
Neeraj Tangariya

Reputation: 1407

In Laravel 8.x you can use Rule::unique method as well

Forcing A Unique Rule To Ignore A Given ID:

use Illuminate\Validation\Rule;

public function update(Request $request, Post $post)
{
    $validatedData = $request->validate([
        'name' => ['required', 'max:60', Rule::unique('posts')->ignore($post->id)],
    ]);

    $post->update($validatedData);
    
    return redirect(route('posts.index'))->with('status', 'post updated successfully');
}

Upvotes: 8

Nikunj Dhimar
Nikunj Dhimar

Reputation: 2386

If you have a separate rules method. You can use easier the following syntax.

public function rules()
{
    return [
        'email' => "required|unique:users,email,{$this->id}"
    ]; 
}

Upvotes: 6

Abbas Arif
Abbas Arif

Reputation: 398

The PUT/PATCH request doesn't support multipart/form-data and will not populate $_FILES so If you are using POST method on UPDATE then use the route name in switch method.

public function rules()
{
    switch ($this->route()->getName()) {
    case 'users.update':
        $rules = [
        'name'                  => 'required|min:3',
        'gender'                => 'required',
        'email'                 => 'required|email|unique:users,id,:id',
        'password'              => 'required|min:5',
        'password_confirmation' => 'required|min:5|same:password',
        ];
        break;

    default:
        $rules = [
            'name'                  => 'required|min:3',
            'gender'                => 'required',
            'email'                 => 'required|email|unique:users',
            'password'              => 'required|min:5',
            'password_confirmation' => 'required|min:5|same:password',
        ];
        break;
    }

    return $rules;
}

Upvotes: 1

alamriku
alamriku

Reputation: 11

If a login user want to update the email then auth() helper function will give us the login user id auth()->user()->id

Laravel helpers#method-auth

   Validator::make($data, [
'email' => [
    'required',
    Rule::unique('users')->ignore(auth()->user()->id),
],

]);

if Admin want to change the specific user information from User list then validation will be like this :

 Validator::make($data, [
'email' => [
    'required',
    Rule::unique('users')->ignore($request->user),
],

Laravel validation#rule-unique

$request object contain the current route related model objects. Which gives the model.

Try dd($request)

Upvotes: 1

Ripon Uddin
Ripon Uddin

Reputation: 714

Very easy to do it ,

Write it at your controller

$this->validate($request,[
     'email'=>['required',Rule::unique('yourTableName')->ignore($request->id)]
]);
Note : Rule::unique('yourTableName')->ignore($idParameter) , here $idParameter you can receive from get url also you can get it from hidden field.
Most important is don't forget to import Rule at the top.

Upvotes: 1

samtax01
samtax01

Reputation: 952

There is a simple and elegant way to do this. If you are passing the user_id in a body request or through a query parameter. e.g

/update/profile?user_id=

Then in your request rules

  public function rules(Request $request)
    {
        return [
            'first_name' => 'required|string',
            'last_name' => 'required|string',
            'email' => ['required','email', 'string', Rule::unique('users')->ignore($request->user_id )],
            'phone_number' => ['required', 'string', Rule::unique('users')->ignore($request->user_id )],
        ];
    }

Better Still, you can pass in auth->id() in place of $request->user_id to get the login user id.

Upvotes: 4

Kashif Shahzad
Kashif Shahzad

Reputation: 135

The Best Option is here try just once no need more code when unique validation on updating data

'email' => 'unique:users,email_address,' . $userId,

hereemailis field name and users is table name and email_address is table attribute name which you want unique and $userid is updating row id

Upvotes: 7

Soubhagya Kumar Barik
Soubhagya Kumar Barik

Reputation: 2615

After researching a lot on this laravel validation topic including unique column, finally got the best approach. Please have a look

In your controller

    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Validator;

    class UserController extends Controller
    {
         public function saveUser(Request $request){
                $validator = Validator::make($request->all(),User::rules($request->get('id')),User::$messages);
                if($validator->fails()){
                    return redirect()->back()->withErrors($validator)->withInput();
                }
            }
    }

saveUser method can be called for add/update user record.

In you model

class User extends Model
{
    public static function rules($id = null)
    {
        return [
            'email_address' => 'required|email|unique:users,email_address,'.$id,
            'first_name' => "required",
            'last_name' => "required",
            'password' => "required|min:6|same:password_confirm",
            'password_confirm' => "required:min:6|same:password",
            'password_current' => "required:min:6"
        ];
    }
    public static $messages = [
        'email_address.required' => 'Please enter email!',
        'email_address.email' => 'Invalid email!',
        'email_address.unique' => 'Email already exist!',
        ...
    ];
}

Upvotes: 2

Raju Rj
Raju Rj

Reputation: 71

You can try this.

protected $rules_update = [
    'email_address' => 'required|email|unique:users,email_address,'. $this->id,
    'first_name' => "required",
    'last_name' => "required",
    'password' => "required|min:6|same:password_confirm",
    'password_confirm' => "required:min:6|same:password",
    'password_current' => "required:min:6"
];

Upvotes: 7

Nazmus Shakib
Nazmus Shakib

Reputation: 832

It works like a charm someone can try this. Here I have used soft delete checker. You could omit the last: id,deleted_at, NULL if your model doesn't have soft delete implementation.

public function rules()
{
    switch ($this->method()) {
        case 'PUT':
            $emailRules = "required|unique:users,email,{$this->id},id,deleted_at,NULL";
            break;
        default:
            $emailRules = "required|unique:users,email,NULL,id,deleted_at,NULL";
            break;
    }

    return [
        'email' => $emailRules,
        'display_name' => 'nullable',
        'description' => 'nullable',
    ];
}

Thank you.

Upvotes: 0

Surya
Surya

Reputation: 124

Since you will want to ignore the record you are updating when performing an update, you will want to use ignore as mentioned by some others. But I prefer to receive an instance of the User rather then just an ID. This method will also allow you to do the same for other models

Controller

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

        return back();
    }

UserRequest

    public function rules()
    {
        return [
            'email' => [
                'required',
                \Illuminate\Validation\Rule::unique('users')->ignoreModel($this->route('user')),
            ],
        ];
    }

update: use ignoreModel in stead of ignore

Upvotes: 1

GQ.
GQ.

Reputation: 147

Test below code:

$validator = Validator::make(
            array(
              'E-mail'=>$request['email'],
             ),
            array(
              'E-mail' => 'required|email|unique:users,email,'.$request['id'],
             ));

Upvotes: 1

Abdul Malik
Abdul Malik

Reputation: 559

You can also use model classpath, if you don't want to hard code the table name.

function rules(){
    return [
        'email' => ['required','string',
         Rule::unique(User::class,'email')->ignore($this->id)]
    ];
}

Here $this->id is either 0 or the record Id to be updated.

Upvotes: 3

Luis Morales
Luis Morales

Reputation: 850

I read the previous post, but none approach the real problem. We need use the rule unique to apply on add and edit case. I use this rule on edit and add case and work fine.

In my solution i use rule function from Request Class.

  1. I sent id over hidden input form field on edit form.
  2. On the Rule function, we find by unique column and get the record.
  3. Now evaluate the situation. If exist record and id are equal the unique must be not activate (that's mean edit record).

On the code:

public function rules()
    {
        //
        $user = User::where('email', $this->email)->first();
        //
        $this->id = isset($this->id) ? $this->id : null;
        $emailRule = (($user != null) && ($user->id == $this->id)) ? 'required|email:rfc,dns|max:255' : 'required|unique:users|email:rfc,dns|max:255';
        //        
        return [
            //
            'email'            =>  $emailRule,                
            //
        ];
        //


    }

Upvotes: 0

ufuk
ufuk

Reputation: 371

Use for Laravel 6.0

use Illuminate\Validation\Rule;

public function update(Request $request, $id)
    {
        // Form validation
        $request->validate([
            'category_name'   =>  [
                'required',
                'max:255',
                 Rule::unique('categories')->ignore($id),
            ]
        ]);
}

Upvotes: 2

Rashed Rahat
Rashed Rahat

Reputation: 2495

Here is the solution:

For Update:

public function controllerName(Request $request, $id)

{

    $this->validate($request, [
        "form_field_name" => 'required|unique:db_table_name,db_table_column_name,'.$id
    ]);

    // the rest code
}

That's it. Happy Coding :)

Upvotes: 8

Rehmat
Rehmat

Reputation: 5081

Just a side note, most answers to this question talk about email_address while in Laravel's inbuilt auth system, the email field name is just email. Here is an example how you can validate a unique field, i.e. an email on the update:

In a Form Request, you do like this:

public function rules()
{
  return [
      'email' => 'required|email|unique:users,email,'.$this->user->id,
  ];
}

Or if you are validating your data in a controller directly:

public function update(Request $request, User $user)
{
  $request->validate([
      'email' => 'required|email|unique:users,email,'.$user->id,
  ]);
}

Update: If you are updating the signed in user and aren't injecting the User model into your route, you may encounter undefined property when accessing id on $this->user. In that case, use:

public function rules()
    {
      return [
          'email' => 'required|email|unique:users,email,'.$this->user()->id,
      ];
    }

A more elegant way since Laravel 5.7 is:

public function rules()
{
    return [
        'email' => ['required', 'email', \Illuminate\Validation\Rule::unique('users')->ignore($this->user()->id)]
    ];
}

P.S: I have added some other rules, i.e. required and email, in order to make this example clear for newbies.

Upvotes: 137

thenguyenit
thenguyenit

Reputation: 789

$rules = [
    "email" => "email|unique:users, email, '.$id.', user_id"
];

In Illuminate\Validation\Rules\Unique;

Unique validation will parse string validation to Rule object

Unique validation has pattern: unique:%s,%s,%s,%s,%s'

Corresponding with: table name, column, ignore, id column, format wheres

/**
 * Convert the rule to a validation string.
 *
 * @return string
 */
public function __toString()
{
    return rtrim(sprintf('unique:%s,%s,%s,%s,%s',
        $this->table,
        $this->column,
        $this->ignore ?: 'NULL',
        $this->idColumn,
        $this->formatWheres()
    ), ',');
}

Upvotes: 5

Matovu Ronald
Matovu Ronald

Reputation: 822

i would solve that by doing something like this

public function rules()
{
    return [
         'name' => 
            'required|min:2|max:255|unique:courses,name,'.\Request::get('id'),
    ];
}

Where you get the id from the request and pass it on the rule

Upvotes: 4

Donkarnash
Donkarnash

Reputation: 12845

For unique rule in the controller - which obviously will be different for the store method and the update method, I usually make a function within the controller for rules which will return an array of rules.

protected function rules($request)
{
    $commonRules = [
        'first_name' => "required",
        'last_name' => "required",
        'password' => "required|min:6|same:password_confirm",
        'password_confirm' => "required:min:6|same:password",
        'password_current' => "required:min:6"
    ];

    $uniqueRules = $request->id

          //update
        ? ['email_address' => ['required', 'email', 'unique:users,email' . $request->get('id')]]

          //store
        : ['email_address' => ['required', 'email', 'unique:users,email']];


    return array_merge($commonRules, $uinqueRules);
}

Then in the respective store and update methods

$validatedData = $request->validate($this->rules($request));

This saves from defining two different rule sets for store and update methods.

If you can afford to compromise a bit on readability, it can also be

protected function rules($request)
{
    return [
        'first_name' => "required",
        'last_name' => "required",
        'password' => "required|min:6|same:password_confirm",
        'password_confirm' => "required:min:6|same:password",
        'password_current' => "required:min:6",
        'email_address' => ['required', 'email', 'unique:users,email' . $request->id ?: null]
    ];
}

Upvotes: 0

tewshi
tewshi

Reputation: 655

From Laravel 5.7, this works great

use Illuminate\Validation\Rule;

Validator::make($data, [
    'email' => [
        'required',
        Rule::unique('users')->ignore($user->id),
    ],
]);

Forcing A Unique Rule To Ignore A Given ID:

Upvotes: 22

Altravista
Altravista

Reputation: 367

an even simpler solution tested with version 5.2

in your model

// validator rules
public static $rules = array(
    ...
    'email_address' => 'email|required|unique:users,id'
);

Upvotes: 8

Saiful Islam
Saiful Islam

Reputation: 670

While updating any Existing Data Write validator as following:

'email' => ['required','email', Rule::unique('users')->ignore($user->id)]

This will skip/ignore existing user's id's unique value matching for the specific column.

Upvotes: 1

Ricardo Canelas
Ricardo Canelas

Reputation: 2459

One simple solution.

In your Model

protected $rules = [
    'email_address' => 'sometimes|required|email|unique:users',
    ..
];

In your Controller, action:update

...
$rules = User::$rules;
$rules['email_address'] = $rules['email_address'] . ',id,' . $id;
$validationCertificate  = Validator::make($input, $rules); 

Upvotes: 54

zarpio
zarpio

Reputation: 7358

Found the easiest way, working fine while I am using Laravel 5.2

public function rules()

{

switch ($this->method()) {
    case 'PUT':
        $rules = [
            'name'                  => 'required|min:3',
            'gender'                => 'required',
            'email'                 => 'required|email|unique:users,id,:id',
            'password'              => 'required|min:5',
            'password_confirmation' => 'required|min:5|same:password',
        ];
        break;

    default:
        $rules = [
            'name'                  => 'required|min:3',
            'gender'                => 'required',
            'email'                 => 'required|email|unique:users',
            'password'              => 'required|min:5',
            'password_confirmation' => 'required|min:5|same:password',
        ];
        break;
}

return $rules;
}

Upvotes: 3

Related Questions