OOP v PHP: Využití OOP v praxi
Po dlouhé teorii přichází na řadu praxe. V následujícím textu si vysvětlíme možnosti přístupu k databázi pomocí různých vzorů objektově orientovaného programování v PHP.
Představme si například internetový obchod, který pracuje s MySQL. Běžný e-shop může mít i několik desítek tisíc řádků kódu. Pokud se změní syntaxe jazyka nebo budete chtít změnit typ databáze, znamená to, že musíte projít všechny řádky kódu a zápis změnit. S OOP tento problém vyřešíte během několika málo minut.
Funkce pracující se syntaxí SQL totiž nejsou součástí procedurálního uspořádání kódu, jsou vázány v objektech, a tak změna jednoho zápisu ovlivní chování celého kódu, který tuto třídu využívá.
Zadání
Naším cílem bude vytvořit kód rozhraní, které umožní komunikaci s databází MySQL i SQLite (případně dalších) s tím, že výběr typu databáze umožníme pouhou změnou jedné hodnoty proměnné. Při tvorbě aplikace využijeme abstraktní třídu, implementace rozhraní, dereference objektů a podobně.
Přípravy
Nejprve je třeba vytvořit databáze v SQLite i MySQL, vytvořit tabulky a databázi naplnit daty. Stáhněte si proto pomocný skript, který za vás vytvoří stejnou tabulku v MySQL i SQLite a naplní ji 150 náhodnými záznamy (v phpMyAdmin je třeba vytvořit databázi „oop“).
Vytvoření proměnných a vzoru Factory
Nejprve si vytvoříme několik proměnných, které budou obsahovat informace o připojení a typ databáze. Pak pomocí vzoru factory zavoláme různou třídu podle hodnoty proměnné $type:
<?php
$typ = 'SQLite';
$host = 'localhost';
$uzivatel = '';
$heslo = '';
$databaze = 'oop';
function Factory($typ,$host,$uzivatel,$heslo,$databaze){
if($typ == 'MySQL'){
return new MySQL($host,$uzivatel,$heslo,$databaze);
}else if($typ == 'SQLite'){
return new SQLite($databaze);
}else{
return FALSE;
}
}
?>
Třídě MySQL předáváme čtyři proměnné, třídě SQLite je třeba předat pouze název databáze.
Definice tříd MySQL a SQLite a rozhraní
Pro obě třídy MySQL a SQLite vytváříme stejnou strukturu, vytvoříme si tedy rozhraní, které obě třídy implementují. Funkce obou tříd je vytvoření spojení s příslušnou databází a volání další třídy, která provádí hlavní logiku operací:
<?php
interface Spojeni{
public function spojeni();
public function dotaz($dotaz);
}
class MySQL implements Spojeni{
protected $host;
protected $uzivatel;
protected $heslo;
protected $databaze;
protected $spoj;
public function __construct($host,$uzivatel,$heslo,$databaze){
$this->host = $host;
$this->uzivatel = $uzivatel;
$this->heslo = $heslo;
$this->databaze = $databaze;
}
public function spojeni(){
@$this->spoj = mysql_connect($this->host,$this->uzivatel,$this->heslo) or die('Spojení se nezdařilo');
@mysql_select_db($this->databaze, $this->spoj) or die('Databáze neexistuje');
}
public function dotaz($dotaz){
if(!$this->spoj){
$this->spojeni();
}
return new VraceniDotazuMySQL($this->spoj,$dotaz);
}
}
class SQLite implements Spojeni{
public $databaze;
public $spoj;
public function __construct($databaze){
$this->databaze = $databaze;
}
public function spojeni(){
$this->spoj = sqlite_open($this->databaze) or die('Spojení se nezdařilo');
}
public function dotaz($dotaz){
if(!$this->spoj){
$this->spojeni();
}
return new VraceniDotazuSQLite($this->spoj,$dotaz);
}
}
?>
Definice tříd VraceniDotazuMySQL a VraceniDotazuSQLite a abstraktní třídy
Nejprve vytvoříme abstraktní třídu. Proč nevytváříme rozhraní? Obsahuje totiž jednu metodu, která bude ve všech třídách stejná, a tak je jednodušší tuto metodu definovat jako abstraktní. Připomínám, že abstraktní třída se neimplementuje, ale dědí (nemůže sama vytvářet instance).
<?php
abstract class VraceniDotazu{
abstract public function Vlozeni();
abstract public function VratPole();
public function Pole(){
$pole = array();
while ($radek = $this->VratPole()){
$pole[] = $radek;
}
return $pole;
}
}
?>
Dále vytváříme třídy VraceniDotazuMySQL a VraceniDotazuSQLite. Konstruktor přiřazuje hodnoty předaným vlastnostem. Metoda Vlozeni() nejprve vytváří pole z argumentů, které metodě byly přiřazeny. Pak pomocí cyklu foreach připravíme dotaz pro databázi. Metoda VratPole() už následně vytváří pole hodnot, které jsme získali z databáze. K syntaxi dotazů se ještě později vrátíme.
<?php
class VraceniDotazuMySQL extends VraceniDotazu{
protected $spojeni;
protected $dotaz;
protected $argumenty;
protected $vysledek;
public function __construct($spojeni, $dotaz){
$this->spojeni = $spojeni;
$this->dotaz = $dotaz;
}
public function Vlozeni(){
$argumenty = func_get_args();
foreach ($argumenty as $index => $hodnota){
$this->argumenty[$index + 1] = $hodnota;
}
$pocet_argumentu = count($argumenty);
$dotaz = $this->dotaz;
foreach($this->argumenty as $index => $hodnota){
$dotaz = str_replace(":$index","'".mysql_escape_string($hodnota)."'", $dotaz);
}
$this->vysledek = mysql_query($dotaz);
if(!$this->vysledek){
return FALSE;
}
return $this;
}
public function VratPole(){
return mysql_fetch_array($this->vysledek);
}
}
class VraceniDotazuSQLite extends VraceniDotazu{
protected $spojeni;
protected $dotaz;
protected $argumenty;
protected $vysledek;
public function __construct($spojeni, $dotaz){
$this->spojeni = $spojeni;
$this->dotaz = $dotaz;
}
public function Vlozeni(){
$argumenty = func_get_args();
foreach ($argumenty as $index => $hodnota){
$this->argumenty[$index + 1] = $hodnota;
}
$pocet_argumentu = count($argumenty);
$dotaz = $this->dotaz;
foreach($this->argumenty as $index => $hodnota){
$dotaz = str_replace(":$index","'".sqlite_escape_string($hodnota)."'", $dotaz);
}
$this->vysledek = sqlite_query($this->spojeni, $dotaz);
if(!$this->vysledek){
return FALSE;
}
return $this;
}
public function VratPole(){
return sqlite_fetch_array($this->vysledek);
}
}
?>
Dotazy na databázi
Když máme rozhraní připravené, můžeme zadávat dotazy a výsledky vypisovat:
<?php
$dotaz = "SELECT * FROM tab WHERE jmeno = :1";
$ins = Factory($typ,$host,$uzivatel,$heslo,$databaze);
$jm = "jmeno".rand(1,10)."jmeno";
$d = $ins->dotaz($dotaz)->Vlozeni($jm)->Pole();
echo '<table>';
for($i = 0; $i < count($d);$i++){
echo '<tr><td>'.$d[$i][id].'</td>
<td>'.$d[$i][jmeno].'</td>
<td>'.$d[$i][prijmeni].'</td></tr>';
}
echo '</table>';
?>
Proč mají dotazy syntaxi SELECT * FROM tab WHERE jmeno = :1? Takový dotaz stačí použít jednou a pak už jen měníme argumenty, tedy další dotazy by například mohly vypadat takto:
<?php
$d = $ins->dotaz($dotaz)->Vlozeni('Nějaké jméno')->Pole();
?>
Jelikož metoda Vlozeni() umožňuje přeměnu libovolného počtu předaných argumentů, můžeme používat dotazy typu:
<?php
$dotaz = "SELECT * FROM tab WHERE jmeno = :1 AND prijmeni = :2";
$d = $ins->dotaz($dotaz)->Vlozeni('Nějaké jméno','Nějaké prijmeni')->Pole();
?>
Díky takovému zápisu rovněž můžeme dotazy přizpůsobovat libovolnému typu databáze.
Pro další studium a testování si stáhněte podklady k příkladu.
Pozn. red.: Zdůrazňujeme, že tento text se týká PHP 5.








Jsef
Pro 8, 2011 v 19:35Článek dobrý, jen trochu neužitečný. Píše se v něm o různém stahování podkladů, ale nikde není nic ke stažení. V takovém případě je celý článek k ničemu…
martin
Dub 22, 2012 v 9:54Kdepak se dají stáhnout ty podklady k článku….?
Aleš
Kvě 6, 2012 v 11:52Stejný problém… Podklady prostě asi nejsou a nebudou…:-/ Škoda…