ID controllers

It goes without saying that each record in the database table must be uniquely identified somehow. And sure, you can say that autoincrement id is traditional in our industry - it's such a classic. But it is certainly not the only way to identify records.

Again, let's take the articles from the sample application as an example. Specifically, the articles_localized entity, JetApplication\Content_Article_Localized class. If you look at its definition, you will find that there is no classic autoincrement id there. Identification consists of these two properties: namespace JetApplication;

use 
Jet\DataModel;
use 
Jet\DataModel_Definition;
use 
Jet\DataModel_Related_1toN;
use 
Jet\DataModel_IDController_Passive;

use 
Jet\Form_Definition;


#[
DataModel_Definition(
    
name'article_localized',
    
database_table_name'articles_localized',
    
id_controller_classDataModel_IDController_Passive::class,
    
parent_model_classContent_Article::class
)]
class 
Content_Article_Localized extends DataModel_Related_1toN
{

    #[
DataModel_Definition(
        
typeDataModel::TYPE_ID,
        
is_idtrue,
        
related_to'main.id',
        
do_not_exporttrue
    
)]
    protected 
string|null $article_id '';

    #[
DataModel_Definition(
        
typeDataModel::TYPE_LOCALE,
        
is_idtrue,
        
do_not_exporttrue
    
)]
    protected 
Locale|null $locale;

}

Thus, the identification of the record consists of the ID of the article to which its localized version belongs, and the locale the given mutation of the article is also marked as identification. Both properties have the is_id: true attribute. The $article_id property is then linked to the $id property of the main entity (attribute related_to: 'main.id' ) and is a text string (we'll talk about that later).

And if you look in your database at the structure of the articles_localized table, you'll see that it has a primary key over the columns that represent these two properties. Thus, the combination of these two data is a unique identification of the record.

I already mentioned that the article ID (that is, the $id entity article property in the JetApplication\Content_Article class) is not of type DataModel::TYPE_ID_AUTOINCREMENT, but of type DataModel::TYPE_ID, which represents some textual identifier. This is again a deviation from the most traditional way of working with autoincrement id.

Let's look at the definition of the article entity, the JetApplication\Content_Article class:

namespace JetApplication;

use 
Jet\DataModel;
use 
Jet\DataModel_Definition;
use 
Jet\DataModel_IDController_UniqueString;

#[
DataModel_Definition(
    
name'article',
    
database_table_name'articles',
    
id_controller_classDataModel_IDController_UniqueString::class,
    
id_controller_options: [
        
'id_property_name' => 'id'
    
]
)]
class 
Content_Article extends DataModel
{
    #[
DataModel_Definition(
        
typeDataModel::TYPE_ID,
        
is_idtrue
    
)]
    protected 
string $id '';
    
}

Why is it like this? Sometimes it can happen that you need to transfer records between several systems, or several instances of systems where each has its own database. For example, with articles, this is likely. And the numerical ID (although it's a really cool thing!) can collide in such a situation. Thus, in situations where we need the uniqueness (with a high probability) of an ID within several separate systems, it is appropriate to use identification using random strings.

Let's step away from the article example for a moment and go back to our old familiar classic... The autoincrement id that is used (again, I'll go to the demo app for a real example) for example for user accounts, let's say for admin: namespace JetApplication;

use 
Jet\Auth_User_Interface;
use 
Jet\DataModel;
use 
Jet\DataModel_Definition;
use 
Jet\DataModel_IDController_AutoIncrement;

#[
DataModel_Definition(
    
name'user',
    
database_table_name'users_administrators',
    
id_controller_classDataModel_IDController_AutoIncrement::class,
    
id_controller_options: ['id_property_name' => 'id']
)]
class 
Auth_Administrator_User extends DataModel implements Auth_User_Interface
{

    
/**
     * @var int
     */
    
#[DataModel_Definition(
        
typeDataModel::TYPE_ID_AUTOINCREMENT,
        
is_idtrue
    
)]
    protected 
