Vnitřní relace

V kapitolách Principy modelování jsme na téma vnitřních relací již narazili. Tedy již patrně víte, že entita může mít (v praxi často má) své subentity a ty mohou mít další subentity a že mezi těmito entitami jsou vztahy, kterým se říká vnitřní relace.

V kapitole Principy modelování jsme si ukazovali příklad faktury. A toho se budeme držet i zde, jen si totéž rozebereme do detailu a ukážeme si reálnou podobu definic.

Připomeňme si uspořádání entity faktura:

A teď si již ukažme jednotlivé definice entity (subentit), které určují vnitřní relaci.

Hlavní entita

Faktura musí mít:

  • Vlastní identifikátor - tedy ID faktury.
  • Číslo dokladu pro účetnictví
  • Datum vystavení faktury
  • Datum splatnosti faktury
  • ID zákazníka
  • Fakturační údaje zákazníka platné v době vystavení faktury
  • ... a celou řadu dalších údajů (pro stručnost definice nebude obsahovat vše)
Základ definice této entity může mít třeba tuto podobu: namespace JetApplication;

use 
Jet\DataModel;
use 
Jet\DataModel_Definition;
use 
Jet\DataModel_Fetch_Instances;
use 
Jet\DataModel_IDController_AutoIncrement;
use 
Jet\Form;
use 
Jet\Form_Field;
use 
Jet\Data_DateTime;

#[
DataModel_Definition(
    
name'invoice',
    
database_table_name'invoice',
    
id_controller_classDataModel_IDController_AutoIncrement::class,
    
id_controller_options: [
        
'id_property_name' => 'invoice_id'
    
]
)]
class 
Invoice extends DataModel
{
    #[
DataModel_Definition(
        
typeDataModel::TYPE_ID_AUTOINCREMENT,
        
is_idtrue
    
)]
    protected 
int $invoice_id 0;

    #[
DataModel_Definition(
        
typeDataModel::TYPE_STRING,
        
is_keytrue,
        
max_len50
    
)]
    protected 
string $invoice_number '';

    #[
DataModel_Definition(
        
typeDataModel::TYPE_DATE
    
)]
    protected ?
Data_DateTime $date_of_invoice null;

    #[
DataModel_Definition(
        
typeDataModel::TYPE_DATE
    
)]
    protected ?
Data_DateTime $expiry_date null;

    #[
DataModel_Definition(
        
typeDataModel::TYPE_INT,
        
is_keytrue
    
)]
    protected 
int $customer_id 0;

    #[
DataModel_Definition(
        
typeDataModel::TYPE_STRING,
        
max_len255
    
)]
    protected 
string $customer_name '';

    #[
DataModel_Definition(
        
typeDataModel::TYPE_DATA_MODEL,
        
data_model_classInvoice_Items::class
    )]
    protected array 
$items = [];

    #[
DataModel_Definition(
        
typeDataModel::TYPE_DATA_MODEL,
        
data_model_classInvoice_Payments::class
    )]
    protected array 
$payments = [];

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

Za pozornost stojí zejména vlastnost $items (a také $payments - stejný princip). Ta navazuje subentitu invoice_item (položky faktury) směrem dolů v relaci 1:N.

Subentita 1. úrovně

V našem příkladu je subentita 1. úrovně (tedy přímo náležící pod hlavní entitu) položka faktury. Co o položce faktury potřebujeme vědět?

  • Určitě musí mít vlastní ID
  • Je nutné vědět, k jaké faktuře patří. Tedy znát ID nadřazené entity.
  • Určitě bude mít několik dalších specifických parametrů jako například popis, cenu za jednotku, počet jednotek a tak dále.
Ukažme si to názorně:

namespace JetApplication;

use 
Jet\DataModel;
use 
Jet\DataModel_Definition;
use 
Jet\DataModel_IDController_AutoIncrement;
use 
Jet\DataModel_Related_1toN;

#[
DataModel_Definition(
    
name'invoice_items',
    
database_table_name'invoice_items',
    
parent_model_classInvoice::class,
    
id_controller_classDataModel_IDController_AutoIncrement::class,
    
id_controller_options: [
        
'id_property_name' => 'item_id'
    
]
)]
class 
Invoice_Items extends DataModel_Related_1toN
{

    #[
DataModel_Definition(
        
typeDataModel::TYPE_ID_AUTOINCREMENT,
        
is_idtrue
    
)]
    protected 
