Entities¶
Models are defined as PHP classes declared within related .class.php
files.
The models defintions are located in the /packages/{package_name}/classes
folder of the package they relate to (see Directory Structure).
All classes inherit from a common Model
ancestor which is declared in the equal\orm
namespace and defined in the file /lib/equal/orm/Model.class.php
.
A class is always referred to as an entity and belongs to a specific package.
In turn, the packages and its subdirectories are used as namespace.
Example:
core\setting\SettingValue
Classes¶
A class consists of a series of fields definition, along with specific methods.
Classes definitions from a same package are placed into the folder /packages/{package_name}/classes/.
The structure of an entity is based on its fields, which are defined using descriptors in an associative array structure, return by a dedicated method getColumns()
.
Here below is an example of an hypothetical entitty named Category
.
<?php
// [...]
class Category extends Model {
public static function getColumns() {
return [
'name' => [
'type' => 'string',
'description' => "Name of the category (for all variants).",
'required' => true
],
'description' => [
'type' => 'string',
'description' => "A few details about category purpose and usage."
],
'product_models_ids' => [
'type' => 'many2many',
'foreign_object' => 'sale\catalog\ProductModel',
'foreign_field' => 'categories_ids',
'rel_table' => 'sale_product_rel_productmodel_category',
'rel_foreign_key' => 'productmodel_id',
'rel_local_key' => 'category_id',
'description' => 'List of product models assigned to the category.'
]
];
}
}
Entity storage¶
eQual uses an ObjectManager
service which implements the Active Record pattern.
It means that classes are mapped with database tables. Each table having structure (columns) matching the fields defined in the model.
Consistency between models (*.class.php
files) and database schema is done at package initialization (columns types must be compatible). It must be maintained manually in case of change.
When a new class is created or the schema of a class is modified, the SQL schema must be adapted consequently. Controllers core_init_package
and utils_sql-schema
are made to help with this task.
Also, Action controller core_test_package-consistency
can help to spot any incompatibility or inconsistency in the definition the classes from a given package.
Fields categories¶
CATEGORY | DESCRIPTION |
---|---|
simple | Direct field that holds a value of a given type. Such fields are stored as is in database and are automatically converted (SQL/PHP). |
relational | Field whose value targets one or more objects. Supported relations are : one2many , many2many , and many2one (which behaves like a simple field) |
computed | Indirect field that results from a computation based on other values. Computed fields have a final type that can be either simple or relational. |
Fields types ¶
The type property sets the type of the field.
The following types are supported by the ORM :
boolean
integer
float
string
date
time
datetime
binary
many2one
one2many
many2many
computed
alias
usage property
The usage
property is complementary to the type
property and allows to refine the way the field should be handled by the ORM and the DBMS, and how it should be rendered in the UI (see Usages).
boolean¶
Numeric value of Boolean type (true or false)
booleans notation
For booleans, we use the PHP built-in constant : true and false (when using '0', '1', 0 or 1, value is converted to a bool).
integer¶
Signed numeric value (negative or positive).
By default, strings are stored in DBMS using INT(11)
.
about integer precision
PHP depends on the platform for integers size allocation (PHP_INT_SIZE
), while SQL INT
will always be 32 bits.
float (double)¶
Floating point numeric value (float or double)
By default, strings are stored in DBMS using DECIMAL(10,2)
.
string¶
Short string without formatting/carriage returns (ex: lastname, place, …)
By default, strings are stored in DBMS using VARCHAR(255)
.
Strings can potentially be long and include formatting, and some behavior require a specific combination of type and usage.
For instance:
<?php
[
'type' => 'string',
'usage' => 'text/plain'
]
whereas :
<?php
[
'type' => 'string',
'usage' => 'phone'
]
VARCHAR(20)
.
text¶
@deprecated : use "string" with a "usage" set to "text/plain" (which default to a length of 65.000).
By default, texts are stored in DMBS as TEXT
. The TEXT data type has a size of string characters upto 65,535 bytes to manage classic long-form contents of the text.
binary¶
Any binary value (ex : a picture, a document, …)
Binary values can either be stored in the database or within the filesystem, under the /bin
folder of the installation.
This behavior can be set using the FILE_STORAGE_MODE
and FILE_STORAGE_DIR
configuration parameter.
file¶
A file value is stored in the DB (like binaries) as a LONGBLOB
.
date, time & datetime¶
When stored to the DBMS, those types follow the standard SQL format:
- date: YYYY-mm-dd
- time: HH:mm:ss
- datetime: YYYY-mm-dd HH:mm:ss
Datetime manipulations using eQual
Within PHP scripts, eQual handles dates, times and datetimes as UTC timestamps. These are adapted to SQL format or JSON format when needed.
many2one¶
Relational field used for fields holding a N-1 relation. The stored value is an integer that holds the identifier of the pointed object.
Example:
<?php
// from todolist\Task
// Each task has only 1 user assigned at a time
'user_id' => [
'type' => 'many2one',
'foreign_object' => 'todolist\User'
]
one2many¶
1-N relation, related to a many2one object
foreign_object ->
foreign_field -> select amongst fields of other class
+ ability to create a new one (many2many) :
temporary creation in one of both ways !
rel_table -> check amongst existing rel tables
({package}_rel_local_foreign or rel_foreign_local)
if no table yet -> create
rel_local_key -> {local_class}_id
rel_foreign_key -> {foreign_class}_id
<?php
// todolist\User
// Each user can have many tasks
'tasks_ids' => [
'type' => 'one2many',
'foreign_object' => 'todolist\Task',
'foreign_field' => 'user_id'
]
many2many¶
M-N relation
- foreign_object : allows to select amongst other classes + ability to create symmetrical one (one2many)
<?php // school\Teacher 'courses_ids' => [ 'type' => 'many2many', 'foreign_object' => 'school\Course', 'foreign_field' => 'teachers_ids', 'rel_table' => 'school_rel_course_teacher', 'rel_foreign_key' => 'course_id', 'rel_local_key' => 'teacher_id' ]
computed¶
Computed fields can be either computed synchronously when the field is requested, or on might are not stored in the DB, unless .
The latter is necessary in order to be able to create domains that use it. In that case, an additional attribute store
must be set to true.
To compute the resulting value, this type comes with 2 possible attributes :
- 'function' : either a string to a callable or a closure (ex.
function () { return time(); }
) - 'relation': an associative array describing the path to the resulting value (ex.
[ 'sale_entry_id' => 'vat_rate']
)
It will return a processed value and afterwards, it can be stored inside the DB.
Most of the time the use of the store attribute requires that field(s) on which depends the computed value, has an onchange event, triggering the upd**ate of the calculated field (see example).**
Within 'function' callbacks
Although it is possible, within a computed field callback, you should never call update()
on a collection involving the current entity (self
) since this would result in marking the object as an instance, even if it is still a draft (field state
).
Recap
- type: 'computed'
- result_type : any ORM supported type (i.e. : 'boolean', 'integer', 'float', 'string', 'many2one', 'many2many', 'one2many')
- 'function' | 'relation'
- store : boolean
When trying to load a computed field:
- if 'store' is not defined or set to false, it computes the value using the provided method each time the field value is requested
- if 'store' is set to true and the field isn't in the DB (NULL), it computes the value using the provided method
- if 'store' is set to true and the field is in the DB, it uses the DB value
<?php
// Example from core\Permission
// ...
'rights' => [
'type' => 'integer',
'onupdate' => 'onchangeRights'
],
'rights_txt' => [
'type' => 'computed',
'store' => true,
'result_type' => 'string',
'function' => 'calcRightsTxt'
],
// ...
public static function onupdateRights($om, $ids, $values, $lang) {
$om->update(__CLASS__, $oids, ['rights_txt' => null, $lang);
}
public static function calcRightsTxt($self) {
$res = [];
$values = $self->read('rights');
foreach($values as $id => $value) {
$rights_txt = [];
$rights = $value['rights'];
if($rights & EQ_R_CREATE) $rights_txt[] = 'create';
if($rights & EQ_R_READ) $rights_txt[] = 'read';
if($rights & EQ_R_WRITE) $rights_txt[] = 'write';
if($rights & EQ_R_DELETE) $rights_txt[] = 'delete';
if($rights & EQ_R_MANAGE) $rights_txt[] = 'manage';
$res[$id] = implode(', ', $rights_txt);
}
return $res;
}
Fields properties¶
Common properties¶
PROPERTY | DESCRIPTION |
---|---|
type | The type of field: one of the value listed as fields types. |
usage | (string) Specifies additional information about the format of the field (@see Usages for more details) |
description | (string) Brief about the field (max 65 chars). |
visible | (optional, boolean | array) Domain holding the conditions that must be met in order for the field to be relevant (and shown in UI). |
default | (optional) (mixed) Tells how to get the default value of the field. Can be either a value (of the same type than the one target by type ) or a callable (string). |
readonly | (boolean) Marks the field as non-editable (default = false). |
required | (boolean) Marks the field as mandatory (trying to store an object without a value for that field raises error) (default = false). |
multilang | (boolean) Marks the field as translatable (default = false). |
onupdate | (optional, string) Name of the method to invoke when field is updated. Format: package\Class::method Signature : public static function onupdateFieldName($orm, $oids, $values, $lang) {} |
domain | (only relational fields, array) Domain holding the additional conditions to apply on the set of objects targeted by the relation. |
dependencies | (optional, array) list of computed fields (relative to current object or through relations traversal) that must be reset when the value of the field (the property is part of) is updated. |
Type-related properties¶
TYPE | ATTRIBUTE | USAGE |
---|---|---|
string | ||
type | Type of the field. Accepted types are: alias, computed, many2one, many2many, one2many, integer, string, float, boolean, text, date and datetime. | |
multilang | (optional, boolean) Tells if field is translatable (default = false) | |
selection | (optional, array) Pre-defined list or associative array holding the possible values for the field. Example | |
alias | ||
alias | (string) Targets another field whose value must be returned when fetching the field value. By default, the name field is an alias for the id field. |
|
many2one | ||
foreign_object | Full name of the class toward which field is pointing back. | |
ondelete | (string ['null', 'cascade'])Tells how to behave when the parent object is deleted. cascade: delete the children too. null : void the pointer and keep the children. |
|
one2many | ||
foreign_object | (string) Class toward which current field is pointing back. | |
foreign_field | (string) Name of the field of the pointed class that is pointing back toward the current class. | |
foreign_key | (optional, string) Name of the field that serves as identifier for objects pointed by the relation (default = 'id') | |
order | (optional) Name of the field pointed objects must be sorted on. | |
sort | (optional, string) direction fort sorting (possible values: 'asc', 'desc') | |
ondetach | (string ['null', 'delete']) Tells how to handle the children objects when the relation is removed: either set the pointer to null or delete the children objects. (default = null) |
|
many2many | ||
foreign_object | Full name of the class the relation points to. | |
foreign_field | Name of the field of the pointed class that is pointing back toward the current class. | |
rel_table | Name of the SQL table dedicated to the m2m relation (recommended syntax: package_rel_class1_class2 ) |
|
rel_local_key | Name of the column in rel_table holding the identifier of the current object. |
|
rel_foreign_key | Name of the column in de rel_table holding the identifier of the pointed object. |
|
computed | ||
result_type | Specifies the type of the result returned by the field function, which can be any of the allowed types. | |
store | (optional) boolean telling if the result must be stored in database (in that case, the related table must contain a column for the field). By default, this attribute is set to false. | |
function | String holding the name of the method to invoke for computing the value of the field. Syntax: method (relative to current class) or package\Class::method (absolute notation) |
|
multilang | (optional, boolean) Flag telling if field can be translated (default value: false). This applies only for stored fields. | |
onrevert | (optional, string) Name of the method to invoke when field is reverted to NULL. |
Examples of fields using the selection
attribute:
<?php
'field_a' => [
'type' => 'string',
'selection' => [
'choice1',
'choice2',
'choice3'
]
],
// associative arrays are also allowed for assinging distinct labels on values
'field_b' => [
'type' => 'string',
'selection' => [
'choice1' => 'First choice',
'choice2' => 'Second choice',
'choice3' => 'Third choice'
]
]
Key-Value mapping
In case of an associative array, if keys are numeric values (i.e. something that can be converted to an integer), the map will be handled as an array when converted to JSON. Therefore, it is advised not to use numeric values for keys to avoid the UI mixing up values and labels.
System fields¶
Some fields are mandatory, and defined in the Model
class.
NAME | TYPE | ROLE |
---|---|---|
id | integer | Unique identifier of the object. |
name | string | Name to use when referring to the object (in views). By default, this field is an alias of id . |
status | string | 'draft', 'instance', 'archive'. |
created | datetime | Date at which the object was created. |
creator | foreign_object | core\User |
modified | datetime | Date date on which the object was last modified. |
modifier | foreign_object | core\User |
deleted | boolean | Marks the object as soft-deleted. |
About 'name field'
Le champ name peut être redéfini comme un alias ou un champ calculé.
optional system fields¶
Some fields are reserved but optional (with a convention of use):
- state : this field is used when a workflow applies on an entity.
- alert: this field is used in conjunction with the core\alert entities. If defined, it is expected to be a computed field.
Getters methods¶
NAME | DESCRIPTION |
---|---|
getName() | Get Model readable name. |
getDescription() | Get Model description. |
getType() | Provide the list of unique rules (array of combinations of fields). |
getLink() | Get the URL associated with the class. |
getColumns() | Returns the user-defined part of the schema (i.e. fields list with types and other attributes). |
getConstraints() | Returns a map of constraint items associating fields with validation functions. |
getUnique() | Provide the list of unique rules (list of arrays of combinations of fields). |
getFields() | Returns all fields names. |
getValues() | Returns values of static instance. |
getDefaults() | Return default values. |
getTable() | Return the name of the DB table to be used for storing objects of current class. |
getWorkflow() | Returns the workflow associated with the entity. For more details @see Workflows section. |
getRoles() | |
getActions() | |
getPolicies() |
Overridable methods¶
NAME | DESCRIPTION |
---|---|
cancreate() | Check wether an object can be created. |
oncreate() | Hook invoked after object creation for performing object-specific additional operations. |
canupdate() | Check wether an object can be updated. |
onupdate() | Hook invoked before object update for performing object-specific additional operations. |
candelete() | Check wether an object can be deleted. |
ondelete() | Hook invoked before object deletion for performing object-specific additional operations. |
canclone() | Check wether an object can be cloned. |
onclone() | Hook invoked after object cloning for performing object-specific additional operations. |
onchange() | Handler for providing consistency in model by reacting to front-end form edition (automatic changes). |