JeffreyWay / Laravel-Model-Validation

This Laravel package auto-validates a model on save.
https://packagist.org/packages/way/database
275 stars 53 forks source link

Trying to understand how to update #29

Open andyjessop opened 10 years ago

andyjessop commented 10 years ago

With this method, let's say you fill a update some fields on an entry, then call the save() method, the validation will run on all the fields currently set on the entry. But if the validation requires a field, say the email address, to be unique, the validation will fail because it already exists:

User.php:

protected static $rules = [
    'email' => 'required|email|unique:users',
    'firstname' => 'required',
    'lastname' => 'required'
];

Controller.php:

     // Get user from id
    $user = User::find($id);

    // Update user
    $user->update($data);

    // Validation when model is saved, via Way\Database\Model
    if ($user->save())
    {
            return Response::json([
                'data' => $user->toArray()
            ], 200);
    }

    if ($user->hasErrors())
    {
        return Response::json([
            'errors' => $user->getErrors()
        ]);
    }

Will return errors because the email address failed the validation. So, with this method, how do you tell the validator to ignore the unique rule for the email?

tomwalsh commented 9 years ago

There is a third field option on the unique validator that lets you specify an id to ignore in the validation check. So you can do something like:

$protected static $rules = array( 'email' => 'required|unique:users,email,{{$id}}');
steadweb commented 9 years ago

Adding {{$id}} to my ruleset doesn't seem to work. I've overridden the model to achieve this within a BaseModel class.

/**
 * Pre-process the rules by replacing instances of {{$id}} with the model id
 *
 * @return bool
 */
public function validate()
{
    foreach(static::$rules as $field => $rules) {
        $rules = explode('|', $rules);

        foreach($rules as &$rule) {
            $rule = explode(':', $rule);

            if($rule[0] == 'unique') {
                $rule[1] = str_replace('{{$id}}', $this->id, $rule[1]);
            }

            $rule = implode(':', $rule);
        }

        static::$rules[$field] = implode($rules, '|');
    }

    return parent::validate();
}
manuelro commented 9 years ago

I think what @tomwalsh wanted to say was that you have to append the id of the user to update. But to alter the rules you will have to copy the Laravel-Model-Validation Model file to your models folder and create a method to modify the static $rules array.

// in Models/Model.php
static function setRules($key = '', $value = '')
    {
        if(array_key_exists($key, static::$rules))
            static::$rules[$key] = $value;

        return static::$rules;
    }

Now in your update method (in your controller file) you'll have to alter the $rules array before it is used to validate the data:

// In Controllers/SomeController.php
public function update($accountId)
    {   
        Account::setRules('email', 'email|required|unique:accounts,email,'.$accountId);
        // Your logic here
    }      

That's it. You can now adapt your rules to any situation. I'll probably be pushing new changes to this repository sometime soon, I will include this proposal.

Also, check out Laravel Validation Docs (unique rule)