Laravel update model with a unique attribute validation rule

I have a laravel User model that has a unique rule for checking for username and email . In my repository, when I update the model, I override these fields so as not to have problems with the necessary validation:

 public function update($id, $data) { $user = $this->findById($id); $user->fill($data); $this->validate($user->toArray()); $user->save(); return $user; } 

It fails with

 ValidationException: {"username":["The username has already been taken."],"email":["The email has already been taken."]} 

Is there any way to fix this elegantly?

+43
php validation eloquent laravel
Mar 14 '14 at 13:01
source share
13 answers

Add the id instance that is currently being updated in the validator.

  • Pass the id your instance to ignore the unique validator.

  • In the validator, use the parameter to detect if you are updating or creating a resource.

If you are updating, make the only rule to ignore this identifier:

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

When creating, proceed as usual:

 //rules 'email' => 'unique:users,email_address', 
+85
Mar 14 '14 at 13:21
source share

Another elegant way ...

In your model, create a static function:

 public static function rules ($id=0, $merge=[]) { return array_merge( [ 'username' => 'required|min:3|max:12|unique:users,username' . ($id ? ",$id" : ''), 'email' => 'required|email|unique:member'. ($id ? ",id,$id" : ''), 'firstname' => 'required|min:2', 'lastname' => 'required|min:2', ... ], $merge); } 

Creation Check:

 $validator = Validator::make($input, User::rules()); 

Check during upgrade:

 $validator = Validator::make($input, User::rules($id)); 

Check during upgrade with some additional rules:

 $extend_rules = [ 'password' => 'required|min:6|same:password_again', 'password_again' => 'required' ]; $validator = Validator::make($input, User::rules($id, $extend_rules)); 

Nice.

+23
Jun 13 '14 at 12:54 on
source share

Work as part of my question:

 public function update($id, $data) { $user = $this->findById($id); $user->fill($data); $this->validate($user->toArray(), $id); $user->save(); return $user; } public function validate($data, $id=null) { $rules = User::$rules; if ($id !== null) { $rules['username'] .= ",$id"; $rules['email'] .= ",$id"; } $validation = Validator::make($data, $rules); if ($validation->fails()) { throw new ValidationException($validation); } return true; } 

- this is what I did based on the accepted answer above.

EDIT: with form requests, everything is simplified:

 <?php namespace App\Http\Requests; class UpdateUserRequest extends Request { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'name' => 'required|unique:users,username,'.$this->id, 'email' => 'required|unique:users,email,'.$this->id, ]; } } 

You just need to pass UpdateUserRequest to your update method and do not forget to specify the POST identifier of the model.

+8
Mar 14 '14 at 13:33
source share

Laravel 5 compatible and general way:

I had the same problem and it was solved in a general way. If you create an element, it uses the default rules; if you update an element, it will check your rules for :unique and automatically add an exception (if necessary).

