7. diel - Databázový wrapper
V minulej lekcii, Založenie databázy a prístupy k nej v PHP , sme si pripravili MySQL databázu a pobavili sa o možných prístupoch k databázam z PHP. V dnešnom dieli si naprogramujeme jednoduchý wrapper nad ovládačom PDO.
Wrapper a PDO
Vytvoríme si CRUD wrapper, pomocou ktorého budeme komunikovať s databázou a ktorý nám pomôže začleniť vstup aj výstup z databázy do objektového kódu. Wrapper je všeobecne akýsi "obal" nad niečím zložitejším, kde si definujeme svoje rozhranie, často jednoduchšie. Viac viď. návrhový vzor Adapter (Wrapper). Wrapper postavíme okolo ovládača PDO, ktorý nám poskytuje priamo PHP a ktorý je jeden z najlepších. Umožňuje aj prepnúť na inú databázu, mapovať entity ako objekty a podobne. Spomeniem fakt, že samotné PDO je vlastne tiež wrapper, čiže balíme wrapper, ale už to tak je. Zjednodušíme si tým ďalšie kód aplikácie.
Model
Wrapper je bezpochyby logika a preto bude patriť medzi modelmi. V priečinku modely vytvorme triedu Db. Názov je krátky a jednoduchý, pretože ju budeme často používať.
class Db
{
}
Zdieľanie inštancie spojenie
Budeme chcieť, aby sa po pripojení k databáze danej spojenie uložilo a bolo prístupné zo všetkých miest našej aplikácie. Existuje na to niekoľko návrhových vzorov, ideálne Dependency Injection, jednoduchšie Service Locator alebo Singleton (aj keď ten je skôr antipattern). Keďže sa jedná o pokročilejšie techniky, nebudeme sa s týmto v našom seriáli vôbec zaťažovať a použijeme namiesto nich statiku. Všetko v triede bude statické, dáva to tu celkom zmysel, keďže ide o pomocnú triedu.
Pridajme si teda statickú premennú $ spojení:
private static $spojeni;
Pripojenie
PDO funkcie pre pripojenie k databáze chce ako parameter nastavenia. Jedná sa o asociatívne pole, kde používame ako kľúče konštanty z triedy PDO. Pridajme triede teda ešte toto pole a rovno v ňom nastavme spôsob reakcie na databázové chyby a inicializačný príkaz. Chyby nastavíme tak, aby spôsobovali výnimky. Inicializačný príkaz bude "SET NAMES utf8", ktorý by ste mali všetci dobre poznať, nastaví kódovanie, aby fungovala diakritika. Ak nie ste na nejaké naozaj staré databázu, pridáme aj nastavenie PDO :: ATTR_EMULATE_PREPARES, ktoré prenechá vkladanie parametrov do dotazu na databázu, je to tak bezpečnejšie a kvalitnejšie. V triede pribudne táto premenná:
private static $nastaveni = array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8", PDO::ATTR_EMULATE_PREPARES => false, );
Máme všetko pripravené na to, aby sme pridali metódu pre pripojenie k databáze:
public static function pripoj($host, $uzivatel, $heslo, $databaze) { if (!isset(self::$spojeni)) { self::$spojeni = @new PDO( "mysql:host=$host;dbname=$databaze", $uzivatel, $heslo, self::$nastaveni ); } }
Kód vychádza z článku PDO objektovo a modulárne.
Metóda jednoducho vytvorí inštanciu PDO s klasickými parametrami pre pripojenie k databáze (hostiteľ, užívateľské meno, heslo a názov databázy) a tú uloží do statickej premennej $ spojení. Túto inštanciu môžeme aj vrátiť. Máme ošetrené aj to, aby sa databáza nepokúšala znova pripojiť, keď už spojenie existuje.
Volanie dotazu
Teraz potrebujeme metódu na volanie databázového dotazu. PDO perfektne zvláda tzv. Prepared statements. Jedná sa spôsob vkladania premenných (alebo presnejšie nevkládání) do dotazu. Pri starých ovládačov sa premenné vkladali do textového reťazca s dotazom. Určite ste niekedy videli otázka:
// Tento kód je nebezpečný mysql_query('SELECT * FROM `uzivatele` WHERE `jmeno` = "' . $jmeno . '"');
Takýto dotaz je potenciálne nebezpečný, pretože ak obsah premennej pochádza od užívateľa, môže do databázy prepašovať škodlivý kód (viac viď. Pojem SQL injection, bolo o ňom na ITnetwork napísané už dosť). Postarom sa to riešilo takto:
mysql_query('SELECT * FROM `uzivatele` WHERE `jmeno` = "' . mysql_real_escape_string($jmeno) . '"');
Funkcia s krkolomným názvom mysql_real_escape_string () zabezpečila premennú tzv. Zescapováním nebezpečných znakov. Zabezpečenie však nebolo dokonalé, napr. Pre čísla.
Prepared statements rieši problém inak, premennú do dotazu jednoducho nevkladá a namiesto nej je vložený zástupný znak "?". Takýto dotaz sa "pripraví". Potom je spustený spolu s poľom parametrov, ktoré sa do neho dosadí namiesto otáznikov. Všetko je teda oddelené a tým pádom aj bezpečné.
Pridajme si do triedy funkciu na získanie jedného riadku z databázy:
public static function dotazJeden($dotaz, $parametry = array()) { $navrat = self::$spojeni->prepare($dotaz); $navrat->execute($parametry); return $navrat->fetch(); }
Na inštanciu spojenie zavoláme metódu prepare (), do ktorej sa vloží text dotazu sa zástupnými znakmi a otázka sa "pripraví". Potom zavoláme metódu execute (), ktorá k zisťovaniu pripojí poľa parametrov a otázka vykoná. Nakoniec získame 1. riadok metódou fetch () a ten vrátime.
Otázka na viac riadkov
Metóda dotazJeden () nám vráti iba jeden riadok, určite ale niekedy budeme potrebovať vyberať viac riadkov. Preto si pridajme metódu dotazVsechny (), ktorá nám vráti pole riadkov, ktoré zodpovedajú dotazu. Reálne by sa táto metóda použila napr. Pre výpis komentárov pod článok.
public static function dotazVsechny($dotaz, $parametry = array()) { $navrat = self::$spojeni->prepare($dotaz); $navrat->execute($parametry); return $navrat->fetchAll(); }
Dotaz na jeden stĺpec
Často sa budeme pýtať tiež len na jeden stĺpec, napr. V dotazoch SELECT COUNT (*) ... Pridáme si metódu dotazSamotny (), ktorá vráti vždy 1. hodnotu v 1. riadku:
public static function dotazSamotny($dotaz, $parametry = array()) { $vysledek = self::dotazJeden($dotaz, $parametry); return $vysledek[0]; }
Táto metóda je síce veľmi podobná metóde dotazJeden (), takto si však ušetríme niekoľko riadkov a pri opakovanom používaní vo zložitejšie aplikácii nám to príde vhod. To je napokon dôvod existencie celého nášho wrapper.
Dotaz vracajúci ovplyvnený počet riadkov
Najmä pri vkladaní, editáciu a mazanie sa nám bude ešte hodiť metóda vracajúci počet ovplyvnených riadkov. Tá bude vyzerať nasledovne:
// Spustí dotaz a vrátí počet ovlivněných řádků public static function dotaz($dotaz, $parametry = array()) { $navrat = self::$spojeni->prepare($dotaz); $navrat->execute($parametry); return $navrat->rowCount(); }
Wrapper máme hotový. Nabudúce, v lekcii Výpis článkov z databázy v PHP (MVC) , si pridáme ClanekKontroler a naučíme aplikáciu zobrazovať článok z databázy a zoznam článkov v databáze. Doterajšie projekt je ako vždy k stiahnutiu nižšie.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 2087x (12.39 kB)
Aplikácia je vrátane zdrojových kódov v jazyku PHP