Laravel update model with unique validation rule for attribute

后端 未结 18 2116
春和景丽
春和景丽 2020-11-27 12:45

I have a laravel User model which has a unique validation rule on username and email. In my Repository, when I update the model, I rev

相关标签:
18条回答
  • 2020-11-27 13:32

    I have BaseModel class, so I needed something more generic.

    //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 user class let's suppose I need only email and name to be validated:

    //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 works fine.

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

    I hope will help someone, even if for getting a better idea. Thanks for sharing yours as well. (tested on Laravel 5.0)

    0 讨论(0)
  • 2020-11-27 13:33

    If you have another column which is being used as foreign key or index then you have to specify that as well in the rule like this.

    'phone' => [
                    "required",
                    "phone",
                    Rule::unique('shops')->ignore($shopId, 'id')->where(function ($query) {
                        $query->where('user_id', Auth::id());
                    }),
                ],
    
    0 讨论(0)
  • 2020-11-27 13:34

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

    is what I did, based on the accepted answer above.

    EDIT: With Form Requests, everything is made simpler:

    <?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 the UpdateUserRequest to your update method, and be sure to POST the model id.

    0 讨论(0)
  • 2020-11-27 13:34

    or what you could do in your Form Request is (for Laravel 5.3+)

    public function rules()
        {
            return [
    
                'email' => 'required|email|unique:users,email,'.$this->user, //here user is users/{user} from resource's route url
                   ];
        }
    

    i've done it in Laravel 5.6 and it worked.

    0 讨论(0)
  • 2020-11-27 13:35

    Append the id of the instance currently being updated to the validator.

    1. Pass the id of your instance to ignore the unique validator.

    2. 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:

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

    If creating, proceed as usual:

    //rules
    'email' => 'unique:users,email_address',
    
    0 讨论(0)
  • 2020-11-27 13:35
    '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);
        })
    ],
    
    0 讨论(0)
提交回复
热议问题