Trick: Validation within models.

Often when dealing with Eloquent models you will fall into the pattern of retrieving data from the user, validating it, filling the model and saving it.

This is perfectly fine, but can lead to a large amount of validation code within your routes and controllers. Let's have a look at how we can shift some of the validation logic into our models instead and clean up our routing. First we will need our Eloquent model.

class Ball extends Eloquent
{

}

Great! That's all we need, now lets add a new private property to the model called rules. This will store our validation rules array in the usual format.

class Ball extends Eloquent
{
    private $rules = array(
        'color' => 'required|alpha|min:3',
        'size'  => 'required',
        // .. more rules here ..
    );
}

Great! Now our rules are attached to our model, this means that we won't have to recreate the validation array each time we create a new model instance. Now we need a way of using these rules, I like to create a public validate() method, let's take a look..

class Ball extends Eloquent
{
    private $rules = array(
        'color' => 'required|alpha|min:3',
        'size'  => 'required',
        // .. more rules here ..
    );

    public function validate($data)
    {
        // make a new validator object
        $v = Validator::make($data, $this->rules);
        // return the result
        return $v->passes();
    }
}

Great! Now we can validate our models using the following syntax.

// get the POST data
$new = Input::all();

// create a new model instance
$b = new Ball();

// attempt validation
if ($b->validate($new))
{
    // success code
}
else
{
    // failure
}

That's a lot more compact, but we are missing something important. What about our validation errors? We will need those for our forms. Let's create a new private attribute to store them.

class Ball extends Eloquent
{
    private $rules = array(
        'color' => 'required|alpha|min:3',
        'size'  => 'required',
        // .. more rules here ..
    );

    private $errors;

    public function validate($data)
    {
        // make a new validator object
        $v = Validator::make($data, $this->rules);

        // check for failure
        if ($v->fails())
        {
            // set errors and return false
            $this->errors = $v->errors;
            return false;
        }

        // validation pass
        return true;
    }
}

Now we need a method to be able to retrieve the errors object.

class Ball extends Eloquent
{
    private $rules = array(
        'color' => 'required|alpha|min:3',
        'size'  => 'required',
        // .. more rules here ..
    );

    private $errors;

    public function validate($data)
    {
        // make a new validator object
        $v = Validator::make($data, $this->rules);

        // check for failure
        if ($v->fails())
        {
            // set errors and return false
            $this->errors = $v->errors;
            return false;
        }

        // validation pass
        return true;
    }

    public function errors()
    {
        return $this->errors;
    }
}

Great! Let's take a look at it in action.

// get the POST data
$new = Input::all();

// create a new model instance
$b = new Ball();

// attempt validation
if ($b->validate($new))
{
    // success code
}
else
{
    // failure, get errors
    $errors = $b->errors();
}

We now have a working validation model, but to use this same system across many models we would have to replicate the code across the other classes..

Dayle, surely there's a better way?

Do not fear! There is always a better way! Let's create a new base model with the code that we have already used. I like to call mine Elegant.

class Elegant extends Eloquent
{
    protected $rules = array();

    protected $errors;

    public function validate($data)
    {
        // make a new validator object
        $v = Validator::make($data, $this->rules);

        // check for failure
        if ($v->fails())
        {
            // set errors and return false
            $this->errors = $v->errors;
            return false;
        }

        // validation pass
        return true;
    }

    public function errors()
    {
        return $this->errors;
    }
}

Now we can have our models extend the base model Elegant instead of Eloquent, define a $rules array, and have access to all the validation features we had before..

class Ball extends Elegant
{
    protected $rules = array(
        'color' => 'required|alpha|min:3',
        'size'  => 'required',
        // .. more rules here ..
    );
}

That's all for now! Enjoy using your validation friendly Elegant models!