Definice
Obdobně jako například konfigurační systém aplikace využívá i Jet DataModel atributy (PHP 8) pro které má Jet svou malou nadstavbu Jet\Attributes.
I zde platí, že definiční atributy musí mít jak třída reprezentující konkrétní datovou entitu, tak její její vlastnosti, které představují (dá se říct) datové sloupce. (Pochopitelně třída může mít i vlastnosti, které nemají s DataModel nic společného a jsou určené například pouze pro interní potřeby logiky třídy).
V případě Jet DataModel jsou samozřejmě možnosti definic o poznání širší než v případě zmíněného konfiguračního systému. Ale mám pro vás dobrou zprávu. Protože definice datových entit je vlastně základní práce při návrhu aplikace, tak Jet Studio samozřejmě disponuje nástrojem, kde se vše dá "naklikat" a ušetřit si tak opravdu spoustu času.
Tedy není nutné znát všechny definice nazpaměť. Ale je dobré znát princip jak to funguje a ideálně si umět poradit i bez nástroje Jet Studio.
Definice třídy (a entity jako takové)
Definice třídy - entity samotné je úplný základ a zahrnuje několik atributů, které musí být definovány. Zde je ukázka definice velice jednoduché entity article (článek) z ukázkové aplikace, jde o 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
{
// ... ... ...
}
V první řadě entita musí dědit od třídy Jet\DataModel, nebo pokud je to vnitřní relace a tedy subentita, pak buď od třídy Jet\DataModel_Related_1toN, nebo Jet\DataModel_Related_1to1 podle toho o jakou vnitřní relaci se jedná.
A teď už se pojďme kouknout jaké atributy třída musí mít a jaké mít může.
name
Jedná se o povinný atribut. Každá datová entita potřebuje nějaké interní pojmenování nezávislé na názvu třídy. Pojmenování pak slouží při odkazování na entity a jejich vlastnosti například při tvorbě dotazů. Jméno nemusí být unikátní v rámci celé vaší aplikace. Pokud budou existovat dvě třídy dědící od DataModel, budou mít stejný název entity, ale reálně se spolu tak říkajíc "nesetkají", tak to není problém. Ovšem rozhodně lze doporučit jména vytvářet unikátní a výstižná.
database_table_name
Opět se jedná o povinný atribut. Jak název napovídá, tak takto je definován název databázové tabulky ve které budou data entity pomocí backendu fyzicky uložena. Nebo jednoduše řečeno: název databázové tabulky, které k dané třídě patří.
V praxi to bude většinou stejná hodnota jako název entity, ale není to nutné. Entita může mít jiný název než tabulka. K čemu je to dobré? V určitých situacích může být entita uložena v několikra různých tabulkách s různými názvy a až při inicializaci aplikace lze vybrat, se kterou tabulkou bude aplikace reálně pracovat. Ale díky tomu, že v dotazech se používá název entity a ne název tabulky, tak aplikace může zůstat konstantní a transparentní - je možné změnit název tabulky (třeba i dynamicky), ale není nutné upravovat aplikaci (tedy měnit podobu dotazů).
id_controller_class
Taktéž povinný atribut. ID kontrolery jsou samostatné téma. Tedy zde pouze stručně, že tato vlastnost určije název třídy použitého ID kontroleru.
id_controller_options
Opět kontext se samostatným tématem ID kontrolerů jsou samostatné téma. ID kontroler může vyžadovat další nastavení. V takovém případě je tento atribut povinný.
parent_model_class
Tento atribut se týká pouze subentit, tedy o vnitřní relace a tedy potomky třídy Jet\DataModel_Related_1toN, nebo Jet\DataModel_Related_1to1. Pro subenetity je atribut povinný a určuje jaká entita je dané subentitě přímo nadřazená - tedy jaká já třída je rodičovská.
Ukažme si to opět konkrétně na reálném příkladu z ukázkové aplikace. Entita článek / article počítá s tím, že může mít víc lokalizací. Tedy hlavní třída JetApplication\Content_Article zastřešuje obecné věci a agreguje subentity představující již obsah vázaní na konkrétní lokalizace. Takový obsah představuje třída JetApplication\Content_Article_Localized.:
namespace JetApplication;
A jak vidíte, je jasně definováno jaká třída je nadřazenou entitou.
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
{
//... ... ... ...
}
default_order_by
Tento atribut se týká pouze subentity ve vnitřní relaci 1:N. Definuje řazení subentit při jejich načítání z databáze. Definice řazení je stejná jako při obecném načítání.
relation
Definuje vnější relaci, tedy provázání s jinou entitou, která není k dané entitě podřízená, což si opět říká o samostatné téma. Definice vnější relace je již trochu složitější, alespoň pro představu si ukažme příklad. Opět reálný příklad z ukázkové aplikace a to provázání galerie a obrázku:
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,
relation: [
'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
Jak si ukážeme za chvíli, tak na jednotlivých vlastnostech entity je možné určit, že se bude jednat o klíče / databázové indexy. Ale někdy je třeba vytvořit složený klič. Tedy klíč / index tvořený několika vlastnostmi (z pohledu DB sloupci). I to je samozřejmě možno definovat, ale opět je to samostatné téma.
Definice vlastností třídy (a prvnků entity)
A nyní si ukážeme definice vlastností třídy. Definice má v praxi například tuto podobu:
//... ... ...
Jak vidíte, tak příklad zahrnuje rovnou i definici formulářů (#[Form_Definition( ... )]), protože v běžné praxi ne na DataModel většinou nějaký formulář napojen. Ale nás teď pochopitelně bude zajímat primárně definice DataModel, tedy #[DataModel_Definition( ... )].
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 = [];
//... ... ...
//... ... ...
//... ... ...
}
Tak si opět ukažme možné atributy a jejich význam.
type
Dá se říct naprosto základní atribut určující typ vlastnosti z pohledu definice DataModel a tedy je logicky povinný
Seznam typů
Typ | Význam |
---|---|
DataModel::TYPE_ID | Textový identifikátor záznamu. Tedy unikátní textový řetězec. |
DataModel::TYPE_ID_AUTOINCREMENT | Číselný identifikátor záznamu. Tedy staré dobré známé autoincrement číslo. |
DataModel::TYPE_STRING | Obecný textový řetězec. Maximální délka textu musí být určena atributem max_len |
DataModel::TYPE_BOOL | Logické ano / ne. |
DataModel::TYPE_INT | Celé číslo. |
DataModel::TYPE_FLOAT | Desetinné číslo. |
DataModel::TYPE_LOCALE | Textový řetězec představující kód lokalizace - jako textový kód lokalizace bude uložen v databázi. Ovšem v rámci vlastnosti třídy se jedná o instanci třídy Jet\Locale. Tedy jednoduše řečeno: V databázi je uložen kód lokalizace, ale s hodnotou se pracuje zásadně jako s instancí třídy Jet\Locale. Konverzi z / na řetězec provádí backend automaticky. Deklarace by tedy měla mít například tuto podobu:
#[DataModel_Definition(
|
DataModel::TYPE_DATE | Typ představuje datum bez času. Tomu bude odpovídat příslušný databázový typ. Ovšem v rámci třídy se bude operovat s instancí Jet\Data_DateTime s indikací, že se jedná pouze o datum bez časového údaje. Deklarace by tedy měla mít tuto podobu:
#[DataModel_Definition(
|
DataModel::TYPE_DATE_TIME | Typ představuje datum a čas. Tomu bude opět odpovídat příslušný databázový typ. V rámci třídy se bude operovat s instancí Jet\Data_DateTime. Deklarace by tedy měla mít tuto podobu:
#[DataModel_Definition(
|
DataModel::TYPE_CUSTOM_DATA | Libovolná data, která budou před uložením do databáze serializována a po načtení deserializována (serializaci i deserializaci řeší backend automaticky). Tedy například se může jednat o pole, asociované pole a podobně. |
DataModel::TYPE_DATA_MODEL |
Již jsme narazili na téma vnitřní relace, které je samostatné. Vnitřní relace ovšem souvisí i s definicí parametrů entity. Vraťme se k příkladu článku z ukázkové aplikace. Jak již bylo řečeno, tak článek má své lokalizované verze. A ty je nutné na samotný článek (tedy na hlavní entitu) navázat a to se dělá právě pomocí vlastnosti s tímto speciálním typem, který se používá takto:
/**
Tedy definice vyžaduje i atribut data_model_class, který určuje jaká třída (tedy definice subentity) je vázána na danou vlastnost.
|
max_len
Určuje maximální délku textového řetězce pro typ DataModel::TYPE_STRING.
Atribut je povinný a samozřejmě platný pouze pro daný typ.
data_model_class
Viz typ DataModel::TYPE_DATA_MODEL.
Atribut je povinný a samozřejmě platný pouze pro daný typ.
database_column_name
Standardně název sloupce v databázové tabulce na nějž je vlastnost mapována odpovídá názvu vlastnosti. Ovšem pokud je třeba, je volitelně možné určit vlastní název sloupce pomocí tohoto atributu.
is_id
Indikuje zda vlastnost je brána jako ID, respektive zda se jedná o jednu z vlastností, které se berou jako ID záznamu. Více se dočtete v kapitole o ID kontrolerech.
related_to
Zde se opět dostáváme k tématu vnitřních relací. Již jsme si řekli, že subentita musí mít určenou nadřazenou entitu pomocí atributu třídy parent_model_class. A to samé platí i pro vlastnosti. Subentita musí mít vlastnost (nebo vlastnosti), které jí svazují s rodičovskou entitou (případně s hlavní entitou - viz příslušná kapitola). A je tedy nutné určit, že právě ta a ta vlastnost je vázána na určitou nadřazenou vlastnost.
Ale ukažme si to opět prakticky. Opět se vracíme k ukázkové aplikaci a k její entitě article / článek. Konkrétně k lokalizovaným verzím článku, tedy k entitě articles_localized a třídě JetApplication\Content_Article_Localized. Lokalizovaná verze článku musí mít ID článku ke kterému náleží a taktéž lokalizaci které se text týká. A právě ID článku musí být navázáno na ID hlavní entity:
#[DataModel_Definition(
type: DataModel::TYPE_ID,
is_id: true,
related_to: 'main.id',
do_not_export: true
)]
protected string|null $article_id = '';
Hodnota atributu related_to má jednoduchou strukturu: Před tečkou je buď hodnota main, nebo parent. Podle toho zda jde o návaznost na hlavní entitu (main), nebo o návaznost na nadřazenou subentitu (parent). Za tečkou je pak název vlastnosti návazné entity.
is_key
Indikuje zda bude vlastnost, přesněji řečeno příslušný sloupeček databázové tabulky, brána jako klíč / index. Jedná se o jednoduchý, nesložený klíč. Jak již bylo uvedeno výše, tak složené klíče se definují jinak. Toto je opravdu pouze pro definici jednoduchého klíče.
key_type
Viz is_key. Tento atribut dále definuje jakého typu má jednoduchý index / klíč být.
Možné typy:
Konstanta | Význam |
---|---|
DataModel::KEY_TYPE_INDEX | Běžný index (výchozí hodnota) |
DataModel::KEY_TYPE_PRIMARY | Primární klíč |
DataModel::KEY_TYPE_UNIQUE | Unikátní klíč |
do_not_export
Určuje, že v například v případě serializace objektu do JSON (či exportu do XML a podobně) se daná vlastnost nemá být exportována.
K čemu je to dobré? Například heslo uživatele by určitě nikdy nikam nemělo být exportováno a to ani jako hash.
Export do JSON, který provádí Jet toto zohledňuje automaticky. Pokud budete implementovat vlastní exportní mechanismy, tak je určitě nutné tento atribut zohledňovat.
backend_options
Specální atribut umožňující jednotlivým typům backendů předat speciální nastavení pro danou vlastnost (z pohledu backendu pro daný sloupeček tabulky). Konkrétní možnosti jsou závislé na konkrétním backendu. Není to však standardní postup a není doporučován. Případné použití nechám na vás a na vašem vlastním průzkumu jak to funguje.