Edit
Lang

Database: ActiveRow

Každý řádek, který vrátí Selection, je reprezentován instancí ActiveRow. Tato třída poskytuje standardní interface pro přístup k vybraným sloupcům. Data můžeme číst jako členské proměnné nebo použít přístup přes klíče pole. Pokud sloupec neexistuje, je vyhozena výjimka MemberAccessException. Pomocí standardní konstrukce isset lze ověřit, zda má sloupec nastavenou neprázdnou hodnotu.

echo $book->id;
echo $book['title'];

isset($book->id);
isset($book['title']);

Pozor, isset vrátí FALSE, i když byl sloupec dotazem vybrán, ale má hodnotu NULL.

Vztahy

Hlavním principem Nette\Database\Table je oddělené načítání dat z různých tabulek. Toto chování je zajišťováno samotnou Nette\Database, příbuzná data jsou z databáze vybrána pro všechny řádky najednou. Máme dva hlavní druhy vztahů: „has one“ a „has many“.

Budeme používat DiscoveredConventions, která načte vztahy mezi tabulkami přímo z databáze.

Relace Has one

Relace has one je velmi běžná. Kniha má jednoho autora. Kniha má jednoho překladatele. Řádek, který je ve vztahu has one získáme pomocí metody ref(). ref() přijímá dva argumenty: jméno cílové tabulky a název spojovacího sloupce. Viz příklad:

$book = $context->table('book')->get(1);
$book->ref('author', 'author_id');

V příkladu výše vybíráme souvisejícího autora z tabulky author. Primární klíč tabulky author je hledán podle sloupce book.author_id. Metoda ref() vrací instanci ActiveRow nebo NULL, pokud hledaný záznam neexistuje. Vrácený řádek je instance ActiveRow, takže s ním můžeme pracovat stejně jako se záznamem knihy.

$author = $book->ref('author', 'author_id');
$author->name;
$author->born;

// nebo přímo
$book->ref('author', 'author_id')->name;
$book->ref('author', 'author_id')->born;

Kniha má také jednoho překladatele, jeho jméno získáme snadno.

$book->ref('author', 'translator_id')->name

Tento přístup je funkční, ale pořád trochu zbytečně těžkopádný, nemyslíte? Databáze už obsahuje definice cizích klíčů, tak proč je nepoužít automaticky. Pojďme to vyzkoušet.

Pokud přistoupíme k členské proměnné, která neexistuje, ActiveRow se pokusí použít jméno této proměnné pro relaci „has one“. Čtení této proměnné je stejné jako volání metody ref() pouze s jedním parametrem. Tomuto parametru budeme říkat klíč. Tento klíč bude použit pro vyhledání cizího klíče v tabulce. Předaný klíč je porovnán se sloupci, a pokud odpovídá pravidlům, je cizí klíč na daném sloupci použit pro čtení dat z příbuzné tabulky. Viz příklad:

$book->author->name;
// je stejné jako
$book->ref('author')->name;

Instance ActiveRow nemá žádný sloupec author. Všechny sloupce tabulky book jsou prohledány na shodu s klíčem. Shoda v tomto případě znamená, že jméno sloupce musí obsahovat klíč. V příkladu výše sloupec author_id obsahuje řetězec „author“ a tedy odpovídá klíči „author“. Pokud chceme přistoupit k záznamu překladatele, obdobným způsobem použijeme klíč „translator“, protože bude odpovídat sloupci translator_id. Více o logice párování klíčů si můžete přečíst v kapitole Joining expressions.

echo $book->title . ": ";
echo $book->author->name;
if ($book->translator) {
    echo " (translated by " . $book->translator->name . ")";
}

Pokud chceme získat autora více knih, použijeme stejný přístup. Nette\Database za nás z databáze záznamy autorů a překladatelů pro všechny knihy najednou.

$books = $context->table('book');
foreach ($books as $book) {
    echo $book->title . ": ";
    echo $book->author->name;
    if ($book->translator) {
        echo " (translated by " . $book->translator->name . ")";
    }
}

Tento kód zavolá pouze tyto tři dotazy do databáze:

SELECT * FROM `book`;
SELECT * FROM `author` WHERE (`id` IN (1, 2, 3)); -- id ze sloupce author_id vybraných knih
SELECT * FROM `author` WHERE (`id` IN (2, 3));    -- id ze sloupce translator_id vybraných knih

Relace Has many

Relace „has many“ je pouze obrácená „has one“ relace. Autor napsal několik (many) knih. Autor přeložil několik (many) knih. Tento typ relace je obtížnější, protože vztah je pojmenovaný („napsal“, „přeložil“). Instance ActiveRow má metodu related(), která vrací pole souvisejících záznamů. Záznamy jsou opět instance ActiveRow. Viz příklad:

$author = $context->table('author')->get(11);
echo $author->name . " napsal:";

foreach ($author->related('book.author_id') as $book) {
    echo $book->title;
}

echo "a přeložil:";
foreach ($author->related('book.translator_id') as $book) {
    echo $book->title;
}

Metoda related() přijímá popis spojení jako dva argumenty, nebo jako jeden argument spojený tečkou. První argument je cílová tabulka, druhý je sloupec.

$author->related('book.translator_id');
// je stejné jako
$author->related('book', 'translator_id');

Můžeme použít heuristiku Nette\Database založenou na cizích klíčích a použít pouze klíč. Klíč bude porovnán s cizími klíči, které odkazují do aktuální tabulky (tabulka author). Pokud je nalezena shoda, Nette\Database použije tento cizí klíč, v opačném případě vyhodí výjimku Nette\InvalidArgumentException nebo AmbiguousReferenceKeyException. Více o logice párování klíčů si můžete přečíst v kapitole Joining expressions.

Metodu related() může samozřejmě volat na všechny získané autory a Nette\Database načte všechny odpovídající knihy najednou.

$authors = $context->table('author');
foreach ($authors as $author) {
    echo $author->name . " napsal:";
    foreach ($author->related('book') as $book) {
        $book->title;
    }
}

Příklad uvedený výše spustí pouze tyto dva dotazy do databáze:

SELECT * FROM `author`;
SELECT * FROM `book` WHERE (`author_id` IN (1, 2, 3)); -- id vybraných autorů