int $id 0;

    
//... ... ...
}

So we have different situations and the ORM DataModel has to deal with it somehow. And to make matters worse, it is realistically possible that you will need a completely different way of identifying records for your applications. So how the record ID is operated cannot be fixed and must be a flexible system.

And that's why Jet DataModel has a controller ID system. The ID controller is a class that implements the logic that will be applied and work with record IDs. Several classes are already prepared by Jet DataModel, but nothing prevents you from creating your own ID controller.

As you already know from the chapter on definitions, it is necessary to define what ID controller the entity (class) uses. Going right back to the first example, the JetApplication\Content_Article_Localized class, you can notice this definition:

//... ... ...
#[DataModel_Definition(
    
//... ... ...
    
id_controller_classDataModel_IDController_Passive::class,
    
//... ... ...
)]
class 
Content_Article_Localized extends DataModel_Related_1toN {
    
//... ... ...
}

The JetApplication\Content_Article class has this definition:

//... ... ...
#[DataModel_Definition(
    
//... ... ...
    
id_controller_classDataModel_IDController_UniqueString::class,
    
id_controller_options: [
        
'id_property_name' => 'id'
    
]
)]
class 
Content_Article extends DataModel
{
    
//... ... ...
}

And the JetApplication\Auth_Administrator_User class this:

//... ... ... 
#[DataModel_Definition(
    
//... ... ... 
    
id_controller_classDataModel_IDController_AutoIncrement::class,
    
id_controller_options: ['id_property_name' => 'id']
)]
class 
Auth_Administrator_User extends DataModel implements Auth_User_Interface
{
    
//... ... ... 
}

So you can see that you only need to specify the class using the id_controller_class attribute and possibly the controller parameters using id_controller_options. The controller ID class used determines the entire behavior of the record identification logic.

What does the ID controller do?

It has already been said that the ID controller takes care of implementing the logic of work with record identification. But that's a pretty broad term, so let's talk about what it specifically means:

  • Operations when creating a record (saving a new record)
    For example, if the identifier is a classic autoincrement id, then after saving the record, it is necessary to get the value generated by the database from the backend and assign this value to the appropriate class properties. Or if it is a random text string, then it is necessary to generate it and pass the value to the property before saving the new record.
  • Record identification carrier
    As we have shown with the example of the language mutation of the article, the record does not necessarily have to be identified by one property, but by two or more properties. However, for the internal purposes of the DataModel, it is necessary to transfer the identification in a unified manner. No matter what type of identification it is and whether it consists of any number of properties, there must be a uniform way of transmitting this identification. And this carrier is precisely the specific instance of the ID controller tied to the specific instance of the entity (specific article, specific user, and so on).
  • The interconnectedness of internal relations
    Again, let's borrow the article as a prime example. You are saving a new article that has N locales. Article localizations are already created, even filled with text, the locale code is clear. But at the moment of creating a new article, its ID is not known at all. This is generated during saving (either before saving or after saving - depending on the controller ID type). And something has to make sure that the sub-entities know the ID of the main entity before they save. So, after saving the main record of the article, something has to be set to the localizations and what ID the new article has. And Jet DataModel does this automatically with the help of the ID controller system.

Pre-prepared ID controllers

ID controller Importance
Jet\DataModel_IDController_AutoIncrement A classic approach that assumes a numerical sequence generated by the database after the record is saved.
Jet\DataModel_IDController_UniqueString Generates a random string with a timestamp at the beginning before saving a new record.
Jet\DataModel_IDController_Passive Completely passive controller. It assumes that the application logic and/or the internal relation will set the necessary values for IDs.

Your own ID controller

Nothing prevents you from creating your own ID controller if the predefined ones are not enough for you. On the contrary, it is expected. The only condition is that it inherits from the Jet\DataModel_IDController class.

Previous chapter
Definition
Next chapter
Internal relation