DataMapper


Validation

DataMapper comes with built in validation, borrowing some of the existing functionality from the CodeIgniter Form Validation library. In fact, the validation is quite similar so you'll have no problems picking it up if you're already familiar with it. However, there are enough differences that you should read on to take full advantage of it!

Note: validate() is automatically run whenever you perform a save().

Setting Validation Rules

DataMapper lets you set as many validation rules as you need for a given field, cascading them in order, and it even lets you prep and pre-process the field data at the same time. Let's see it in action, we'll explain it afterwards.

Using the Basic Template from the DataMapper Models page, create a User model and add this code just above the class constructor:

var $validation = array(
    array(
        'field' => 'username',
        'label' => 'Username',
        'rules' => array('required')
    ),
    array(
        'field' => 'password',
        'label' => 'Password',
        'rules' => array('required')
    ),
    array(
        'field' => 'email',
        'label' => 'Email Address',
        'rules' => array('required')
    )
);

Your model should now look like this:

In the above, we have specified that the username, password, and email fields are all required. When a developer attempts to save their user object to the database, these validation rules must be met in order for the save to be successful.

Also, you can now add validation rules for non-Database Table fields, such as 'Confirm Email Address' or 'Confirm Password'. For example:

var $validation = array(
    array(
        'field' => 'username',
        'label' => 'Username',
        'rules' => array('required')
    ),
    array(
        'field' => 'password',
        'label' => 'Password',
        'rules' => array('required', 'encrypt')
    ),
    array(
        'field' => 'confirm_password', // accessed via $this->confirm_password
        'label' => 'Confirm Password',
        'rules' => array('encrypt', 'matches' => 'password')
    ),
    array(
        'field' => 'email',
        'label' => 'Email Address',
        'rules' => array('required', 'valid_email')
    ),
    array(
        'field' => 'confirm_email', // accessed via $this->confirm_email
        'label' => 'Confirm Email Address',
        'rules' => array('matches' => 'email')
    )
);

Setting Related Validation Rules

DataMapper also lets you set validation rules for your relationships. Using save(), you can save both an object and its relationships at the same time. This is useful if you, for example, have a requirement that a User must relate to a Group. To validate this requirement, you would add rules for the Group relationship to the User $validation array in this way:

var $validation = array(
    array(
        'field' => 'username',
        'label' => 'Username',
        'rules' => array('required')
    ),
    array(
        'field' => 'password',
        'label' => 'Password',
        'rules' => array('required')
    ),
    array(
        'field' => 'email',
        'label' => 'Email Address',
        'rules' => array('required')
    ),
    array(
        'field' => 'group',
        'label' => 'Group',
        'rules' => array('required')
    )
);

Now, whenever you attempt to save a new User, you will only be able to successfully save it if you are also saving it with a Group relationship. If you are saving on an existing User, it will save if they are already related to a Group (otherwise you need to save with a Group relationship).

Cascading Rules

DataMapper lets you set multiple rules on each field. Let's try it. Change your $validation array like this:

var $validation = array(
    array(
        'field' => 'username',
        'label' => 'Username',
        'rules' => array('required', 'trim', 'unique', 'min_length' => 3, 'max_length' => 20)
    ),
    array(
        'field' => 'password',
        'label' => 'Password',
        'rules' => array('required', 'trim', 'min_length' => 3)
    ),
    array(
        'field' => 'email',
        'label' => 'Email Address',
        'rules' => array('required', 'trim', 'unique', 'valid_email')
    ),
    array(
        'field' => 'group',
        'label' => 'Group',
        'rules' => array('required')
    )
);

Now we have a mix of pre-processing and prepping validation functions.

Pre-Processing

A pre-processing validation function is one that returns TRUE or FALSE depending on the field's value. For example, the required function checks if the field value is empty. If it is, it will return FALSE meaning the field value has not met the validation rule.

Prepping

A prepping validation function is one that directly modifies the value of the field. For example, trim will remove any leading or trailing whitespace from the field value.

Custom Validation

You can create custom validation functions specific to the DataMapper model you put it in. For example, here is an encrypt function which we'll put in our User model to encrypt the password.

Encrypt (prepping example)

// Validation prepping function to encrypt passwords
function _encrypt($field) // optional second parameter is not used
{
    // Don't encrypt an empty string
    if (!empty($this->{$field}))
    {
        // Generate a random salt if empty
        if (empty($this->salt))
        {
            $this->salt = md5(uniqid(rand(), true));
        }

        $this->{$field} = sha1($this->salt . $this->{$field});
    }
}

Rules

There are important rules you need to be aware of when setting up your custom validation functions.

DataMapper's validate function ensures the validation rules are only applied to a field if it has changed since the last time validate ran. This prevents a field from having prepping functions applied to it multiple times, such as encryption, and the main reason why you should not call the actual validation functions directly. Calling an objects validate() function is all that's needed to have the validation rules applied. Note that validate is automatically run whenever you perform a save() call without parameters. You can also run or validate()->get() on an object to get a matching record using the objects current field values.


Anyway, back to putting in our custom encrypt function.

Add the encrypt function to your user model and the encrypt rule to the $validation array for the password field. Your model should now look like this:

