[php] 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;
}

This question is related to php validation laravel laravel-4

The answer is


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.

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,                
            //
        ];
        //


    }

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'
);

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),
            ]
        ]);
}

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;
}

If i understand what you want:

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

In model update method, for exemple, should receive the $id with parameter.

Sorry my bad english.


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.


My solution:

$rules = $user->isDirty('email') ? \User::$rules : array_except(\User::$rules, 'email');

Then in validation:

$validator = \Validator::make(\Input::all(), $rules, \User::$messages);

The logic is if the email address in the form is different, we need to validated it, if the email hasn't changed, we don't need to validate, so remove that rule from validation.


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


public function rules()
{

    switch($this->method())
    {
        case 'GET':
        case 'DELETE':
        {
            return [];
        }
        case 'POST':
        {
            return [
                'name' => 'required|unique:permissions|max:255',
                'display_name' => 'required',
            ];
        }
        case 'PUT':
        case 'PATCH':
        {
            return [                    
                'name' => 'unique:permissions,name,'.$this->get('id').'|max:255',
                'display_name' => 'required',
            ];
        }
        default:break;
    }    
}

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


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!',
        ...
    ];
}

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]
    ];
}


Test below code:

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

Where ID is the primary id of the table


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.


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 :)


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.


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.


Test below code:

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

$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()
    ), ',');
}

This is what I ended up doing. I'm sure there is a more efficient way of doing this but this is what i came up with.

Model/User.php

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

Model/BaseModel.php

public function validate($data, $id = null) {


      $rules = $this->$rules_string;

     //let's loop through and explode the validation rules
     foreach($rules as $keys => $value) {

        $validations = explode('|', $value);

        foreach($validations as $key=>$value) {

            // Seearch for {{$id}} and replace it with $id
            $validations[$key] = str_replace('{{$id}}', $id, $value);

        }
        //Let's create the pipe seperator 
        $implode = implode("|", $validations);
        $rules[$keys] = $implode;

     }
     ....

  }

I pass the $user_id to the validation in the controller

Controller/UserController.php

public function update($id) { 

   .....

    $user = User::find($user_id);

    if($user->validate($formRequest, $user_id)) {
      //validation succcess 
    } 

    ....


}

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.


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"
];

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


There is an elegant way to do this. If you are using Resource Controllers, your link to edit your record will look like this:

/users/{user}/edit OR /users/1/edit

And in your UserRequest, the rule should be like this :

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

Or if your link to edit your record look like this:

/users/edit/1

You can try this also:

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

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:


Examples related to php

I am receiving warning in Facebook Application using PHP SDK Pass PDO prepared statement to variables Parse error: syntax error, unexpected [ Preg_match backtrack error Removing "http://" from a string How do I hide the PHP explode delimiter from submitted form results? Problems with installation of Google App Engine SDK for php in OS X Laravel 4 with Sentry 2 add user to a group on Registration php & mysql query not echoing in html with tags? How do I show a message in the foreach loop?

Examples related to validation

Rails 2.3.4 Persisting Model on Validation Failure Input type number "only numeric value" validation How can I manually set an Angular form field as invalid? Laravel Password & Password_Confirmation Validation Reactjs - Form input validation Get all validation errors from Angular 2 FormGroup Min / Max Validator in Angular 2 Final How to validate white spaces/empty spaces? [Angular 2] How to Validate on Max File Size in Laravel? WebForms UnobtrusiveValidationMode requires a ScriptResourceMapping for jquery

Examples related to laravel

Parameter binding on left joins with array in Laravel Query Builder Laravel 4 with Sentry 2 add user to a group on Registration Target class controller does not exist - Laravel 8 Visual Studio Code PHP Intelephense Keep Showing Not Necessary Error The POST method is not supported for this route. Supported methods: GET, HEAD. Laravel How to fix 'Unchecked runtime.lastError: The message port closed before a response was received' chrome issue? Post request in Laravel - Error - 419 Sorry, your session/ 419 your page has expired Expected response code 250 but got code "530", with message "530 5.7.1 Authentication required How can I run specific migration in laravel Laravel 5 show ErrorException file_put_contents failed to open stream: No such file or directory

Examples related to laravel-4

Parameter binding on left joins with array in Laravel Query Builder Laravel 4 with Sentry 2 add user to a group on Registration 'Malformed UTF-8 characters, possibly incorrectly encoded' in Laravel Can I do Model->where('id', ARRAY) multiple where conditions? how to fix stream_socket_enable_crypto(): SSL operation failed with code 1 Rollback one specific migration in Laravel How can I resolve "Your requirements could not be resolved to an installable set of packages" error? Define the selected option with the old input in Laravel / Blade Redirect to external URL with return in laravel laravel the requested url was not found on this server