Computed Fields¶
Computed fields allow you to dynamically derive values based on other data in the model. These values can either be computed synchronously at runtime (when the field is accessed) or precomputed and stored in the database.
When to Use Stored Computed Fields¶
Storing the value in the database is necessary when the field needs to be:
- Used in domains or filtering conditions
- Indexed for performance
- Queried without triggering computation
To store computed values, set:
'store' => true
Defining Computed Fields¶
Using function¶
A callable used to compute the value. It can be:
A string referencing a method name:
<?php
'rights_txt' => [
'type' => 'computed',
'store' => true,
'result_type' => 'string',
'function' => 'calcRightsTxt'
]
An anonymous function (closure):
<?php
'timestamp' => [
'type' => 'computed',
'result_type' => 'integer',
'function' => function() { return time(); }
]
It is strongly recommended to define named methods with protected scope so they:
- Remain inaccessible from outside the class, preserving encapsulation
- Are still available to child classes, enabling controlled inheritance
Using relation¶
An associative array describing a path of relations to follow to retrieve the value from a related entity:
<?php
'vat_rate' => [
'type' => 'computed',
'result_type' => 'float',
'relation' => ['sale_entry_id' => 'vat_rate']
]
This follows the relation to sale_entry_id, then retrieves the vat_rate field from that related object.
Properties¶
| Attribute | Type | Description |
|---|---|---|
| type | string |
(required)Type of the computed result (boolean, integer, float, string, many2one, etc.). |
| description | string |
Field description. |
| help | string |
Help text. |
| visible | boolean |
UI visibility. |
| default | mixed |
Default value. |
| dependents | array |
List of computed fields to reset when this field is updated. |
| readonly | boolean |
Non-editable. |
| deprecated | boolean |
Deprecated field. |
| result_type | string |
(required) Type of the computed result. |
| usage | string |
Format information. |
| function | string | callable |
(required) Method name or closure for computing the value. |
| relation | array |
(required) Path of relations to follow for retrieving the value. |
| onupdate | string |
Method to call on update. |
| onrevert | string |
Method to invoke when the field is reverted to NULL. |
| store | boolean |
Whether the computed value is stored (default: false). |
| instant | boolean |
Whether the value is computed instantly. |
| multilang | boolean |
Whether the field is translatable (only for stored fields). |
| domain | array |
Additional conditions for relational field targets. |
| selection | array |
Pre-defined list of possible values. |
| foreign_object | string |
Target class name (if applicable). |
Computation Behavior¶
When loading a computed field:
- If
storeis not defined orfalse: computes the value using the provided method each time - If
storeistrueand the field isNULLin database: computes and stores the value - If
storeistrueand the field has a database value: uses the stored value
Updating Stored Computed Fields¶
When using the store attribute, you must typically define an onchange event (or use the dependents property) on the field(s) that influence the computed value. This ensures the computed field is recalculated when its dependencies change.
Using dependents¶
The dependents property lists computed fields that should be reset when a field is updated:
<?php
'rights' => [
'type' => 'integer',
'dependents' => ['rights_txt']
],
'rights_txt' => [
'type' => 'computed',
'store' => true,
'result_type' => 'string',
'function' => 'calcRightsTxt'
]
Complete Example¶
<?php
namespace core;
use equal\orm\Model;
class Permission extends Model {
public static function getColumns() {
return [
'rights' => [
'type' => 'integer',
'dependents' => ['rights_txt']
],
'rights_txt' => [
'type' => 'computed',
'store' => true,
'result_type' => 'string',
'function' => 'calcRightsTxt'
]
];
}
protected static function calcRightsTxt($self) {
$result = [];
$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';
$result[$id] = implode(', ', $rights_txt);
}
return $result;
}
}
Best Practices¶
Avoid Calling update() in Callbacks
Within computed field callbacks, you should never call update() on a collection involving the current entity (self). This would mark the object as an instance even if it is still a draft.
Method Scope Guidelines:
- Use
protectedscope for computation methods to allow inheritance while preventing external access - Use meaningful method names that reflect the computation purpose (e.g.,
calcRightsTxt,computeTotal) - Keep computation logic focused and avoid side effects