Now if you were to do the following:

$u = new User();
$u->username = "foo";
$u->password = "bar";
$u->save();

You would have a new user named foo saved to the database, with an encrypted password!


Here is an example of a custom pre-processing function using a parameter:

Exact Length (pre-processing example)

// Validation prepping function to encrypt passwords
function _exact_length($field, $param)
{
    // Check if field value is the required length
    if (strlen($this->{$field}) == $param)
    {
        return TRUE;
    }

    // Field value is not the required length
    return FALSE;
}

And we would add it to the validation array like this:

$validation = array(
    array(
        'field' => 'word',
        'label' => 'Your Word',
        'rules' => array('required', 'trim', 'exact_length' => 10)
    )
);

Now if word is not exactly 10 characters in length, it will fail validation.

Note: The exact_length validation function is already included in DataMapper.

DataMapper's validate function ensures the validation rules are only applied to a field if it has changed since the last time validate ran. This prevents a field from having prepping functions applied to it multiple times, such as encryption, and the main reason why you should not call the actual validation functions directly. Calling an objects validate() function is all that's needed to have the validation rules applied. Note that validate is automatically run whenever you perform a save() call without parameters. You can also run or validate()->get() on an object to get a matching record using the objects current field values.

Custom Related Validation

You can create custom related validation functions specific to the DataMapper model you put it in. For example, here is a max_size function which we'll put in our Group model to restrict the size of each Group.

Max Size (pre-processing example)

// Checks if the value of a property is at most the maximum size.
function _related_max_size($object, $model, $param = 0)
{
    return ($this->_count_related($model, $object) > $size) ? FALSE : TRUE;
}

Note: The max_size related validation function is already included in DataMapper.

Rules

There are important rules you need to be aware of when setting up your custom validation functions.


Predefined Validation Functions

DataMapper lets you use any of the validation functions in the CodeIgniter Form Validation library, as well as any native PHP function that accepts one parameter.

As well as those, DataMapper provides a few extra validation functions.

Rule Parameter Description Example
alpha_dash_dot No Returns FALSE if the property contains anything other than alpha-numeric characters, underscores, dashes or full-stops.  
alpha_slash_dot No Returns FALSE if the property contains anything other than alpha-numeric characters, underscores, dashes, forward slashes or full-stops.  
unique No Returns FALSE if the property is not unique.  
unique_pair Yes Returns FALSE if the property, paired with another property, is not unique. 'unique_pair' => 'other_property'
min_size Yes Returns FALSE if the property is less than the specified size. 'min_size' => 1
max_size Yes Returns FALSE if the property is greater than the specified size. 'max_size' => 10
min_date Yes Returns FALSE if the property is less than the specified date. 'min_date' => '1950-10-15'
max_date Yes Returns FALSE if the property is greater than the specified date. 'max_date' => '2050-12-25'
valid_date No Returns FALSE if the property is not a valid date.  
valid_date_group Yes Returns FALSE if the property, grouped with other properties, is not a valid date. 'valid_date_group' => array('year' => 'property1', 'month' => 'property2', 'day' => 'property3')
valid_match Yes Returns FALSE if the property does not match one of the specified array values. 'valid_match' => array('value1', 'value2')

Any custom validation functions you would like to add, can be added to your DataMapper models, such as the example of the encrypt function.

Predefined Related Validation Functions

DataMapper has some specific validation rules used to validate relationships. These are:

Rule Parameter Description Example
required No Returns FALSE if the object is not being saved with a relationship and one does not already exist.  
min_size Yes Returns FALSE if the number of relationships is less than the specified size. 'min_size' => 1
max_size Yes Returns FALSE if the number of relationships is greater than the specified size. 'max_size' => 10

Any custom related validation functions you would like to add, can be added to your DataMapper models, such as the example of the max_size function above.

Error Messages

If any of the field values fail validation, the object will have its error property populated. You can view loop through and show each error in the error's all list, show the specific error for each field, or show all errors in one string. For example:

Viewing All Errors

foreach ($object->error->all as $e)
{
    echo $e . "<br />";
}

Viewing Specific Field Errors

echo $object->error->fieldname;
echo $object->error->otherfieldname;

Viewing All Errors as a Single String

echo $object->error->string;

The save function will return FALSE if validation fails, so if that happens you can check the error object for the errors.

Calling the validate() function will see a valid flag set to true or false. For example:

$this->validate();

if ($this->valid)
{
    // Validation Passed
}
else
{
    // Validation Failed
}

Setting Custom Error Messages

With the option of creating custom validation functions or having custom methods specific to each DataMapper model, you'll at one time or another want to raise an error message. To do this, you use the error_message() method. This method accepts accepts two parameters.

$field - This is the name by which you'll access the error in the error object.

$error - This is the error message itself.

Here is an example of setting a custom error message and accessing it.

$u = new User();

$u->error_message('custom', 'This is a custom error message.');

echo $u->error->custom;

Changing the Error Delimiters

By default, DataMapper adds a paragraph tag (<p>) around each individual error message. You can easily change these delimiters by setting the $error_prefix and $error_suffix class variables in your DataMapper model. For example, we'll set them in our User model: