简体   繁体   中英

Laravel validation service with unique fields

I'm using a validation service to validate user submitted form input (something along the lines of: http://laravel.io/bin/vrk ).

Using this approach (validation service classes) to validate user submitted form data against a set of rules, how can I validate user submitted data when rules have a unique rule. For example, if a user has the username of John then when I try to update the model validation fails (because John exists as a username, even though it belongs to the current model).

To solve this in Laravel I can do something like 'username' => 'required|alpha_dash|unique:users,username'.$id . How should I modify my current code, in the link, to best accommodate this? Should I have separate validator classes depending on the scenario (for example, UserCreateValidator, UserUpdateValidator, etc). Or should I do something like create separate validation rules in UserValidator class and pass which rule I want as an argument to either the constructor or the passes() method when calling UserValidator?

Well, what are you doing on post?

Because this is what you should be doing:

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

$user->username = $input['username'];
$user->email = $input['email'];

$user->save();

To update a record.

Or

$input = array('username' => 'w0rldart', 'email' => 'hahafu@dumbledore.com');
// Retrieve the user by the attributes, or create it if it doesn't exist, 
// based on the data above, which can come from an Input::all();
$user = User::firstOrCreate($input);

... many possibilities. But you could also do:

$input = array_forget($input, 'username');

To comply with your case, by removing the username index from the input array.

This is all I call tell you, based on the information you gave us. If you want more, post the controller's put method.

Update:

Here's my version of your PUT method: http://laravel.io/bin/OaX

I really think that try catch syntax is useless, since it's obvious that a User model will always be there. But I still don't know what you're trying to update. Even though I can't test it right now, I don't think that updating should be giving that problem, and if it does, retrieve user by username/id then unset the username index in your input array, and update it according to your specifications.

I think you could do something like this

First update UserValidator rules like this.

class UserValidator extends Validator {

    // Override parent class $rules
    protected $rules = [
        'default' => [
            'username' => 'required|alpha_dash|unique:users',
            'password' => 'required|between:6,16|confirmed',
            'password_confirmation' => 'required|between:6,16'
        ],
        'update' => [
            'username' => null,
        ]
    ];
} 

Then modify Validator 's passes method like this

public function passes($rule = null) {

    $rules = $this->rules['default'];
    if ($rule && isset($this->rules[$rule])) {
        $rules = array_merge($rules, $this->rules[$rule]);
    }

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

    if ($validator->fails()) {
        $this->validator = $validator;
        return false;
    }

    return true;
}

Then in your controller's PUT method, this will merge update rules to default rules

$rule = 'update';

// user has changed his username
if ($input['username'] !== $old_username) {
    $rule = 'create'; // validate uniqueness
}

else {
    unset($input['username']); // remove it, we don't validate it anymore since it's the same
}

$validator->passes($rule); // override 'default' rules with 'update' rules

You don't have to change your controller's POST method, it'll stay the same

$validator->passes(); // use 'default' rules

If I'm understanding right, you have issues updateng data because of primary key constraints on your model. What you need to do is to create 2 sets of rules, one for insert, and one for update.

Asuming you have a set of rules like this:

protected $rules = [
'id' => 'required|unique:users'
]

You should implement something like this:

protected $rules = [
'id' => 'required|unique|unique:users,id,' . $this->id
];

This should tell laravel to ignore the duplicate id in the table users for the specified id, in this case, the id for the current object.

You can read more about this on laravel's documentation at http://laravel.com/docs/validation

unique:table,column,except,idColumn

The field under validation must be unique on a given database table. If the column option is not specified, the field name will be used.

A little modification in UserValidator class

class UserValidator extends Validator {

    // Override parent class $rules
    protected $rules = [
        'username' => 'required|alpha_dash|unique:users',
        'password' => 'required|between:6,16|confirmed',
        'password_confirmation' => 'required|between:6,16'
    ];

    // ADD THIS
    public function __construct(Array $rules = array())
    {
        parent::__construct();
        if(count($rules)){
            foreach($rules as $k => $v) $this->rules[$k] = $v;
        }
    }
}    

In your controller putUpdate method

$user = User::whereUsername($username)->firstOrFail();
$rules = ['username' => 'required|alpha_dash|unique:users,username,'. $user->id];
// Pass the rule to update the rule for username in this method
$validator = \Services\Validators\UserValidator(Input::all(), $rules);

Check the manual here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM