Data modeling - creating models

This tutorial shows how to create model classes and define data properties.

Creating models

Models are PHP classes which:

  • Are created inside a models directory or any sub directory of inside the model directory .
  • The PHP file name is exactly the same as the model class name (case sensitive).
  • Inherited from the Artaengine Model class directly or indirectly.
  • Call the constructor of class Model within their constructor and pass the model's group name as first argument.

If one of the above conditions are missing the class will not be considered as a data model.

This is a model:

<?php
/* ModelClassName.php */
class ModelClassName extends Model {

    public function __construct() {
        parent::__construct('modelGroupName');
    }
}

It is recommended to define the model constructors as shown below. Sometimes when model instances are created by Artaengine, a reserved first param is required to be sent to the Model constructor.

Models which are referenced in an array of model objects (defined as many) inside other models must have a constructor as below.

<?php
/* ModelClassName.php */
class ModelClassName extends Model {

    /* Artaengine 1.1.0 or higher */
    public function __construct($params=null) {
        parent::__construct('modelGroupName', $params);
    }
}

Properties

Private and protected properties are ignored by Artaengine. You can use them as you wish.

By convention a model has three types of public properties. This conventions are based on the property names:

  • These are properties which represent the data attributes of the model. All public properties which do not start and end with __ (two underlines) are considered as data attribute properties.
  • Properties which control model behavior. All properties with leading and trailing __ (two underlines) which are shown in the Model API are of this kind. Adding and setting them in a model class will effect the model behavior as described in the API.
  • Properties with leading and trailing __ (two underlines) which are do not exists in the Model API . This properties are ignored by Artaengine and can be defined by developers for any usage.

A model example:

/* ModelClassName.php */
class ModelClassName extends Model {

    /* ignored by Artaengine used by the programmer */

    private $prop1;
    private $prop2;

    protected $prop3;
    protected $prop4;

    public $__prop5__; // not defined in the Model API
    public $__prop6__; // not defined in the Model API


    /* directive properties - defined in the Model API */

    public $__table__  = 'mytable';
    public $__delete__ = false;

    /* data properties */

    public $id = array(
        'key'      => true,
        'label'    => 'ID',
    );

    public $name = array(
        'type'     => T::STRING,
        'len'      => 45,
        'required' => true,
        'label'    => 'Name',
    );

    public $create_date = array(
        'type'     => T::TIMESTAMP,
        'required' => true,
        'default'  => 'NOW()',
        'label'    => 'Create date-time',
    );

    /* methods */

    public function __construct($params=null) {
        parent::__construct('modelGroupName', $params);
    }
}

Data properties

The data properties can be three types:

  • Data attributes with types such as string, integer, boolean, ... . Always name scalar data properties with lower-case characters. The same property names are used in the database, so use names what are compatible with the database used.
  • Another model object. The model itself can not referenced.
  • An array of objects from another model. The model itself can not referenced.

Scalar simple data

Type of this properties can be any of: Artaengine data types .

Always name this properties as lowercase.

A definition array is assigned to this properties. Below are examples of scalar properties:

    public $id = array(
        'key'      => true,
        'label'    => 'ID',
    );

    public $name = array(
        'type'     => T::STRING,
        'len'      => 45,
        'required' => true,
        'label'    => 'Name',
    );

    public $email = array(
        'type'     => T::EMAIL,
        'required' => true,
        'unique'   => true,
        'label'    => 'Email',
    );

    public $password = array(
        'type'     => T::PASSWORD,
        'len'      => 100,
        'required' => true,
        'label'    => 'Password',
    );

Artaengines will make a field in the model table for this properties.

When a new model instance is created, this properties are set to null.

Model object

To reference another model object.

As it is not possible to assign an object to a class property in PHP, this properties are defined as:

  • The property name is exactly (case sensitive) the same as the model class-name it is referencing, and is set to null.
  • A string is assigned to a data property where the string is exactly (case sensitive) the same name as the model class-name it is referencing.

When a new model instance is created, this properties are set to an instance of the model class they are referencing.

    public $ModelName = null;

    public $optionalName = 'ModelName';

    public $UserGroup = null;

    public $group = 'UserGroup';

NOTE! A model class can not have an object of itself, this will crash the application.

To define configs on an model object data property define a property with the same name leaded and trailed by __ as:

    public $UserGroup     = null;
    public $__UserGroup__ = array(
        'required' => true,
        'label'    => 'User groups',
    );

Object list

To define a property which can be a list of another model objects then:

  • The property name is exactly (case sensitive) the same as the model class-name is is referencing and is assigned to an empty array.
  • An array which contains only a string is assigned to a data property where the string is exactly (case sensitive) the same as a model class-name.
    public $ModelName = array();

    public $optionalName = array('ModelName');

    public $UserPermissions = array();

    public $permissions = array('UserPermissions');

NOTE! A model class can not have an object list of itself, this will crash the application.

To define behaviors on an model object list data property define a property with the same name leaded and trailed by __.

Property definition

Below is all available keys that can be used to define a data property this keys can be used for all three types of data properties if they make sense:

public $dataproperty = array(
    'key'      => true/false default=false,
    'unique'   => true/false default=false,
    'type'     => Arta type constant,
    'unsigned' => true/false default=false,
    'len'      => number or array(minLen, maxLen),
    'min'      => number,
    'max'      => number,
    'required' => true/false default=false,
    'default'  => default value,
    'check'    => array(strict to this values),
    'options'  => array(selectable values),
    'multi'    => true/false default=false,
    'i18n'     => true/false default=false,
    'label'    => label string which will be translated by gettext,
    'unit'     => string,
    'present'  => true/false default=false,
    'widget'   => string or constant,
    'range'    => array(from, to),
    'crawl'    => number,
    'relation' => string,
    'midware'  => string,
    'regex'    => validation regular expression,
);

Descriptions:

Note: all values which can be words or sentences are translatable in a multi lingual application. Artaengine build-out takes care of fetching out this tokens and making them available for tools like poedit.

  • Each model needs a property marked as key to work properly. Unlike database tables you can not define models with more than one key, this was a feature in the early versions but eliminated for the sake of performance and simplicity.
        // auto-increment model key
        public $id = array(
            'key' => true,
        );
        // model key of type string
        public $id = array(
            'key' => true,
            'type' => T::STRING,
        );
        // model key of type long
        public $id = array(
            'key' => true,
            'type' => T::LONG,
        );
    
  • If True value of the property can not be duplicated.
  • Can by any of Artaengine data types .
        // stores string, validations check for email
        public $email = array(
            'type' => T::EMAIL,
        );
        // arrays can be stored and queried
        public $dic = array(
            'type' => T::DICTIONARY,
        );
        // stores text, CK editor is loaded in form as widget
        public $content = array(
            'type' => T::WYSIWYG,
        );
    
  • Only for number types. If true only unsigned values will be validated.
  • Only for string types.
        // maximum 45 characters
        public $name = array(
            'type' => T::STRING,
            'len'  => 45,
        );
        // minimum 5 and maximum 20 characters
        public $name = array(
            'type' => T::STRING,
            'len'  => array(5, 20),
        );
    
  • Only for numbers. Restrict number within a range for validating.
        public $age = array(
            'type' => T::SMALL,
            'min'  => 18,
            'max'  => 60,
        );
    
  • Data can not be stored into model unless property is not Null.
  • When storing data if property is Null sets value to this.
  • To define a list of available values, property values must be limited to this set.
        // accepts only Y OR N OR null
        public $active = array(
            'type'  => T::CHAR,
            'check' => array('Y', 'N'),
        );
        // accepts only Y OR N
        public $active = array(
            'type'     => T::CHAR,
            'required' => true,
            'check'    => array('Y', 'N'),
        );
    
  • To define a dictionary of available values. Unlike "check", property values are not restricted to this dictionary but form generators respect this dictionary and create a select widget from this dictionary.
        /* use options for forms */
        public $active = array(
            'type'   => T::CHAR,
            'opions' => array(
                'Y' => 'Yes',
                'N' => 'No',
            ),
        );
        /* restrict and use options for forms */
        public $active = array(
            'type'   => T::CHAR,
            'check'  => array('Y', 'N'),
            'opions' => array(
                'Y' => 'Yes',
                'N' => 'No',
            ),
        );
        /* The options can be from another class */
        public $btype = array(
            'type'   => T::CHAR,
            'opions' => 'Dics::btype',
        );
    
        /* Where the other class is in another file: */
    
    class Dics {
        static public $btype = array(
            'F' => 'Family house',
            'A' => 'Flat',
            'G' => 'Garage',
        );
    }
    
  • When property type is STRING, multi can be used with options to fake property accepting multi data. In a form such property would be presented by a multi select widget which lets selecting one or more items from option array. Data will be saved as a string of selected option keys separated by a coma. So data is stored as a string but presented as array in the model object.
        /* multi select widget will be shown data will be stored as 'F,G' */
        public $btype = array(
            'type'     => T::STRING,
            'multi'    => true,
            'required' => true,
            'default'  => 'F',
            'options'  => array(
                'F' => 'Family house',
                'A' => 'Flat',
                'G' => 'Garage',
            ),
        );
    
  • Marking a string type property with this key to True, makes the property multi lingual, meaning property data will be locale sensitive. If application is using 3 locales, 3 strings can be stored per locale in a record. add/update/query works based on current application locale or model locale.
  • Form and table generators or any other class uses the label when presenting property data. If no label is defined for scalar properties then property name will be used. If no label is defined for object and object list properties the label will be inherited from the object model.
  • Unit is a label which comes after the property values in forms, tables, ....
  • Only for non object properties. Properties marked with this key as True will be presenters of the model when model data is shown as an object in another model. For example if you have a model called 'User' which has an object property of a model called City, when you are showing a row of User, City data will be shown by City presenter properties values. A model can have more than one presenter. If not defined 'name' or 'description' or 'id' will be used by default.
  • Used by form generators when presenting property in a form. By default form generators choose the best available widget based on property definitions. To strictly assign a widget to a property set widget to this key. A widget value is a string that shows Widget class and method separated by :: or .
        public $mydata = array(
            'type'   => T::CHAR,
            'widget' => 'Widget::COMBO',
            'opions' => array(
                'Y' => 'Yes',
                'N' => 'No',
            ),
        );
        public $mydata = array(
            'type'   => T::CHAR,
            'widget' => 'Widget.COMBO',
            'opions' => array(
                'Y' => 'Yes',
                'N' => 'No',
            ),
        );
        // if Widget::COMBO = 'Widget.COMBO'; is a class constant
        public $mydata = array(
            'type'   => T::CHAR,
            'widget' => Widget::COMBO,
            'opions' => array(
                'Y' => 'Yes',
                'N' => 'No',
            ),
        );
    
  • Used by form generators if property data type contains date. To limit widget year range
        public $mydata = array(
            'type'  => T::DATE,
            'range' => array(2000, 2014),
        );
    
  • Available only for object properties. To enhancing performance. Normally object properties and their objects properties and so on will be objects of their models on and on. Sometimes there are plenty of inward objects which are not used at all. Set crawl to 0 if you never use a model object properties as an object. Set crawl to 1 if you use the first inward stage of model objects but not more.
  • Assign a Model class name to this. Unlike model object properties no model object will be created and assigned to this properties. This is useful when you need a relation but only for application internal uses and you must manually set the value to related model key value.
        /* to store which user created model data record manually
           not via forms
         */
        public $user_id = array(
            'relation' => 'User',
        );
    
  • Setting this key to a string of a model name will create a database foreign key relation to the table of the model. But at model and object level the property will only hold table ids (foreign key values) versus the model object property.
    To gain performance use this instead of defining object properties when you do not need to show a relation property in forms and tables and you only need the relation at database level and to set the id values manually.
  • A mid-ware for a model data property is a class which contains some methods that are responsible for altering property data before or after few events. Next tutorial will completely explain midwares.
        /* class Persian contains methods which are called before/after
           one or more Model add/update/delete/query/fetch to alter
           property data
         */
        public $text = array(
            'type'    => T::STRING,
            'midware' => 'Persian',
        );
    
  • Set to validate property data by a regular expression.
        // form validation will show an error if entered text violates regex 
        public $name = array(
            'type'     => T::STRING,
            'required' => true,
            'unique'   => true,
            'regex'    => '^[\u0600-\u06FF\x20\-0-9\(\)]*$',
            'label'    => 'Mountain name (Parsi)',
        );
    

To assign definition parameters to object properties define a directive/meta property and assign definition array to it:

public $Groups = null;
public $__Groups__ = array(
    'required' => true,
    'label'    => 'User group',
);

public $Permissions = array();
public $__Permissions__ = array(
    'label' => 'User permissions',
);

Below is an example for User/Group/Permission models:

class Permission extends Model {

    public $id = array(
        'key'      => true,
    );

    public $name = array(
        'type'     => T::STRING,
        'required' => true,
        'unique'   => true,
        'len'      => 20,
        'label'    => 'User permission',
    );

    public function __construct($params=null) {
        parent::__construct('mymodelgroup', $params);
    }
}
class Group extends Model {

    public $id = array(
        'key'      => true,
    );

    public $name = array(
        'type'     => T::STRING,
        'required' => true,
        'unique'   => true,
        'len'      => 20,
        'label'    => 'User group name',
    );

    public $Permission = array();

    public function __construct($params=null) {
        parent::__construct('mymodelgroup', $params);
    }
}
class user extends Model {

    public $id = array(
        'key'      => true,
    );

    public $Group = null;
    public $__GROUP__ = array(
        'required' => true,
    );

    public $name = array(
        'type'     => T::STRING,
        'required' => true,
        'len'      => 20,
        'label'    => 'Name',
    );

    public $email = array(
        'type'     => T::EMAIL,
        'required' => true,
        'unique'   => true,
        'label'    => 'email',
    );

    public $createtime = array(
        'type'     => T::TIMESTAMP,
        'required' => true,
        'default'  => 'NOW()',
        'label'    => 'Create time',
    );

    public $description = array(
        'type'     => T::TEXT,
        'label'    => 'Description',
    );

    public $active = array(
        'type'     => T::CHAR,
        'default'  => 'N',
        'check'    => array('Y', 'N'),
        'options'  => array(
            'Y' => 'Yes',
            'N' => 'No',
        ),
        'label'    => 'Active',
    );

    public function __construct($params=null) {
        parent::__construct('mymodelgroup', $params);
    }
}

Directive properties

Below is a list of reserved directive properties, value of this properties effect model behavior. Properties marked as read-only should not be set.

  • Unique model instance id.
  • Name of model key property. Database primary key.
  • Model key name as foreign key. This property is for internal usage and usefull when creating classes and plug-ins for models.
  • Name of the database table which model data is stored in. By default it will be set to "plural lower case model class name". To manually set database table name for a model set this property.
  • The database object which is used by the model to access database.
  • MySQL engine name. 'InnoDB' is default.
  • This property sets model delete behavior. By default it is True, meaning that when deleting a row of data it will be deleted from database. If set to False it means when deleting a row will only mark it as deleted. In the database the table created to hold model data will have a field called 'deleted' which is normally NULL, when deleting a row it will be set to current TIMESTAMP value. When querying model data, rows marked as deleted will be ignored.
  • Holds a list of model presenter property named. Classes such as Table and Forms(for creating widgets) use this list to present model rows when in different forms different properties will be presenting model data changing this property manually is the trick.
  • If application is multi lingual and model has i18n properties then value of this property shows the locale which model data are stored/queried under. By default set to locale selected by user.
    // manually change model locale
    $model->__locale__ = 'hu_HU';
    // set to more than one locale - model data for i18n properties
    // would be presented for this localess
    $model->__locale__ = array('hu_HU', 'en_EN');
    // set model locales to all available locales
    $model->__locale__ = Arta::$globals['locales'];
    
  • Holds property definitions. Useful when create adapter, warper or classes on models. Useful to hack property behavior by changing the definitions.
    __defs__ = array(
        0 => array( // scalar data properties
            'id' => array(
                'key'      => true,
                'label'    => 'ID',
            ),
            'name' => array(
                'type'     => T::STRING,
                'required' => true,
                'len'      => 20,
                'label'    => 'Name',
            ),
        ),
        1 => array( // model object data properties
            'Group'    => array(
                'required' => true,
                'class'    => 'UserGroup',
                'i'        => null,
                'fk'       => 'usergroup_id',
                'name'     => 'usergroup',
                'class_no' => 9,
                'table'    => 'usergroups',
            ),
        ),
        2 => array( // model object list data properties
            'Permission' => array(
                'class'    => 'Permission',
                'table'    => 'users_permissions',
                'junction' => null,
                'view'     => 'v_users_permissions_j',
                'obj'      => array(
                    'table' => 'permissions',
                    'pk'    => 'id',
                    'fk'    => 'permission_id',
                ),
                'i18n'      => false,
            ),
        ),
        3 => array( // scalar i18n data properties
            'description' => array(
                'type'     => T::INTEGER,
                'len'      => 50,
                'nullable' => false,
                'i18n'     => true,
                'label'    => 'User-group name',
                'present'  => true,
            ),
        ),
    );
    
  • To preserve performance. When fetching data and not using model object properties set this to True.
  • To preserve performance. When fetching data and not using model object list properties set this to True.
  • 'AND' or 'OR' operator used for queries. default is 'AND'.
  • To define SQL constraints on the model database table add constraints to this property as an array.
  • Last action which was done on data. 'Add', 'Delete' or 'Update'.
  • Sort list. Set sort using "sort()" method.
  • Array of condition strings and parameters. Set conditions using "where()" method.
  • If set to True "sort()" and "where()" should be supplied with database relation names instead of class and property names.
  • For special use only. To join model table with other tables to alter the result instead of using where. This is to gain better database performance.
  • When querying a page of data, this property would be an object of class Paging and can be used to render paging links.
  • Use "limit()" method to set limit.

Creating model instance

$user = new User();

// debug the model instance
I($user->id, $user->name, $user->Group);
// $user->id = null
// $user->name = null
// $user->Group = object of model Group

$group = new Group();

// debug the model instance
I($group->Permission);

After using a model for doing different tasks using method "reset()" takes it to create object state.

After creating or resetting a model non object properties are set to Null.

After creating or resetting a model object properties are set to their object. This applies too all objects of objects of ...

After creating or resetting a model list object properties are set to an empty array.

Methods

class User extends Model {

    public $id = array(
        'key'      => true,
    );

    public $first_name = array(
        'type'     => T::STRING,
        'required' => true,
        'len'      => 20,
        'label'    => 'First name',
    );

    public $family_name = array(
        'type'     => T::STRING,
        'required' => true,
        'len'      => 20,
        'label'    => 'Family name',
    );

    public $email = array(
        'type'     => T::EMAIL,
        'required' => true,
        'unique'   => true,
        'label'    => 'email',
    );

    public $createtime = array(
        'type'     => T::TIMESTAMP,
        'required' => true,
        'default'  => 'NOW()',
        'label'    => 'Create time',
    );

    public function __construct($params=null) {
        parent::__construct('modelGroupName', $params);
    }

    public function fullName() {
        return $this->first_name.' '.$this->family_name;
    }

    public function createDate() {
        $date = explode($this->createDate);
        return $date[0];
    }

    public static function colsAdd() {
        return array(
            'first_name',
            'family_name',
             'email',
       );
    }

    public static function colsUpdate() {
        return User::colsAdd();
    }

    public static function colsList() {
        return array(
            'id',
            'first_name',
            'family_name',
            'email',
            'createDate',
        );
     }

    public static function colsView() {
        return array(
            'id',
            'full_name',
            'email',
            'createtime',
       );
    }
}

Method return values can be used over data property values as shown in this example.

It is a good practice to define property lists for different actions such as forms and views inside the model by a static property or method (like in this example). Later on if model properties change there is no need to change codes outside the model.

Example

Models
TOP