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ářů.


Komentáře

  1. zaujimave, ale komplikovane?
    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
  2. re : zaujimave, ale komplikovane?
    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


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

Dovolená VI

12.7.2007

Dovolená V

12.7.2007

Dovolená IV

archiv

Vyhledávání

Na tomto serveru hledá Google.


Počasí v Brně

19°C
zataženo