int $item_id 0;
 
    #[
DataModel_Definition(
        
related_to'main.invoice_id',
        
is_keytrue
    
)]
    protected 
int $invoice_id 0;

    #[
DataModel_Definition(
        
typeDataModel::TYPE_INT,
        
is_keytrue
    
)]
    protected 
int $goods_id 0;

    #[
DataModel_Definition(
        
typeDataModel::TYPE_STRING,
        
max_len255
    
)]
    protected 
string $item_name '';

    #[
DataModel_Definition(
        
typeDataModel::TYPE_FLOAT
    
)]
    protected 
float $price_per_unit 0.0;

    #[
DataModel_Definition(
        
typeDataModel::TYPE_FLOAT
    
)]
    protected 
float $number_of_units 0.0;

    #[
DataModel_Definition(
        
typeDataModel::TYPE_FLOAT
    
)]
    protected 
float $vat_rate 0.0;

    public function 
getArrayKeyValue() : string
    
{
        return 
$this->item_id;
    }

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

A tady už je to trochu zajímavější. Subentita musí definovat návaznost na rodičovskou entitu a to dvěma způsoby:

První je definice rodiče (rodičovské třídy) v atributech třídy:

#[DataModel_Definition(
        .... ... ...
    
parent_model_classInvoice::class
)]

A druhou nutností je definovat návaznost příslušné vlastnosti (nebo vlastností) na ID vlastnost (nebo vlastnosti) nadřazené entity a to takto:

#[DataModel_Definition(
    
related_to'main.invoice_id',
    
is_keytrue
)]
protected 
int $invoice_id 0;

Subentita 2. úrovně (a libovolné další úrovně)

A nyní si ukážeme jak řešit situaci, kdyby položka faktory měla další na ni navázanou subentitu.

  • Musí znát ID nadřazené entity - tedy ID položky faktory.
  • Musí znát ID hlavní entity - tedy ID faktury.
  • A samozřejmě další specifické vlastnosti dané subentity.
Opět si to ukažme názorně:

namespace JetApplication;

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

#[
DataModel_Definition(
    
name'invoice_items_subentty',
    
database_table_name'invoice_items_subentty',
    
parent_model_classInvoice_Items::class,
    
id_controller_classDataModel_IDController_Passive::class,
)]
class 
Invoice_Items_Subentty extends DataModel_Related_1toN
{

    #[
DataModel_Definition(
        
typeDataModel::TYPE_STRING,
        
is_idtrue
    
)]
    protected 
string $subitem_id '';

    #[
DataModel_Definition(
        
related_to'main.invoice_id',
        
is_keytrue
    
)]
    protected 
int $invoice_id 0;

    #[
DataModel_Definition(
        
related_to'parent.item_id',
        
is_keytrue
    
)]
    protected 
int $item_id 0;

    #[
DataModel_Definition(
        
typeDataModel::TYPE_STRING,
        
max_len255
    
)]
    protected 
string $something '';

    public function 
getArrayKeyValue() : string
    
{
        return 
$this->subitem_id;
    }

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

Důležitá je dvojí vazba. Subentita 2. a každé další úrovně musí být vázána na svou přímo nadřazenou (rodičovskou) entitu:

#[DataModel_Definition(
    
related_to'parent.item_id',
    
is_keytrue
)]
protected 
int $item_id 0;

Stejně tak jako musí být vázána na entitu hlavní:

#[DataModel_Definition(
    
related_to'main.invoice_id',
    
is_keytrue
)]
protected 
int $invoice_id 0;

Shrnutí

Pro vnitřní vazby stačí dodržovat tyto principy:

  • Z rodiče definovat vazbu na potomka pomocí k tomu určené vlastnosti třídy.
  • Z potomka definovat vazbu na rodiče atributu třídy.
  • Provázat příslušné vlastnosti potomka na ID vlastnosti rodiče.
  • V každé další úrovni subentit vždy provázat příslušné vlastnosti na ID vlastnosti hlavní entity.
Krom toho není třeba vnitřní relace extra definovat.

A pozor, dobrá zpráva! Nic z toho nemusíte dělat ručně ;-) Vše za vás vyřeší nástroj Jet Studio.

Předchozí kapitola
ID kontrolery
Další kapitola
Vnější relace