Definition
Similar to, for example, the configuration system of the application , Jet DataModel also uses attributes (PHP 8), for which Jet has its own small facade Jet\Attributes .
Here too, definition attributes must have both the class representing a specific data entity and its properties, which represent (one could say) data columns. (Of course, a class can also have properties that have nothing to do with the DataModel and are intended, for example, only for the internal needs of the logic of the class).
In the case of Jet DataModel, the definition possibilities are of course much wider than in the case of the mentioned configuration system. But I have good news for you. Since the definition of data entities is actually the basic work in the design of the application, Jet Studio naturally has a tool where everything can be "clicked" and thus save a lot of time.
So it is not necessary to know all the definitions by heart. But it is good to know the principle of how it works and ideally to be able to do it without the Jet Studio tool.
Definition of a class (and entity as such)
The definition of the entity class itself is the very foundation and includes several attributes that must be defined. Here is an example of the definition of a very simple entity article (article) from a sample application, it is a JetApplication\Content_Article class.
mespace JetApplication;
use Jet\DataModel;
use Jet\DataModel_Definition;
use Jet\DataModel_IDController_UniqueString;
/**
*
*/
#[DataModel_Definition(
name: 'article',
database_table_name: 'articles',
id_controller_class: DataModel_IDController_UniqueString::class,
id_controller_options: [
'id_property_name' => 'id'
]
)]
class Content_Article extends DataModel
{
// ... ... ...
}
First of all, the entity must inherit from the class Jet\DataModel , or if it is an inner relation and therefore a sub-entity, then either from the class Jet\DataModel_Related_1toN , or Jet\DataModel_Related_1to1 , depending on which inner relation it is.
And now let's take a look at what attributes the class must have and what it can have.
name
This is a required attribute. Each data entity needs some internal naming independent of the class name . Naming is then used when referring to entities and their properties, for example when creating queries . The name does not have to be unique throughout your application. If there will be two classes inheriting from DataModel, they will have the same entity name, but in reality they will not "meet" so to speak, so this is not a problem. However, it is definitely recommended to create unique and concise names.
database_table_name
Again, this is a mandatory attribute. As the name suggests, this is how the name of the database table is defined, in which entity data will be physically stored using the backend . Or simply put: the name of the database table that belongs to the given class.
In practice, this will usually be the same value as the entity name, but it is not necessary. An entity can have a different name than a table. What is it good for? In certain situations, an entity can be stored in several different tables with different name, and only when the application is initialized you can choose which table the application will actually work with. But thanks to the fact that the entity name and not the table name is used in the queries , the application can remain constant and transparent - it is possible to change the table name (perhaps dynamically), but it is not necessary to modify the application (that is, to change the form of the queries).
id_controller_class
Also a mandatory attribute. ID controllers are a separate topic. So here only briefly, this property determines the class name of the used ID controller .
id_controller_options
Again context with a separate topic ID controllers are a separate topic. ID controller may require additional settings. In such a case, this attribute is mandatory.
parent_model_class
This attribute applies only to sub-entities, i.e. to internal relations and therefore to children of the class Jet\DataModel_Related_1toN , or Jet\DataModel_Related_1to1 . For subentities, the attribute is mandatory and determines which entity is directly superior to the given subentity - i.e. which self class is the parent.
Let's show it concretely again with a real example from the sample application. The article entity takes into account that it can have more locations . Thus, the main JetApplication\Content_Article class covers general things and aggregates sub-entities representing content already bound to specific locale. Such content is represented by the JetApplication\Content_Article_Localized.. class:
mespace JetApplication;
use Jet\DataModel;
use Jet\DataModel_Definition;
use Jet\DataModel_Related_1toN;
use Jet\DataModel_IDController_Passive;
#[DataModel_Definition(
name: 'article_localized',
database_table_name: 'articles_localized',
id_controller_class: DataModel_IDController_Passive::class,
parent_model_class: Content_Article::class
)]
class Content_Article_Localized extends DataModel_Related_1toN
{
//... ... ... ...
}
And as you can see, it is clearly defined what class is the parent entity.
default_order_by
This attribute applies only to a subentity in a 1:N internal relations . Defines the ordering of sub-entities when they are retrieved from the database. The sorting definition is the same as for general loading .
relation
It defines an external relation , i.e. a connection with another entity that is not subordinate to the given entity, which again calls itself a separate topic . The definition of an external relation is already a little more complicated, at least for an idea, let's show an example. Again, a real example from the sample application, linking a gallery and an image:
namespace JetApplication;
use Jet\DataModel;
use Jet\DataModel_Definition;
use Jet\DataModel_IDController_UniqueString;
#[DataModel_Definition(
name: 'gallery',
database_table_name: 'image_galleries',
id_controller_class: DataModel_IDController_UniqueString::class,
relationship: [
'related_to_class_name' => Content_Gallery_Image::class,
'join_by_properties' => ['id' => 'gallery_id'],
'join_type' => DataModel_Query::JOIN_TYPE_LEFT_OUTER_JOIN
]
)]
class Content_Gallery extends DataModel
{
//... ... ...
}
key
As we will show in a moment, it is possible to determine on the individual properties of the entity that these will be keys / database indexes. But sometimes you need to create a composite key . That is, a key / index made up of several properties (from the point of view of DB columns). Of course, this can also be defined, but again it is a separate topic .
Definition of class properties (and entity elements)
And now we will show the class property definitions. In practice, the definition takes the following form:
//... ... ...
class Content_Article extends DataModel
{
#[DataModel_Definition(
type: DataModel::TYPE_ID,
is_id: true
)]
protected string $id = '';
#[DataModel_Definition(
type: DataModel::TYPE_DATE_TIME,
)]
#[Form_Definition(
type: Form_Field::TYPE_DATE_TIME,
label: '',
error_messages: [
Form_Field::ERROR_CODE_INVALID_FORMAT => 'Invalid date format'
]
)]
protected ?Data_DateTime $date_time = null;
#[DataModel_Definition(
type: DataModel::TYPE_DATA_MODEL,
data_model_class: Content_Article_Localized::class
)]
#[Form_Definition(is_sub_forms:true)]
protected array $localized = [];
//... ... ...
//... ... ...
//... ... ...
}
As you can see, the example also includes form definitions (#[Form_Definition( ... )]), because in common practice no form is usually connected to the DataModel. But we will naturally be primarily interested in the DataModel definition, i.e. #[DataModel_Definition( ... )].
So let's show the possible attributes and their meaning again.
type
It can be said that it is a completely basic attribute determining the property type from the point of view of the DataModel definition and is therefore logically mandatory
List of types
Typee | Meaning of |
---|---|
DataModel::TYPE_ID | The text identifier of the record. That is, a unique text string. |
DataModel::TYPE_ID_AUTOINCREMENT | The numeric identifier of the record. So the good old familiar autoincrement number. |
DataModel::TYPE_STRING | A generic text string. The maximum text length must be specified by the max_len attribute |
DataModel::TYPE_BOOL | Logical yes / no. |
DataModel::TYPE_INT | Integer. |
DataModel::TYPE_FLOAT | Decimal. |
DataModel::TYPE_LOCALE | A text string representing locale code - will be stored in the database as a text locale code. However, within the class property, it is an instance of the Jet\Locale class. So, simply put: The locale code is stored in the database, but the value is basically handled like an instance of the Jet\Locale class. Conversion from/to string is done automatically by the backend . So, for example, the declaration should look like this:
#[DataModel_Definition(
|
DataModel::TYPE_DATE | The type represents a date without a time. This will correspond to the appropriate database type. However, within the class, the Jet\Data_DateTime instance will be operated with the indication that it is only a date without time data. So the declaration should look like this:
#[DataModel_Definition(
|
DataModel::TYPE_DATE_TIME | The type represents the date and time. This will again be matched by the respective database type. An instance of Jet\Data_DateTime will be operated within the class. So the declaration should look like this:
#[DataModel_Definition(
|
DataModel::TYPE_CUSTOM_DATA | Any data that will be serialized before being stored in the database and deserialized after loading (serialization and deserialization are handled automatically by the backend ). So, for example, it can be a field, an associated field, and the like. |
DataModel::TYPE_DATA_MODEL |
We've already come across the topic of the internal relations , which is self-contained. However, internal relations are also related to the definition of entity parameters. Let's go back to the article example from the sample application. As already said, the article has its localized versions. And these must be linked to the article itself (i.e. to the main entity) and this is done by using a property with this special type, which is used as follows:
/**
Thus, the definition also requires the data_model_class attribute, which specifies what class (i.e., the subentity definition) is bound to the property. |
max_len
Specifies the maximum length of a text string for the DataModel::TYPE_STRING type.
The attribute is mandatory and of course valid only for the given type.
data_model_class
See DataModel::TYPE_DATA_MODEL type.
The attribute is mandatory and of course valid only for the given type.
database_column_name
By default, the name of the column in the database table to which the property is mapped corresponds to the name of the property. However, if needed, it is optionally possible to specify a custom column name using this attribute.
is_id
Indicates whether the property is taken as an ID, or whether it is one of the properties that are taken as a record ID. You can read more in the chapter on ID controllers .
related_to
Here again we come to the topic of internal relations . We already said that a sub-entity must have a parent entity specified using the parent_model_class attribute. And the same goes for properties. A subentity must have a property (or properties) that bind it to the parent entity (or to the main entity - see the relevant chapter ). And it is therefore necessary to determine that this and that property is bound to a certain superior property.
But let's show it practically again. We return again to the sample application and its article entity. Specifically, to the localized versions of the article, i.e. to the articles_localized entity and the JetApplication\Content_Article_Localized class. The localized version of the article must have the ID of the article to which it belongs and also locale to which the text refers. And the ID of the article must be linked to the ID of the main entity:
#[DataModel_Definition(
type: DataModel::TYPE_ID,
is_id: true,
related_to: 'main.id',
do_not_export: true
)]
protected string|null $article_id = '';
The value of the related_to attribute has a simple structure: Before the dot is either the value of main or parent. Depending on whether it is a connection to the main entity (main) or a connection to a superior sub-entity (parent). After the dot is the property name of the related entity.
is_key
It indicates whether the property, more precisely the corresponding column of the database table, will be taken as a key / index. This is a simple, uncomplicated key. As already mentioned above, composite keys are defined differently . This is really just for defining a simple key.
key_type
See is_key. This attribute further defines what type the simple index / key should be.
Possible types:
Constant | Meaning of |
---|---|
DataModel::KEY_TYPE_INDEX | Common index (default value) |
DataModel::KEY_TYPE_PRIMARY | Primary key |
DataModel::KEY_TYPE_UNIQUE | Unique key |
do_not_export
Specifies that, for example, in the case of object serialization to JSON (or export to XML, etc.), the given property should not be exported.
What is it good for? For example, a user's password should never be exported anywhere, not even as a hash.
The export to JSON performed by Jet takes this into account automatically. If you are going to implement your own export mechanisms, it is definitely necessary to take this attribute into account.
backend_options
A special attribute allowing individual types of backends to pass special settings for a given property (from the backend's point of view for a given table column). Specific options depend on the specific backend. However, this is not standard practice and is not recommended. I'll leave any use up to you and your own research into how it works.