ID kontrolery
Že každý záznam v databázové tabulce je nutné nějak jednoznačně identifikovat určitě není třeba říkat. A jistě, dá se říct, že v našem oboru je tradiční autoincrement id - je to taková klasika. Ale rozhodně to není jediný způsob jak záznamy identifikovat.
Opět si jako příklad vezmeme články z ukázkové aplikace. Konkrétně entitu articles_localized, třídu JetApplication\Content_Article_Localized. Když se kouknete na její definici, tak zjistíte, že žádné klasické autoincrement id tam není. Identifikaci tvoří tyto dvě vlastnosti:
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_class: DataModel_IDController_Passive::class,
parent_model_class: Content_Article::class
)]
class Content_Article_Localized extends DataModel_Related_1toN
{
#[DataModel_Definition(
type: DataModel::TYPE_ID,
is_id: true,
related_to: 'main.id',
do_not_export: true
)]
protected string|null $article_id = '';
#[DataModel_Definition(
type: DataModel::TYPE_LOCALE,
is_id: true,
do_not_export: true
)]
protected Locale|null $locale;
}
Tedy identifikaci záznamu tvoří ID článku ke kterému náleží jeho lokalizovaná verze a jako identifikace je rovněž označena lokalizace dané mutace článku. U obou vlastností je atribut is_id: true. Vlastnost $article_id je pak navázána na vlastnost $id hlavní entity (atribut related_to: 'main.id' ) a jedná se textový řetězec (o tom si něco povíme později).
A pokud kouknete do vaší databáze na strukturu tabulky articles_localized, tak uvidíte, že má primární klíč nad sloupci, které reprezentují tyto dvě vlastnosti. Tedy kombinace těchto dvou údajů je jednoznačná identifikace záznamu.
Již jsem zmínil, že ID článku (tedy vlastnosti $id entity article ve třídě JetApplication\Content_Article) není typu DataModel::TYPE_ID_AUTOINCREMENT, ale typu DataModel::TYPE_ID, což reprezentuje nějaký textový identifikátor. To jest opět odchylka od nejtradičnějšího způsobu práce s pomocí autoincrement id.
Koukněme se na definici antiti article, třídu JetApplication\Content_Article:
namespace 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
{
#[DataModel_Definition(
type: DataModel::TYPE_ID,
is_id: true
)]
protected string $id = '';
}
Proč to tak je? Někdy se může stát, že potřebujete záznamy přenášet mezi několika systémy, nebo několika instancemi systémů, kde každý má svou databázi. Například u článků je to pravděpodobné. A číselné ID (ač je to fakt super věc!) může v takové situaci kolidovat. Tedy v situacích kdy potřebujeme unikátnost (s velkou pravděpodobností) ID v rámci několika oddělených systémů je vhodné použít právě identifikaci pomocí náhodných řetězců.
Pojďme na chvíli pryč od příkladu s článkem a vraťme se k naší staré známé klasice ... Autoincrement id, které je použito (opět sáhnu do ukázkové aplikace pro reálný příklad) například pro uživatelské účty, dejme tomu pro administrátora:
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_class: DataModel_IDController_AutoIncrement::class,
id_controller_options: ['id_property_name' => 'id']
)]
class Auth_Administrator_User extends DataModel implements Auth_User_Interface
{
/**
* @var int
*/
#[DataModel_Definition(
type: DataModel::TYPE_ID_AUTOINCREMENT,
is_id: true
)]
protected int $id = 0;
//... ... ...
}
Tak tedy máme různé situace a ORM DataModel si s tím musí nějak poradit. A aby toho nebylo málo, tak je reálně možné, že vy budete potřebovat pro vaše aplikace ještě úplně jiný způsob identifikace záznamů. Tedy to jak se s ID záznamu operuje nesmí být pevně dáno a musí to být flexibilní systém.
A právě proto má Jet DataModel systém ID kontrolerů. ID kontroler je třída, která implementuje logiku pomocí která bude aplikována a práci s ID záznamů. Několik tříd má Jet DataModel již připraveno, ale vůbec nic vám nebrání vytvořit si vlastní ID kontroler.
Jak již víte z kapitoly o definicích, tak to jaký ID kontroler entita (třída) používá je nutné definovat. Když se vrátíme hned k prvnímu příkladu a to třídě JetApplication\Content_Article_Localized, tak si můžete všimnout této definice:
//... ... ...
#[DataModel_Definition(
//... ... ...
id_controller_class: DataModel_IDController_Passive::class,
//... ... ...
)]
class Content_Article_Localized extends DataModel_Related_1toN {
//... ... ...
}
Třída JetApplication\Content_Article má tuto definici:
//... ... ...
#[DataModel_Definition(
//... ... ...
id_controller_class: DataModel_IDController_UniqueString::class,
id_controller_options: [
'id_property_name' => 'id'
]
)]
class Content_Article extends DataModel
{
//... ... ...
}
A třída JetApplication\Auth_Administrator_User tuto:
//... ... ...
#[DataModel_Definition(
//... ... ...
id_controller_class: DataModel_IDController_AutoIncrement::class,
id_controller_options: ['id_property_name' => 'id']
)]
class Auth_Administrator_User extends DataModel implements Auth_User_Interface
{
//... ... ...
}
Tedy vidíte, že stačí pouze určovat třídu pomocí atributu id_controller_class a případně parametry kontroleru pomocí id_controller_options. Právě použitá třída ID kontroleru určuje celé chování logiky identifikace záznamu.
Co ID kontroler dělá?
Již bylo řečeno, že ID kontroler se stará o implementaci logiky práce s identifikací záznamu. Ale to je dost široký pojem, tak si řekněme co konkrétně to znamená:
- Operace při vytváření záznamu (ukládání nového záznamu)
Například pokud je identrifikátor klasické autoincrement id, pak je nutné po uložení záznamu získat od backendu hodnotu vygenerovanou databází a tuto hodnotu přidělit příslušné vlastnosti třídy. Nebo pokud se jedná o náhodný textový řetězec, tak je nutné jej vygenerovat a hodnotu předat vlastnosti ještě před uložením nového záznamu. - Nosič identifikace záznamu
Jak jsme si ukázali na příkladu jazykové mutace článku, tak záznam nemusí být nutné identifikován jednou vlastností, ale dvěma i více vlastnostmi. Ovšem pro interní účely DataModel je nutné přenášet identifikaci unifikovaně. Ať je identifikace jakéhokoliv typu a ať je tvořena libovolným počtem vlastností, tak musí existovat jednotný způsob přenášení této identifikace. A tímto nosičem je právě konkrétní instance ID kontroleru vázaná na konkrétní instanci entity (konkrétního článku, konkrétního uživatele a tak dále). - Provázanost vnitřních relací
Opět si půjčme článek jako ukázkový příklad. Ukládáte nový článek co má N lokalizací. Lokalizace článku jsou již vytvořené, dokonce naplněné textem, je jasný kód lokalizace. Ale v momentě zakládání nového článku vůbec není známo jeho ID. To se vygeneruje při ukládání (buď před uložením, nebo po uložení - dle typu ID kontroleru). A něco se musí postarat o to, aby subentity znaly před svým uložením ID hlavní entity. Tedy něco musí po uložení hlavního záznamu článku nastavit lokalizacím i to jaký má nový článek ID. A i to dělá Jet DataModel automaticky s dopomocí systému ID kontrolerů.
Předpřipravené ID kontrolery
ID kontroler | Význam |
---|---|
Jet\DataModel_IDController_AutoIncrement | Klasický přístup, který předpokládá číselnou sekvenci vygenerovanou databází po uložení záznamu. |
Jet\DataModel_IDController_UniqueString | Před uložením nového záznamu generuje náhodní řetězec s časovým razítkem na začátku. |
Jet\DataModel_IDController_Passive | Zcela pasivní kontroler. Předpokládá, že potřebné hodnoty pro ID nastaví logika aplikace a/nebo vnitřní relace. |
Vlastní ID kontroler
Vůbec nic vám nebrání vytvořit si vlastní ID kontroler pokud vám nestačí ty předdefinované. Ba naopak se to očekává. Jedinou podmínkou je, aby dědil od třídy Jet\DataModel_IDController.