Při vývoji aplikací v PHP spolupracujících s některým z SQL serverů se můžete setkat s problematikou parametrizace dotazů. Pokud jste se již někdy do takové aplikace pustili, určite jste se setkali s takovýmto nebo podobným zápisem :
$dotaz="
select id, nadpis, text
from clanek
where autor_id=$autor_id and
datum>='$datum' and
nadpis like '$nadpis'
";
Tento zápis je ale problematický v okamžiku kdy potřebujume oddělit definice SQL dotazů od vlastního PHP kódu, což se může hodit zejména v rozsáhlejších projektech, u kterých je třeba umožnit běh na různých SQL serverech, které jsou vzájemně syntakticky nekompatibilní.
K řešení toho problému jsem využil regulární výrazy a pole. Celý fígl spočívá v definici parametrů v SQL dotazu, jejich vyhledání pomocí regulárního výrazu a následném nahrazení definice hodnotami z pole.
Dejme tedy, že máme definici SQL dotazu $dotaz a hodnoty, které chceme naplnit v poli $hodnoty:
// dotaz
$dotaz="
select id, nadpis, text
from clanek
where autor_id={autor_id:n} and
datum>={datum:d} and
nadpis like {nadpis:s}
";
// hodnoty
$hodnoty=array(
„autor_id”=>1,
„datum”=>strtodate('2004-01-01'),
„nadpis”=>„%php%”
);
Předpokládám, že už vás napadlo, že veškeré definice parametrů jsou uvedeny ve složených závorkách, a to ve formátu {jméno_parametru:typ_parametru}. Pomocí funkce preg_match_all(), nyní vyhledáme všechny parametry a výsledek necháme uložit do pole $parametry.
preg_match_all("/{[^}]+}/",$dotaz,$parametry);
Následně odstraníme z parametrů složené závorky :
$parametry=preg_replace(array(„/}/”,„/{/”), array(„”,„”),$parametry0);
Obsah proměnné $parametry nyní vypadá takto :
Array ( [0] => autor_id:n [1] => datum:d [2] => nadpis:s )
A pustíme se do parsování pole $parametry :
// projedeme vsechny nalezene parametry
while (list($hodnota,$definice)=each($parametry)) {
if (strpos($definice,':')>0) {
// rozlozime na jmeno a typ parametru
list($jmeno,$typ)=split(':',$definice);
// pokud hodnota neexistuje nastavime NULL
if (array_key_exists($jmeno,$hodnoty))
$hodnota=$hodnoty[$jmeno];
else
$hodnota=null;
if (!is_null($hodnota))
switch (strtoupper($typ)) {
case 'D': // Datum
$hodnota=„'”.date('Y-m-d',$hodnota).„'”;
break;
case 'S': // String
$hodnota=„'”.$hodnota.„'”;
break;
case 'N': // Cislo
break;
default:
die(„chybny typ parametru '$value'”);
}
else
$hodnota=„null”;
$dotaz=str_replace('{'.$definice.'}', $hodnota, $dotaz);
} else {
die(„definice typu parametru '$definice' nenalezena”);
}
}
No a zakončíme to výpisem proměnné $dotaz :
echo($dotaz);
Pokud vše dopadlo dobře výsledek by měl vypadat asi takto :
select id, nadpis, text
from clanek
where autor_id=1 and
datum>='2004-01-01' and
nadpis like '%php%'
Celý kód se dá zabalit do jediné funkce a přímo se nabízí k řešení modifikace dat přijatých z formuláře, kde pole hodnot může být $HTTP_POST_VARS a dotazy mohou být uloženy v ini souboru nebo na SQL.
Tak a to je vše, jenom doufám, že mě tu nebudete kamenovat pokud nebudete sdílet mé nadšení z tohoto řešení, ale konstruktivní kritiku přijímam a proto pokud budete mít jakékoliv dotazy nebo připomínky pište do komentářů.
Novinky
17.7.2008
Doplněny fotogalerie :
Dovolená I
Dovolená II
Dovolená III
Dovolená IV
Dovolená V
28.2.2008
Doplněny fotogalerie :
09.02.2008 - Čtvrté narozeniny
26.12.2007 - Vánoční procházka
15.12.2007 - Předčasné vánoce
05.12.2007 - Mikuláš
25.11.2007 - Fotografka
16.09.2007 - Na pouti a bezva převlek
15.09.2007 - Na pouti
03.09.2007 - Poprvé ve školce
19.08.2007 - V Olomouci na Flóře
11.08.2007 - Liberecký dětský koutek
29.07.2007 - V Luhačovicích
12.7.2007
12.7.2007
12.7.2007
archivPočasí v Brně
19°C
zataženo
Komentáře
nie je lepsie spravit abstraktnu vrstvu / class / kniznicu, ktora spracuje poziadavku podla danej databazy? toto mi pride moc komplikovane.
ja by som to riesil trosku inak - rozdelil si poziadavku na premenne (napr. name, date..) a prikazy (select, update), podmienky (where) a zoradovanie (order) pripadne doplnkove ako group by...
ale inak zaujimave riesenie.
29.4.2004 00:21:39 - dusoft - mail
No me to komplikovane nepripada, divne ze ? :-)
Samozrejmne ze tento princip muze byt soucasti obecneho mechanizmu pristupu k databazi, ale reseni ktere navrhujete, pokud jsem to dobre pochopil, predpoklada, ze pro jednu akci pouziji vzdy stejny pristup.
Pokud ale budu resit napriklad update nejakeho zaznamu, tak se mi muze stat, ze na jednom SQL pouziji na navazane akce trigger, na jinem SQL cely update zapouzdrim do volani ulozene procedury a napriklad na MYSQL to budu muset resit jako jednu SQL davku. Jenze to by znamenalo upravu programoveho kodu pro jednotliva SQL, kdezto takto prepisi pouze danou definici dotazu, ale parametry mi zustavaji.
Pokud jsem vase reseni spatne pochopil tak me prosim vyvedte z omylu :-)
S pozdravem,
Petr Kodytek
29.4.2004 16:16:27 - Petr Kodytek - mail