Create a BaseModel class and let all your models inherit from it:

 <?php namespace App; use Illuminate\Database\Eloquent\Model; class BaseModel extends Model { /** * The validation rules for this model * * @var array */ protected static $rules = []; /** * Return model validation rules * * @return array */ public static function getRules() { return static::$rules; } /** * Return model validation rules for an update * Add exception to :unique validations where necessary * That means: enforce unique if a unique field changed. * But relax unique if a unique field did not change * * @return array; */ public function getUpdateRules() { $updateRules = []; foreach(self::getRules() as $field => $rule) { $newRule = []; // Split rule up into parts $ruleParts = explode('|',$rule); // Check each part for unique foreach($ruleParts as $part) { if(strpos($part,'unique:') === 0) { // Check if field was unchanged if ( ! $this->isDirty($field)) { // Field did not change, make exception for this model $part = $part . ',' . $field . ',' . $this->getAttribute($field) . ',' . $field; } } // All other go directly back to the newRule Array $newRule[] = $part; } // Add newRule to updateRules $updateRules[$field] = join('|', $newRule); } return $updateRules; } } 

Now you define your rules in your model, as you are used to:

 protected static $rules = [ 'name' => 'required|alpha|unique:roles', 'displayName' => 'required|alpha_dash', 'permissions' => 'array', ]; 

And confirm them in your controller. If the model is not validated, it automatically redirects back to the form with the corresponding validation errors. If verification errors did not occur, it will continue to execute code after it.

 public function postCreate(Request $request) { // Validate $this->validate($request, Role::getRules()); // Validation successful -> create role Role::create($request->all()); return redirect()->route('admin.role.index'); } public function postEdit(Request $request, Role $role) { // Validate $this->validate($request, $role->getUpdateRules()); // Validation successful -> update role $role->update($request->input()); return redirect()->route('admin.role.index'); } 

What is this! :) Note that when creating, we call Role::getRules() , and when editing we call $role->getUpdateRules() .

+3
Jan 31 '15 at 17:38
source share

I have a BaseModel class, so I need something more general.

 //app/BaseModel.php public function rules() { return $rules = []; } public function isValid($id = '') { $validation = Validator::make($this->attributes, $this->rules($id)); if($validation->passes()) return true; $this->errors = $validation->messages(); return false; } 

In the user class, suppose I only need an email and a name to verify:

 //app/User.php //User extends BaseModel public function rules($id = '') { $rules = [ 'name' => 'required|min:3', 'email' => 'required|email|unique:users,email', 'password' => 'required|alpha_num|between:6,12', 'password_confirmation' => 'same:password|required|alpha_num|between:6,12', ]; if(!empty($id)) { $rules['email'].= ",$id"; unset($rules['password']); unset($rules['password_confirmation']); } return $rules; } 

I tested this with phpunit and worked great.

 //tests/models/UserTest.php public function testUpdateExistingUser() { $user = User::find(1); $result = $user->id; $this->assertEquals(true, $result); $user->name = 'test update'; $user->email = 'ddd@test.si'; $user->save(); $this->assertTrue($user->isValid($user->id), 'Expected to pass'); } 

I hope this helps someone, even if for a better idea. Thank you for sharing with you. (tested on Laravel 5.0)

+2
Jan 21 '15 at 19:11
source share

Simple role update example




 // model/User.php class User extends Eloquent { public static function rolesUpdate($id) { return array( 'username' => 'required|alpha_dash|unique:users,username,' . $id, 'email' => 'required|email|unique:users,email,'. $id, 'password' => 'between:4,11', ); } } 

.

 // controllers/UsersControllers.php class UsersController extends Controller { public function update($id) { $user = User::find($id); $validation = Validator::make($input, User::rolesUpdate($user->id)); if ($validation->passes()) { $user->update($input); return Redirect::route('admin.user.show', $id); } return Redirect::route('admin.user.edit', $id)->withInput()->withErrors($validation); } } 
+2
Mar 06 '15 at 19:16
source share

Unique validation using column id in Laravel

 'UserEmail'=>"required|email|unique:users,UserEmail,$userID,UserID" 
+2
Jan 16 '16 at 6:40
source share

I call different validation classes for Store and Update. In my case, I do not want to update all fields, so I have baseRules for common fields for Create and Edit. Add additional validation classes for each. Hope my example is helpful. I am using Laravel 4.

Model:

 public static $baseRules = array( 'first_name' => 'required', 'last_name' => 'required', 'description' => 'required', 'description2' => 'required', 'phone' => 'required | numeric', 'video_link' => 'required | url', 'video_title' => 'required | max:87', 'video_description' => 'required', 'sex' => 'in:M,F,B', 'title' => 'required' ); public static function validate($data) { $createRule = static::$baseRules; $createRule['email'] = 'required | email | unique:musicians'; $createRule['band'] = 'required | unique:musicians'; $createRule['style'] = 'required'; $createRule['instrument'] = 'required'; $createRule['myFile'] = 'required | image'; return Validator::make($data, $createRule); } public static function validateUpdate($data, $id) { $updateRule = static::$baseRules; $updateRule['email'] = 'required | email | unique:musicians,email,' . $id; $updateRule['band'] = 'required | unique:musicians,band,' . $id; return Validator::make($data, $updateRule); } 

controller: save method:

 public function store() { $myInput = Input::all(); $validation = Musician::validate($myInput); if($validation->fails()) { $key = "errorMusician"; return Redirect::to('musician/create') ->withErrors($validation, 'musicain') ->withInput(); } } 

Update method:

 public function update($id) { $myInput = Input::all(); $validation = Musician::validateUpdate($myInput, $id); if($validation->fails()) { $key = "error"; $message = $validation->messages(); return Redirect::to('musician/' . $id) ->withErrors($validation, 'musicain') ->withInput(); } } 
+1
Feb 04 '15 at 2:06
source share
 public static function custom_validation() { $rules = array('title' => 'required ','description' => 'required','status' => 'required',); $messages = array('title.required' => 'The Title must be required','status.required' => 'The Status must be required','description.required' => 'The Description must be required',); $validation = Validator::make(Input::all(), $rules, $messages); return $validation; } 
+1
Jul 07 '15 at 12:42
source share

I had the same problem. What I did: add to my hidden field with the model identifier and in the validator check the uniqueness only if I have some identifier from the view.

 $this->validate( $request, [ 'index' => implode('|', ['required', $request->input('id') ? '' : 'unique:members']), 'name' => 'required', 'surname' => 'required', ] ); 
+1
May 17 '16 at 15:41
source share
 'email' => [ 'required', Rule::exists('staff')->where(function ($query) { $query->where('account_id', 1); }), ], 'email' => [ 'required', Rule::unique('users')->ignore($user->id)->where(function ($query) { $query->where('account_id', 1); }) ], 
+1
Dec 14 '16 at 18:51
source share

You can try the code below

 return [ 'email' => 'required|email|max:255|unique:users,email,' .$this->get('id'), 'username' => 'required|alpha_dash|max:50|unique:users,username,'.$this->get('id'), 'password' => 'required|min:6', 'confirm-password' => 'required|same:password', ]; 
0
Jan 17 '17 at 8:52
source share

If you have another column that is used as a foreign key or index, then you should also indicate this in a rule like this.

 'phone' => [ "required", "phone", Rule::unique('shops')->ignore($shopId, 'id')->where(function ($query) { $query->where('user_id', Auth::id()); }), ], 
0
May 12 '17 at 10:39
source share



All Articles