forked from nette/docs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Database translated to Czech, typos fixed in English version. This re…
…solves nette#82
- Loading branch information
Jan Straka
committed
May 8, 2014
1 parent
3c48391
commit 7a69e2f
Showing
8 changed files
with
455 additions
and
134 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
Database: ActiveRow | ||
******************* | ||
|
||
Každý řádek, který vrátí [Selection | api:Nette\Database\Table\Selection] je reprezentován instancí [ActiveRow | api:Nette\Database\Table\ActiveRow]. Tato třída poskytuje standardní interface pro přístup k vybraným sloupcům. Data může číst jako členské proměnné, nebo použít přístup přes klíče pole. Pokud sloupec neexistuje, je vyhozena výjimka [MemberAccessException | api:Nette\MemberAccessException]. Jestli sloupec existuje, můžeme ověřit pomocí standardní konstrukce `isset`. | ||
|
||
/--code php | ||
echo $book->id; | ||
echo $book['title']; | ||
|
||
isset($book->id); | ||
isset($book['title']); | ||
\-- | ||
|
||
Pozor, `isset` vrátí FALSE i když sloupec ve výsledku existuje, ale má hodnotu NULL. | ||
|
||
|
||
Vztahy | ||
--------- | ||
|
||
Hlavním principem v 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". | ||
|
||
.[note] | ||
Budeme používat DiscoveredReflection, která z databáze 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() | api:Nette\Database\Table\ActiveRow::ref()]. Ref() přijímá dva argumenty: jméno cílové tabulky a název spojovacího sloupce. Viz příklad: | ||
|
||
/--code php | ||
$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. | ||
|
||
/--code php | ||
$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. | ||
/--code php | ||
$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 se shoduje, cizí klíč na daném sloupci je použit pro čtení dat z příbuzné tabulky. Viz příklad: | ||
|
||
/--code php | ||
$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íč. Tedy 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 | database-joining-expressions]. | ||
|
||
/--code php | ||
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. | ||
|
||
/--code php | ||
$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: | ||
/--code sql | ||
SELECT * FROM `book`; | ||
SELECT * FROM `author` WHERE (`id` IN (1, 2, 3)); -- ids of fetched books from author_id column | ||
SELECT * FROM `author` WHERE (`id` IN (2, 3)); -- ids of fetched books from translator_id column | ||
\-- | ||
|
||
|
||
Relace Has many | ||
--------------- | ||
|
||
Relace "has many" je pouze obrácená "has one". Autor napsal *několik* knih. Autor přeložil *několik* knih. Tento typ relace je obtížnější, protože vztah je pojmenovaný ("napsal", "přeložil"). Instance ActiveRow má metodu [related() | api:Nette\Database\Table\ActiveRow::related()], která vrací pole souvisejících záznamů. Záznamy jsou opět instance ActiveRow. Viz příklad: | ||
|
||
/--code php | ||
$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() | api:Nette\Database\Table\ActiveRow::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. | ||
/--code php | ||
$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 v databázi 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í náležitou výjimku: [MissingReferenceException | api:Nette\Database\Reflection\MissingReferenceException] nebo [AmbiguousReferenceKeyException | api:Nette\Database\Reflection\AmbiguousReferenceKeyException]. Více o logice párování klíčů si můžete přečíst v kapitole [Joining expressions | database-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. | ||
|
||
/--code php | ||
$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:: | ||
|
||
/--code sql | ||
SELECT * FROM `author`; | ||
SELECT * FROM `book` WHERE (`author_id` IN (1, 2, 3)); -- id vybraných autorů | ||
\-- | ||
|
||
{{toc: title}} | ||
|
||
{{themeicon: icon-database.png}} | ||
{{care: Jan Škrášek|449}} | ||
{{composer: nette/database}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
Database: Selection | ||
=================== | ||
|
||
[api:Nette\Database\Table\Selection] nám umožňuje vybírat a filtrovat řádky databázových tabulek. Její síla spočívá v psaní podmínek, které automaticky spojí tabulky pomocí JOIN. Už nikdy nebude potřeba spojovat tabulky ručně. .[perex] | ||
|
||
Filtrování | ||
---------- | ||
Pojďme se podívat na základní API Selection. Jednoduché podmínky můžeme vytvořit zavoláním metody [where() |api:Nette\Database\Table\Selection::where()]. Otazníky v dotazu budou nahrazeny obsahem proměnných. | ||
|
||
/--code php | ||
$selection->where('name = ?', $name); | ||
$selection->where('born > ?', $datetime); | ||
\-- | ||
|
||
Nette\Database automaticky přidá vhodné operátory podle toho, jaká data dostane: | ||
/--code php | ||
$name = 'Jon Snow'; | ||
$ids = array(1, 2, 3); | ||
$null = NULL; | ||
|
||
$selection->where('name', $name); // name = 'Jon Snow' | ||
$selection->where('id', $ids); // id IN (1, 2, 3) | ||
$selection->where('born', $null); // born IS NULL | ||
\-- | ||
|
||
Vícenásobné volání metody `where()` spojí podmínky pomocí operátoru `AND`. Pokud potřebujeme operátor `OR`, musíme podmínku napsat najednou. | ||
|
||
/--code php | ||
// id = 1 AND name = 'Jon Snow' | ||
$selection->where('id', 1)->where('name', $name); | ||
$selection->where('id = ? AND name = ?', 1, $name); | ||
|
||
// id = 1 OR name = 'Jon Snow' | ||
$selection->where('id = ? OR name = ?', 1, $name); | ||
\-- | ||
|
||
Opatrně, pokud nepřidáme operátor pro sloupec, může předaná hodnota ovlivnit zamýšlené chování. .[warning] | ||
|
||
Zástupný symbol (otazník) funguje i bez sloupcového operátoru. Následující volání jsou prakticky stejná. | ||
/--code php | ||
$selection->where('id = ? OR id = ?', 1, 2); | ||
$selection->where('id ? OR id ?', 1, 2); | ||
\-- | ||
|
||
Selection správně zpracovává i záporné podmínky a umí pracovat také s poli. | ||
|
||
/--code php | ||
$selection->where('NOT id', 1); | ||
$selection->where('id NOT', 1); // to samé | ||
|
||
$ids = array(); | ||
$selection->where('id', $ids); // id IS NULL AND FALSE | ||
$selection->where('id NOT', $ids); // id IS NULL OR FALSE | ||
$selection->where('NOT (id ?)', $ids); // NOT (id IS NULL AND FALSE) | ||
|
||
// toto způsobí výjimku, tato syntax není podporovaná | ||
$selection->where('NOT id ?', $ids); | ||
\-- | ||
|
||
|
||
Filtrování hodnotou z jiné tabulky | ||
---------------------------------- | ||
|
||
Často potřebujeme filtrovat výsledky pomocí podmínky, která zahrnuje jinou databázovou tabulku. Tento typ podmínek vyžaduje spojení tabulek, s Nette\Database už je ale nikdy nemusíme psát ručně. | ||
|
||
Řekněme, že chceme vybrat všechny knihy, které napsal autor jménem "Jon". Musíme napsat pouze jméno "spojovacího klíče" relace a název sloupce spojené tabulky. "Spojovací klíč" je odvozen od jména sloupce, který odkazuje na tabulku, se kterou se chceme spojit. V našem příkladu (viz databázové schéma) je to sloupec `author_id`, ze kterého stačí použít část - `author`. `name` je název sloupce v tabulce `author`. Můžeme vytvořit podmínku také pro překladatele knihy, který je připojen sloupcem `translator_id`. | ||
|
||
/--code php | ||
$selection = $context->table('book'); | ||
$selection->where('author.name LIKE ?', '%Jon%'); | ||
$selection->where('translator.name', 'David Grudl'); | ||
\-- | ||
|
||
Logika vytváření "spojovacího klíče" je dána implementací [IReflection |api:Nette\Database\IReflection]. Doporučujeme použití [DiscoveredReflection |api:Nette\Database\Reflection\DiscoveredReflection], které analyzuje cizí klíče a umožňuje jednoduše pracovat se vztahy mezi tabulkami. | ||
|
||
Vztah mezi knihou a autorem je 1:N. Obrácený vzhtah je také možný, nazýváme ho **backjoin**. Podívejte se na následující příklad. Chceme vybrat všechny autory, kteří napsali více než tři knihy. Pro vytvoření obráceného spojení použijeme `:` (dvojtečku). Dvojtečka znamená, že jde o vztah "hasMany" (a je to logické, dvě tečky jsou více než jedna). Bohužel Selection není dostatečně chytré a musíme mu pomoci s agregací výsledků a předat mu nějaký `GROUP BY` výrok, také podmínka musí být zapsaná jako výrok `HAVING`. | ||
|
||
/--code php | ||
$selection = $context->table('author'); | ||
$selection->group('author.id') | ||
->where('COUNT(:book.id) > 3'); | ||
\-- | ||
|
||
Možná jste si všimli, že spojovací výraz odkazuje na book, ale není jasné, jestli spojujeme přes `author_id` nebo `translator_id`. Ve výše uvedeném příkladu Selection spojuje přes sloupec `author_id`, protože byla nalezena shoda se jménem zdrojové tabulky - tabulky `author`. Pokud by neexistovala shoda a existovalo více možností, Nette vyhodí výjimku [AmbiguousReferenceKeyException |api:Nette\Database\Reflection\AmbiguousReferenceKeyException]. | ||
|
||
Abychom mohli spojovat přes `translator_id`, stačí přidat volitelný "parametr" do spojovacího výrazu. | ||
|
||
/--code php | ||
$selection = $context->table('author'); | ||
$selection->group('author.id') | ||
->having('COUNT(:book(translator).id) > 3') | ||
\-- | ||
|
||
Teď se podívejme na složitější příklad na skládání tabulek. | ||
|
||
Chceme vybrat všechny autory, kteří napsali něco o PHP. Všechny knihy mají štítky, takže chceme vybrat všechny autory, kteří napsali knihu se štítkem "PHP". | ||
|
||
/--code php | ||
$selection = $context->table('author'); | ||
$selection->where(':book:book_tags.tag.name', 'PHP') | ||
->group('author.id') | ||
->having('COUNT(:book:book_tags.tag.id) > 0'); | ||
\-- | ||
|
||
|
||
|
||
|
||
API | ||
------------- | ||
Podívejme se na různé možnosti filtrování a omezování pomocí Selection: | ||
.[wide] | ||
|
||
| `$table->where($where[, $param[, ...]])` | Nastaví WHERE | ||
| `$table->order($columns)` | Nastaví ORDER BY, může být výraz `('column DESC, id DESC')` | ||
| `$table->select($columns)` | Nastaví vrácené sloupce, může být výraz `('col, MD5(col) AS hash')` | ||
| `$table->limit($limit[, $offset])` | Nastaví LIMIT a OFFSET | ||
| `$table->group($columns)` | Nastaví GROUP BY | ||
| `$table->having($having)` | Nastaví HAVING | ||
|
||
Můžeme použít tzv. fluent interface, například `$table->where(...)->order(...)->limit(...)`. Více `where` podmínek je spojeno pomocí operátoru `AND`. Pro operátor `OR` musíme použít pouze jedno volání `where()`: | ||
|
||
/--code php | ||
$table->where('born > ? OR born IS NULL', $datetime); | ||
\-- | ||
|
||
Možné argumenty metody [where() |api:Nette\Database\Table\Selection::where()]: | ||
|
||
.[wide] | ||
| `$table->where("field", $value)` | field = $value | ||
| `$table->where("field", NULL)` | field IS NULL | ||
| `$table->where("field > ?", $val)` | field > $val | ||
| `$table->where("field", array(1, 2))` | field IN (1, 2) | ||
| `$table->where("field", $conn->table($tableName))` | field IN (SELECT $primary FROM $tableName) | ||
| `$table->where("field", $conn->table($tableName)->select('col'))` | field IN (SELECT col FROM $tableName) | ||
|
||
Můžeme vytvořit také agregaci výsledků: | ||
|
||
.[wide] | ||
| `$table->count("*")` | Vrátí počet řádků | ||
| `$table->count("DISTINCT $column")` | Vrátí počet odlišných hodnot | ||
| `$table->min($column)` | Vrátí minimální hodnotu | ||
| `$table->max($column)` | Vrátí maximální hodnotu | ||
| `$table->aggregation("GROUP_CONCAT($column)")` | Pro jakoukoliv jinou agregační funkci | ||
|
||
Čtení dat: | ||
|
||
.[wide] | ||
| `foreach ($table as $id => $row)` | Iteruje přes všechny řádky výsledku | ||
| `$row = $table->get($id)` | Vrátí jeden řádek s ID $id | ||
| `$row = $table->fetch()` | Vrátí následující řádek výsledku | ||
| `$array = $table->fetchPairs($key, $value)` | Vrátí všechny výsledky jako asociativní pole | ||
| `$array = $table->fetchPairs($key)` | Vrátí všechny řádky jako asociativní pole | ||
| `count($table)` | Vrátí počet řádků výsledku | ||
|
||
{{toc: title}} | ||
|
||
{{themeicon: icon-database.png}} | ||
{{care: Jan Škrášek|449}} | ||
{{composer: nette/database}} |
Oops, something went wrong.