Php 具有唯一属性验证规则的Laravel更新模型

Php 具有唯一属性验证规则的Laravel更新模型,php,laravel,validation,eloquent,Php,Laravel,Validation,Eloquent,我有一个laravel用户模型,它在用户名和电子邮件上有一个唯一的验证规则。在我的存储库中,当我更新模型时,我会重新验证字段,以便不出现所需规则验证的问题: public function update($id, $data) { $user = $this->findById($id); $user->fill($data); $this->validate($user->toArray()); $user->save();

我有一个laravel
用户
模型,它在
用户名
电子邮件
上有一个唯一的验证规则。在我的存储库中,当我更新模型时,我会重新验证字段,以便不出现所需规则验证的问题:

public function update($id, $data) {
    $user = $this->findById($id);
    $user->fill($data);
    $this->validate($user->toArray());
    $user->save();
    return $user;
}
这在测试中失败

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

有没有办法优雅地解决这个问题?

将当前正在更新的实例的
id
附加到验证器中

  • 传递实例的
    id
    ,以忽略唯一验证器

  • 在验证器中,使用参数检测您是否正在更新或创建资源

  • 如果正在更新,请强制唯一规则忽略给定id:

    //rules
    'email' => 'unique:users,email_address,' . $userId,
    
    如果要创建,请照常进行:

    //rules
    'email' => 'unique:users,email_address',
    

    根据我的问题:

    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;
    }
    
    这就是我所做的,基于上述公认的答案

    编辑:通过表单请求,一切都变得更简单:

    <?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,
            ];
        }
    }
    
    另一种优雅的方式

    在模型中,创建静态函数:

    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);
    }
    
    创建时的验证:

    $validator = Validator::make($input, User::rules());
    
    更新验证:

    $validator = Validator::make($input, User::rules($id));
    
    更新时的验证,以及一些附加规则:

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

    很好。

    我有BaseModel类,所以我需要更通用的东西

    //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;
    }
    
    在用户类中,假设我只需要验证电子邮件和名称:

    //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;
    }
    
    我用phpunit测试了这个,效果很好

    //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');
    
    }
    
    我希望能帮助别人,即使是为了得到更好的主意。谢谢分享你的。
    (在Laravel 5.0上测试)

    Laravel 5兼容和通用方式:

    <?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,
            ];
        }
    }
    
    我只是遇到了同样的问题,并以一种通用的方式解决了它。如果您创建一个项目,它将使用默认规则;如果您更新一个项目,它将检查您的规则是否为
    :unique
    ,并自动插入一个排除项(如果需要)

    创建一个
    BaseModel
    类,并让您的所有模型从中继承:

    <?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;
        }
    }    
    
    并在控制器中验证它们。如果模型未验证,它将自动重定向回带有相应验证错误的表单。如果没有发生验证错误,它将在验证后继续执行代码

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

    就这样!:)请注意,在创建时我们调用
    Role::getRules()
    ,在编辑时我们调用
    $Role->getUpdateRules()
    我调用不同的验证类来存储和更新。在我的例子中,我不想更新每个字段,所以我有创建和编辑公共字段的基本规则。为每个类添加额外的验证类。我希望我的例子有帮助。我用的是Laravel4

    型号:

    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);
    }
    
    控制器: 存储方法:

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

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

    角色更新的一个简单示例



    Laravel中具有不同列ID的唯一验证

    'UserEmail'=>"required|email|unique:users,UserEmail,$userID,UserID"
    
    我也有同样的问题。 我所做的:在我的视图隐藏字段中添加一个模型的id,并在验证器中检查唯一性,前提是我从视图中获得了一些id

    $this->validate(
            $request,
            [
                'index'       => implode('|', ['required', $request->input('id') ? '' : 'unique:members']),
                'name'        => 'required',
                'surname'     => 'required',
            ]
    );
    

    你可以试试下面的代码

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

    如果您有另一列用作外键或索引,那么您必须在规则中也这样指定它

    'phone' => [
                    "required",
                    "phone",
                    Rule::unique('shops')->ignore($shopId, 'id')->where(function ($query) {
                        $query->where('user_id', Auth::id());
                    }),
                ],
    

    或者您可以在您的申请表中做什么(对于Laravel 5.3+)


    我已经在Laravel 5.6中完成了这项工作,并且工作正常。

    对于自定义FormRequest和Laravel 5.7+您可以获得更新模型的id,如下所示:

    public function rules()
        {
            return [
                'name' => 'required|min:5|max:255|unique:schools,name,'.\Request::instance()->id
            ];
        }
    

    Laravel 5.8简单易用

    您可以在一个表单请求中完成这一切,非常好地

    首先创建一个字段,通过该字段可以以正常编辑形式传递id(不可见)。i、 e

     <div class="form-group d-none">
          <input class="form-control" name="id" type="text" value="{{ $example->id }}" >
     </div>
    
    。。。添加忽略当前id的唯一规则,如下所示:

    public function rules()
    {
        return [
              'example_field_1'  => ['required', Rule::unique('example_table')->ignore($this->id)],
              'example_field_2'  => 'required',
    
        ];
    
    。。。最后,在update方法中键入与store方法相同的hint表单请求,如下所示:

     public function update(ExampleValidation $request, Examle $example)
    {
        $example->example_field_1 = $request->example_field_1;
        ...
        $example->save();
    
        $message = "The aircraft was successully updated";
    
    
        return  back()->with('status', $message);
    
    
    }
    
    这样,您就不会对使用表单请求的任何人重复不必要的代码:-)

    就我而言,我尝试了以下所有方法,但均无效:

    $this->id
    $this->user->id
    $this->user

    这是因为我无法直接访问模型
    $id
    ,也无法访问
    $id

    因此,我从一个查询中获得了
    $id
    ,该查询使用了我试图验证的相同的
    unique
    字段:

        /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        $id = YourModel::where('unique_field',$this->request->get('unique_field'))->value('id');
        return [
            'unique_field' => ['rule1','rule2',Rule::unique('yourTable')->ignore($id)],
        ];
    }
    

    很不错的!为了让我的邮件以这种方式工作,我需要以下内容:
    “email”=>“required”| email | unique:member”。($id?”,id,$id:“)
    那么用户id与电子邮件地址相关吗?确定。假设,如果您正在更新已经存在的电子邮件地址,那么如何找到它?请查看此问题需要帮助@xcy7e웃: <代码>唯一:表,列,除了,idColumn
    它是否仅适用于
    唯一
    ,意味着我在
    lte
    中使用它作为
    “order”=>“lte:products,stock,2'
    但它不起作用为什么?你在哪里发布ID?它适用于laravel 5.3版本你应该在答案中格式化代码(我为你做了)。另外,一般来说,答案不应该只是没有注释的代码-您可能需要添加一个简短的解释,说明为什么这个代码对这种情况有帮助。您能再解释一下吗?只有这个代码对我有效。因为在MongoDB中,主列是
    \u id
    ,所以其他答案对我来说并不适用。
    public function rules()
        {
            return [
                'name' => 'required|min:5|max:255|unique:schools,name,'.\Request::instance()->id
            ];
        }
    
     <div class="form-group d-none">
          <input class="form-control" name="id" type="text" value="{{ $example->id }}" >
     </div>
    
    use Illuminate\Validation\Rule;
    
    public function rules()
    {
        return [
              'example_field_1'  => ['required', Rule::unique('example_table')->ignore($this->id)],
              'example_field_2'  => 'required',
    
        ];
    
     public function update(ExampleValidation $request, Examle $example)
    {
        $example->example_field_1 = $request->example_field_1;
        ...
        $example->save();
    
        $message = "The aircraft was successully updated";
    
    
        return  back()->with('status', $message);
    
    
    }
    
    public function rules()
    {
        if ($this->method() == 'PUT') {
            $post_id = $this->segment(3);
            $rules = [
                'post_title' => 'required|unique:posts,post_title,' . $post_id
            ];
        } else {
            $rules = [
                'post_title' => 'required|unique:posts,post_title'
            ];
        }
        return $rules;
    }
    
        /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        $id = YourModel::where('unique_field',$this->request->get('unique_field'))->value('id');
        return [
            'unique_field' => ['rule1','rule2',Rule::unique('yourTable')->ignore($id)],
        ];
    }