The DataModel class is used to ensure the validity and integrity of data. This is done by defining a definition for each datum in the data model. The class ensures that any data defined by the definition conforms to its specified conditions, and when it does not it sets errors specific to each datum which can then be used to communicate what type of datum should have been passed.

Data models are primarily used to verify incoming user data from forms in the browser, but can also be used as contracts in class communications. The only thing necessary to implement a data model is defining the data models definition in protected $definition = Array().

Notice that data models can be accessed via object syntax and array access syntax, this is because the data model class implements \ArrayAccess and implements the necessary methods for object access.

Example #

Lets look at creating a DataModel and the available options that can be used. The keys of the definition are the datums/variables that the DataModel is in charge of validating, and each keys value is an array which defines the rules that should be applied to the datum/variable when verifying it.

namespace App\data_model;

class User extends \Disco\classes\DataModel {

    protected $definition = Array(
        'age' => Array(
            'type' => 'uint',
            'min' => 18,
            'max' => 100,
            'required' => true,
        ),
        'username' => Array(
            'type'      => 'string',
            'minlen'    => 2,
            'maxlen'    => 60,
            'required'  => true,
            'premassage' => 'alphaOnly',
        ),
        'email' => Array(
            'premassage' => 'trim',
            'method' => 'isEmail',
            'required'  => true,
        ),
        'password' => Array(
            'type' => 'string',
            'minlen' => 6,
            'required' => true,
        ),
        'password_verify' => Array(
            'method' => 'passwordVerify',
        )
    );


    public function alphaOnly($value){
        return preg_replace("/[^a-zA-Z0-9\s\_\-]/",' ',$value);
    }//alphaOnly


    public function isEmail($value){

        if(!$value || strpos($value,'@') === false){
            return false;
        }//if

        $pieces = explode('@',$value);
        if(!isset($pieces[0]) || !strlen($pieces[0]) || !isset($pieces[1]) || !strlen($pieces[1])){
            return false;
        }//if

        if(strlen($value) > 120){
            $this->definition['email']['error'] = 'Email must be less than 120 characters';
            return false;
        }//if

        return true;

    }//isEmail


    public function trim($value){
        return trim($value);
    }//trim


    public function passwordVerify($value){

        if(!$value || !isset($this['password']) || $value !== $this['password']){
            $this->definition['password_verify']['error'] = 'Passwords do not match!';
            return false;
        }//if

        return true;

    }//passwordVerify


}//User

Lets look at all the possible options available in the definition:

A definition is defined by a key, which is the name of the data/field, and an array which defines the conditions verifying the data/field.

Conditions used to define a data/field:

  • type : string (int, uint, float, ufloat, string, boolean, char, array, object, closure)
  • nullable : boolean (optional) Data can be null.
  • truthy : boolean (optional) Data can be a boolean.
  • in : array (optional) Data must be contained in this array.
  • notin : array (optional) Data must NOT be contained in this array.
  • instanceof : string (optional)(only applies to object) Data must be an instance of class.
  • max : numeric (optional)(only applies to int, uint, float, ufloat) Max value of data.
  • min : numeric (optional)(only applies to int, uint, float, ufloat) Min value of data.
  • maxlen : int (optional)(only applies to string) Max length of data.
  • minlen : int (optional)(only applies to string) Min length of data.

Conditions that when set will disregard all conditions listed above (aka are mutually exclusive with any other condition):

  • regexp : string A regular expression that the data must match, can be one of the default matching conditions provided by Disco.
  • method : string The name of a method of the class that is passed the value of the data and returns a boolean value indicating whether the data is valid.

Conditions that aren't mutually exclusive to any data type and can be used to augment a piece of data:

  • required : boolean Whether the data is required in the data model.
  • default : mixed A default value to give the data when its not set, mutually exclusive with required.
  • premassage : string The name of the method of the class that can massage the data value prior to validation.
  • postmassage : string The name of the method of the class that can massage the data value after successful validation.
  • error : string The error message returned when the data doesn't validate. If this is not set a custom one will be generated for you based on the conditions.

As you can see above we defined 5 different fields/datums that this model is in charge of containing and verifying; age, username, email, password, and password_verify.

Lets look at how this can be used in real code:

// Lets say we were creating the model from a posted form.
// You can pass in an array of fields to the DataModels constructor to set the values.
// Any data passed via the constructor whos key doesn't exist in the DataModels definition will be discarded.
$UserDataModel = new \App\data_model\User( \Data::post()->all() );

// Now lets verify the data
$verified = $UserDataModel->verify();

// If it didn't verify lets get the errors
if( !$verified ){

    // The errors are returned as an array where the set keys are the fields/datums that didn't verify and the value is a descriptive message specifying why the verification failed
    $errors = $UserDataModel->getErrors();

    // Now if we wanted to return the errors to the client for display we could set them in the sessions flash and redirect back to the form
    \Session::setFlash('user-errors', $errors);
    \View::redirect('/user-form');

    // Or you could return them as JSON
    \View::json( $errors );
}

The key take aways in the above example are that we:

  • Passed data to the DataModels constructor which set the data in the DataModel, discarding any fields which didn't exist in the DataModels definition.
  • Used the verify() method to validate the data as defined by the definition.
  • Got any validation errors using the getErrors() method.

Lets look at another example to see how we can use the methods verifyOnly(), verifySetData(), and verifyData() to validate only select fields:

$UserDataModel = new \App\data_model\User;

// Notice we can set fields by direct array access syntax
$UserDataModel['password'] = 'password';

// Or we can set fields using object syntax
$UserDataModel->password_verify = 'wontmatch';

// To verify only our password fields
if( !$UserDataModel->verifySetData() ){
    $errors = $UserDataModel->getErrors();
}

// Or
if( !$UserDataModel->verifyOnly(['password', 'password_verify']) ){
    $errors = $UserDataModel->getErrors();
}

// Or maybe you want to verify only a single field
if( !$UserDataModel->verifyData('password') ){
    $errors = $UserDataModel->getErrors();
}

// Its important if you are going to use more than one verification method in a row that you clear out the DataModels errors beforehand
$UserDataModel->clearErrors();

That wraps up a brief introduction to using DataModels to ensure data integrity. Be sure to read the full API docs (linked at the top of the page) for everything you can do with your DataModels.