Nejčastější chyby programátorů 3
Tento díl je zaměřen hlavně na práci s daty z databáze. Především na to, co se dělá zbytečně a co jde udělat lépe s menší zátěží serveru. Příklady vyžadují alespoň základní znalost objektového ovladače PDO a využívají databázi MySQL.
Vyhledání jen potřebných dat
V SQL lze data vyhledat dvěma způsoby - vyjmenovat jednotlivé sloupce nebo napsat hvězdičku (ta vyhledá všechny sloupce z tabulky). Použití hvězdičky je sice velmi jednoduché a není potřeba moc psaní, avšak ve většině případů vyhledá i sloupce, které nakonec vůbec nepotřebujete a nevyužijete je.
Jednoduchý příklad
Máme tabulku s uživateli, která obsahuje sloupce ID, přihlašovací jméno, jméno, příjmení, datum narození a datum registrace. Chceme vyhledat všechny uživatele a zobrazit jejich jméno, příjmení a datum registrace.
Při použití hvězdičky bychom napsali
$pdo->query(" SELECT * FROM `user` ");
Nicméně tento dotaz nám vyhledá všech 6 sloupců, namísto 3, které chceme. Znamená to, že se bude přenášet zbytečně více dat. U velmi malých databází by to prakticky nemělo na výkon vliv, ale představte si databázi, kde je registrovaných 10 tisíc lidí a stránku s tímto dotazem by si otevřely třeba 2 tisíce uživatelů v jeden okamžik. Znamenalo by to, že by se zbytečně přenášelo o 3 * 10k * 2k = 60M více bloků dat (3 sloupce navíc, 10.000 záznamů a 2000 uživatelů na stránce).
Prakticky bychom nevyhledávali všech 10 tisíc záznamů, ale různě je omezovali klauzulí LIMIT (např. u stránkování). Nicméně pro malý příklad toho, kolik zbytečných dat by se přeneslo, to stačí.
Řešením je vyjmenovat v dotazu jen ty sloupce, které opravdu potřebujeme.
$pdo->query(" SELECT `nick`, `first_name`, `last_name` FROM `user` ");
Zpětné apostrofy
Někdy se nám může stát, že jméno tabulky nebo sloupce bude kolidovat s klíčovým slovem databáze. Například kdybychom měli sloupec pojmenovaný text nebo from, takový dotaz by byl špatně:
$pdo->query(" SELECT text, from FROM user ");
Musíme tedy říci databázi, aby názvy našich sloupců nebrala jako klíčová slova. K tomu slouží zpětné apostrofy.
$pdo->query(" SELECT `text`, `from` FROM `user` ");
Osobně doporučuji psát zpětné apostrofy vždy, i když názvy nekolidují. Minimálně se tím vyhnete risku, že v nové verzi databáze přibude nové klíčové slovo, které by kolidovat mohlo.
Manipulace se záznamy
Databáze obsahují hromadu funkcí, které můžeme využít pro získání přesného formátu záznamů. Například pokud bychom chtěli sečíst hodnotu nějakého sloupce všech záznamů, můžeme to udělat na úrovni PHP.
$sum = 0; $query = $pdo->query(" SELECT `amount` FROM `table` "); $rows = $query->fetchAll(PDO::FETCH_OBJ); foreach ($rows as $row) { $sum += $row->amount; } echo $sum; // zobrazení celkového součtu
V tomto případě ale píšeme zbytečně více kódu a zároveň zbytečně z databáze taháme hodnotu sloupce a teprve potom přičítáme. Databáze obvykle obsahuje funkci, která to rovnou spočítá za nás a vrátí nám již výsledek. V MySQL je pro to funkce SUM().
$query = $pdo->query(" SELECT SUM(`amount`) FROM `table` "); $sum = $query->fetchColumn(); echo $sum; // zobrazení celkového součtu
Velmi podobný případ nastává i u samotného získávání počtu záznamů. Můžeme to řešit na úrovni PHP, kde jednotlivě budeme v cyklu přičítat. Pokud ještě používáte starý ovladač mysql_*, mohli byste napsat něco takového:
$query = mysql_query(" SELECT `amount` FROM `table` "); $rowsCount = mysql_num_rows($query); echo $rowsCount; // zobrazení počtu záznamů
Opět zbytečně taháme data a řádky počítáme až potom. Databáze ovšem obsahuje funkci COUNT(), která řádky rovnou spočítá.
$query = $pdo->query(" SELECT COUNT(*) FROM `table` "); $rowsCount = $query->fetchColumn(); echo $rowsCount; // zobrazení počtu záznamů
Funkci COUNT() můžeme předat také název určitého sloupce. V takovém případě nám to spočítá záznamy, kde hodnota daného sloupce není NULL.
Databáze takových funkcí obsahuje mnoho. Je výhodnější tyto funkce použít již na úrovni databázového dotazu a ne až na úrovni PHP.
Získání ID posledně vloženého záznamu
V určitých případech chceme, abychom po vložení nového řádku do tabulky získali jeho ID. Budeme mít tabulku se dvěma sloupci - ID a jméno uživatele. Lze to udělat zbytečně složitým způsobem:
// ze seznamu sloupců vynecháme ID, protože je nastaven jako AUTO_INCREMENT // a databáze do něj hodnotu vloží automaticky $pdo->query(" INSERT INTO `user` (`nick`) VALUES ('jméno uživatele') "); $query = $pdo->query(" SELECT MAX(`id`) FROM `user` "); $lastId = $query->fetchColumn();
Tento kód nám prvně vloží nový záznam do tabulky a následně pomocí funkce MAX() vybere to nejvyšší číslo.
$query = $pdo->query(" SELECT `id` FROM `user` ORDER BY `id` DESC LIMIT 1 "); $lastId = $query->fetchColumn();
Jinak zapsané, ale udělá to to samé. Jednoduše vybere hodnotu sloupce ID, seřadí od největšího a vybere jen to první (pomocí LIMIT).
Tyto dotazy jsou ale úplně zbytečné. Pro získání posledně vloženého ID má PDO metodu lastInsertId().
$pdo->query(" INSERT INTO `user` (`nick`) VALUES ('jméno uživatele') "); $lastId = $pdo->lastInsertId();
Procházení záznamů
Na rozdíl například od starého ovladače mysql_*, umí PDO vracet záznamy tak, jak požadujeme. Pokud chceme jen jednu hodnotu, nemusíme se k ní dostávat přes nějaké pole nebo naopak pro získání všech záznamů nemusíme postupně procházet řádek po řádku, ale PDO nám vrátí rovnou celý seznam.
Metoda fetchAll()
Jak z názvu vyplývá, metoda nám vrátí všechny nalezené záznamy. Uloží je do dvourozměrného pole ve tvaru:
array ( 0 => array( // první řádek ), 1 => array( // druhý řádek atp. ) )
Díky tomu lze pole projít cyklem (obvykle se používá foreach) a pracovat s jednotlivými záznamy.
$query = $pdo->query(" SELECT `data` FROM `table` "); $data = $query->fetchAll(); foreach ($data as $row) { // $row obsahuje sloupce z daného řádku }
Pokud se nenašla žádná data, výsledkem je prázdné pole.
Metoda fetch()
Metoda fetch() vrací jednorozměrné pole dle daného záznamu. Využije se v případě, že hledáme jeden konkrétní záznam, například informace o uživateli.
$id = (int) $_GET["id"]; $query = $pdo->query(" SELECT `first_name`, `last_name` FROM `user` WHERE `id` = {$id} LIMIT 1 "); $data = $query->fetch(PDO::FETCH_OBJ); echo $data->first_name; // vypsání jména daného uživatele
Pokud se nenajde žádný vyhovující řádek, metoda vrátí FALSE. Lze to velmi snadno použít pro zjištění, zda se řádek našel nebo ne. Využít se to dá například u přihlašování uživatelů.
$query = $pdo->query(" SELECT `id` FROM `user` WHERE `nick` = 'nějaké jméno' AND `password` = 'nějaké heslo' LIMIT 1 "); $data = $query->fetch(PDO::FETCH_OBJ); if ($data !== FALSE) { $_SESSION["userId"] = $data->id; // atd. } else { echo "Je nám líto, uživatel s takovým jménem nebo heslem neexistuje."; }
Metoda fetchColumn()
Tato metoda nám vrací hodnotu jednoho určitého sloupce v jednom záznamu. Často se používá například při počítání záznamů.
$query = $pdo->query(" SELECT COUNT(*) FROM `user` "); $usersCount = $query->fetchColumn(); // proměnná obsahuje počet všech uživatelů
Pokud se nenajde žádná hodnota, metoda vrátí FALSE.
Formát výstupních dat
Tato část úzce souvisí s tou předchozí. Kromě jednotlivého uskupení dat můžeme určit v jakém formátu se nám vrátí. Můžeme data například uložit pouze do asociativního pole nebo třeba do objektu.
$id = (int) $_GET["id"]; $query = $pdo->query(" SELECT `first_name`, `last_name`, `email` FROM `user` WHERE `id` = {$id} LIMIT 1 "); // nejčastěji používané $data = $query->fetch(PDO::FETCH_BOTH); // vrátí jak indexované, tak asociativní pole (defaultně) $data = $query->fetch(PDO::FETCH_NUM); // vrátí pouze indexované pole $data = $query->fetch(PDO::FETCH_ASSOC); // vrátí pouze asociativní pole $data = $query->fetch(PDO::FETCH_OBJ); // vrátí objekt třídy stdClass
Uvedl jsem pouze nejpoužívanější styly, existuje jich více (viz php.net).
Unikátní hodnoty
Pokud chceme mít v tabulce pouze unikátní hodnoty, není potřeba se předem dotazovat, zda tam již daná hodnota je. Často je k vidění takový kód:
// budeme předpokládat, že proměnná $name neobsahuje nebezpečné znaky $query = $pdo->query(" SELECT 1 FROM `user` WHERE `name` = '{$name}' LIMIT 1 "); $exists = $query->fetchColumn(); if (!$exists) { // zaregistrování nového uživatele }
Pokud nějaký záznam existuje, vrátí se 1, jinak nám PDO vrátí FALSE.
Můžeme ale využít unikátního klíče přímo v tabulce. Stačí danému sloupci přidat "unikátní klíč (unique key)" a nemůže se nám stát, že bychom vložili stejný záznam. Pokud se o to pokusíme, vrátí databáze chybu. Tu PDO reprezentuje dle nastavení. Nejčastějším nastavením je, že se při chybě vyhodí výjimka.
try { $pdo->query(" INSERT INTO `user` (`nick`) VALUES ('nějaké jméno') "); } catch (PDOException $e) { echo "Uživatel s tímto jménem již existuje."; }
Tak a třetí díl je za námi. Děkuji za přečtení a pokud bude zájem, rád se pokusím sepsat další tipy, které by mohly pomoct jak začínajícím, tak určitě i mírně pokročilým vývojářům.
Komentáře


Zobrazeno 10 zpráv z 12. Zobrazit vše