Klávesové zkratky na tomto webu - rozšířené Na obsah stránky

Translate to English… Ins Deutsche übersetzen…

DibiFluent - tekuté SQL příkazy

Dibi má ode dneška velice šikovnou novinku. Jak se vám líbí zápis SQL příkazů ve stylu fluent interfaces?

$res = dibi::select('product_id')->as('id')
        ->select('title')
        ->from('products')
        ->innerJoin('orders')->using('(product_id)')
        ->orderBy('title')
        ->execute();

// nebo
$record = array(
        'title'  => 'Výrobek',
        'price'  => 318,
        'active' => TRUE,
);
dibi::update('products', $record)
        ->where('product_id = %d', $id);
        ->execute();

Tento přístup má jednu zásadní výhodu – velmi snadno se SQL parametrizuje. Továrna může připravit SQL příkaz, který později upravím:

function sqlFactory()
{
    return dibi::select('*')
        ->from('products')
        ->innerJoin('orders')->using('(product_id)')
        ->where('active = ', TRUE);
}

$sql = sqlFactory();

// doplníme řazení
$sql->orderBy('price');
$sql->orderBy('name')->asc((bool) $dir);

// doplnime podminku
$sql->where('[price] > %i', $minPrice)->or('[price] IS NULL');

// a ještě SQL flag
$sql->setFlag('IGNORE');
// todo: možná bude vhodnější $sql->setIgnore();

Tuhle hračku jsem se poukoušel implementovat už asi dvakrát, ale pokud to chcete udělat fakt hodně dobře, není to taková legrace. Tentokrát jsem na to kápl. Výsledkem je třída DibiFluent. Není omezena na příkaz SELECT, ale poradí si s jakoukoliv syntaxí. Také velmi úzce spolupracuje s dibi modifikátory, což vidíte v uvedených příkladech. A možná budete překvapeni, jak je její kód krátký a srozumitelný.

Komentáře RSS 2.0 komentářů » přidat

avatar

#1 Martin Grames http://www.chapadlo.cz nový

Jen si takhle surfuju na netu a co najednou nenajdu – nový blog Davida Grudla o PHP, paráda ! :-)

Díky, těším se opět na kvalitní články nejen o Nette.

Posláno 21. 5. 2008 v 15.59 | Odpovědět
avatar

#2 Tomik http://tomik.jmx.cz nový

Davide, to je špička! Taky jsem nad tím jeden čas přemýšlel, ale od přemýšlení k realizaci je hodně dlouhá cesta. Díky!

P.S.: Vítej zpět! ;-)

Posláno 21. 5. 2008 v 17.33 | Odpovědět

#3 Petr Procházka nový

Už jsem se začínal po tak dlouhé odmlce bát, ale rozdělit latrine není špatný nápad. Trošku jsem poprskal monitor když jsem dneska poprvé zahlídl novej design, tak doufám že tady zůstane tenhle v jednoduchosti úžasnej.

Už se těším na další články.

Posláno 22. 5. 2008 v 15.49 | Odpovědět
avatar

#4 Milan svobodai@centrum.cz nový

Věřil jsem, že David to bez blogu dlouho nevydrží. Ještě by možná bylo dobré, kdyby zde byly odkazy na původní článkz na LaTrine.

Ale hlavně že je David zpět. Hurááááá

Posláno 22. 5. 2008 v 19.29 | Odpovědět
Na komentář reagoval [12] David Grudl

#5 deric nový

Výborné!! Vypadá to velice přehledně. Skládání dotazů přes pole bylo přece jen dosti krkolomné.

Posláno 22. 5. 2008 ve 23.09 | Odpovědět
avatar

#6 Martin Hujer http://www.martinhujer.cz nový

To už je skoro lepší než v Zend Frameworku :-)

Posláno 23. 5. 2008 ve 22.34 | Odpovědět

#7 Zdeněk http://www.dozd.eu nový

Konečně jsem se dostal k vyzkoušení a musím číct pouze: pěkné, moc pěkné.

Jen otázečka, je nějaký důvod k tomu, aby byla třída final? Hodilo by se mi, kdybych ji mohl dědit.

Posláno 24. 5. 2008 v 18.20 | Odpovědět
Na komentář reagoval [12] David Grudl
avatar

#8 finc http://blog.irminsul.cz nový

Vypadá to moc hezky. Takovéto sestavování dotazu je mnohem efektivnější i znovupoužitel­nější než opakované sestavování stringu ala SQL.

Jenom se zeptám, koukal jsi někdy na Hibernate Criteria API? Myslím, že jako námět na implementaci je to naprosto skvělá záležitost.
V postatě se jedná o něco podobného, alespoň na úrovni zápisu.

Posláno 24. 5. 2008 ve 21.39 | Odpovědět
avatar

#9 Šimon sajmi@email.cz nový

Konečně je dibi použitelný. Akorát se mi nelíbí to execute, nebylo by lepší třebe Return, Get, Fetch, … ?

Posláno 25. 5. 2008 ve 13.32 | Odpovědět
Na komentář reagoval [12] David Grudl [13] v6ak
avatar

#10 enoice http://enoice.fialove.eu nový

Super, jsem uchávcen hned ze dvou věcí, dostalo mě jak DibiFluent, tak i phpFashion… Ááá, opravdu se musím přiznat, že mě to hodně potěšilo, že si tě zas můžu přidat do čtečky… :)

Posláno 25. 5. 2008 v 15.09 | Odpovědět
avatar

#11 johno http://johno.jsmf.net/ nový

Prečo tie zátvorky v using('(product_id)') Bohumile?

Posláno 25. 5. 2008 ve 22.18 | Odpovědět
Na komentář reagoval [12] David Grudl
avatar

#12 David Grudl http://davidgrudl.com nový

#4 Milan: odkazy jsou v archívu

#7 Zdeněk: důvod není, final dám pryč. Ale je to zatím testovací a připomínkovací verze, API a chování se nejspíš změní.

#9 Šimon: takové ...delete()->from('table')->where('id = %i', $id)->fetch() nebo get() nevypadá moc dobře. Pro jednořádkové select by se teoreticky dalo fetch() použít, jako zkratka execute()->fetch().

#11 johno: závorky vyžaduje syntax SQL a inteligence, která by je doplnila, v DibiFluent (zatím) není.

Posláno 26. 5. 2008 v 1.07 | Odpovědět
Na komentář reagoval [13] v6ak

#13 v6ak http://v6ak.profitux.cz/ nový

#12 David Grudl:+ #9 Šimon: Mám nápad: ->fetch() by vyžádalo ->limit(1). Buď v SQL dotazu, nebo by při vrácení >1 výsledků vrhnulo výjimku.

Posláno 29. 5. 2008 ve 14.35 | Odpovědět
Na komentář reagoval [14] Šimon [19] David Grudl

#14 Šimon nový

#13 v6ak: To je dobrej nápad, ale výjimku bych neházel …

Posláno 29. 5. 2008 v 15.19 | Odpovědět
Na komentář reagoval [15] v6ak

#15 v6ak http://v6ak.profitux.cz/ nový

#14 Šimon: Proč? Mělo by to význam neřízené výjimky (z Javy – výjimka, která by při správném vstupu neměla nastat) a říkalo by to, že je něco špatně. Ale zas pro výkon by bylo lepší LIMIT 1…

Posláno 30. 5. 2008 v 18.23 | Odpovědět
Na komentář reagoval [16] Šimon

#16 Šimon nový

#15 v6ak: Ovšem potom by si musel psat ->limit(1)->fetch(), kdybys jen napsal ->fetch() a automaticky si to vynutilo limit 1 tak by to bylo logičtější, výjimku by to mohlo hodit kdybys dal ->limit(15)->fetch() … Co ty na to?

Posláno 1. 6. 2008 v 16.41 | Odpovědět
Na komentář reagoval [17] v6ak

#17 v6ak http://v6ak.profitux.cz/ nový

#16 Šimon: Špatně jsem se vyjádřil – tím, že by to vyžádalo ->limit(1) jsem myslel, že by to automaticky zavolalo.

Takže vidím to takhle: fetch v závislosti na limitu se rozhodne o řešení:

  • 1 – return $this->execute()->fetch();
  • null (nebo jak to je, pokud to není nastaveno) – return $this->limit(1)->execute()->fetch();
  • jiné – throw new …Exception(…);
Posláno 1. 6. 2008 ve 20.22 | Odpovědět
Na komentář reagoval [19] David Grudl
avatar

#18 Charlie http://charlie.cz nový

Dřívější absence fluent interfaces byla důvod, proč jsem přešel k Zend_Db. Tušil jsem, že dibi bude něco takového časem obsahovat, tento návrhový vzor totiž rulez :)

Posláno 1. 6. 2008 ve 21.28 | Odpovědět
Na komentář reagoval [19] David Grudl
avatar

#19 David Grudl http://davidgrudl.com nový

Otázka do pléna: $sql->setFlag('IGNORE'); slouží k nastavení příznaků, které se v SQL příkazech zapisují hned po prvním klíčovém slově (např. DISTINCT, HIGH_PRIORITY, SQL_CALC_FOUND_ROWS u SELECT pod MySQL). Váhám, jestli by nebyla stylovější syntax $sql->setIgnore(), $sql->setSqlCalcFoundRows(FALSE) apod. V tom případě bych setFlag zrušil.

#13 v6ak:- #17 v6ak: není lepší zjistit, co si vyžádá praxe?

#18 Charlie: Zend_Db to umí? Jen se ptám, nikdy jsem to nepoužíval a v manuálu to nejspíš přehlédl.

Posláno 1. 6. 2008 ve 23.38 | Odpovědět

#20 v6ak http://v6ak.profitux.cz/ nový

#19 David Grudl: Já jsem vpodstatě jen uvažoval nad implementací.

Posláno 2. 6. 2008 v 7.39 | Odpovědět
Na komentář reagoval [21] David Grudl
avatar

#21 David Grudl http://davidgrudl.com nový

#20 v6ak: nemyslel jsem to nijak zle ;)

Posláno 2. 6. 2008 v 15.47 | Odpovědět
avatar

#22 The Zero http://www.thezero.info/ nový

#19 David Grudl: Jo, třídy Zend_Db_Select a Zend_Db_Table_Se­lect, ale jen pro select a u Table zase nejdou joiny.

Posláno 2. 6. 2008 ve 20.55 | Odpovědět
Na komentář reagoval [24] Michaels

#23 Petr Procházka nový

#19 David Grudl: Používám jen pár příznaků a tak druhá varianta je kratší na zápis, přesto bych preferoval setFlag, které je hezčí.

U fetch automaticky přidávat ->limit(1) se mi zdá jako dobrej nápad. Podle čeho si myslíš že by si praxe žádala něco jiného? Jen bych asi nevyhazoval vyjímku při zadaní jiné hodnoty, to je podle mě zbytečné.

Posláno 2. 6. 2008 ve 22.56 | Odpovědět
avatar

#24 Michaels mayer.martin@gmail.com nový

#22 The Zero: I u Table jdou joiny.

Posláno 2. 6. 2008 ve 23.16 | Odpovědět
avatar

#25 David Grudl http://davidgrudl.com nový

Koukal jsem na Zend_Db_Select, to používá malinko jiný přístup. DibiFluent je a má být magické skládání příkazu, syntactic SQL sugar.

Ad fetch() a limit 1: on je to vlastně dobrej nápad ;)

Posláno 2. 6. 2008 ve 23.57 | Odpovědět
avatar

#26 rmaslo rmaslo@archa.cz nový

Něco podobného (pro akční SQL dotazy) také používám, z tohoto důvodu:

V té funkci kde to SQL poskládám vím název tabulky. Takže jsem schopen zjistit zda v adresáři „trigery“ existuje třeba PHP script nejakatabulka_af­terupdate. Pokud takovýto script najdu tak ho includnu a něm pomocí call_user_func zavolám funkci nejakatabulka_af­terupdate(). (Samozřejmě si předám to pole měněných hodnot.)

Tímto přístupem jsem schopen nahradit „db trigery“ pomocí PHP kódu (vytvářím si v PHP „datové eventy“). Což bylo ve starších verzích MySQL nutnost. I v nových verzích MySQL považuju jazyk trigerů za velmi slabý. A takové věci jako třeba smazat v trigeru soubory na disku asi nikdy nepůjdou. (např. smazat obrázek produktu (soubor na disku) při jeho rušení toho produktu v db)

Celej tenhle můj pohled na věc vychází z toho že jsem vždy preferoval aplikační logiku v trigerech nikoliv v objektech.

Takže na závěr taková zvědavá otázka na autora. Nejplánuje něco podovného? tj. třeba v metodě
dibi::update(‚pro­ducts‘, …
hledat zda neexistují funkce products_Afte­rUpdate() a products_Befo­reUpdate?
A v případě že existují je zavolat? Programátor pak třeba v products_Af­terUpdate může u toho produktu nastavit datum a uživatel,e jenž udělal poslední změnu a tím centralizovat aplikační logiku.
O využití products_Befo­reDelete pro kaskádové mazání záznamů (třeba nějakých parametrů produktu) ani nemluvě…

Posláno 6. 6. 2008 v 0.49 | Odpovědět
avatar

#27 gogloid tomas.brukner@gmail.com nový

Chtěl poukázat na chybu – s aktuální dibi (dibi 0.9 (revision 155 released on 2008/10/22 13:44:11)) nefunguje příklad

update(dibi::update('products', $record)
        ->where('product_id = %d', $id);
        ->execute();

tento ale ano:

update(dibi::update('products')->set($record)
        ->where('product_id = %d', $id);
        ->execute();

(Samotnou implementaci jsem zběžně ze zdrojáku nepochopil, tudíž nemůžu napsat bugfix :-), ale naštěstí tohle jsem tipnul a funguje to :-) )

Posláno před 26 dny | Odpovědět
avatar

#28 David Grudl http://davidgrudl.com nový

Právě naopak, první příklad funguje a druhý vygeneruje error.

dibi::update('products', $record)
    ->where('product_id = %d', $id)
    ->test();

vypíše

UPDATE `products`
SET ...
WHERE product_id = NULL
Posláno před 26 dny | Odpovědět

Zanechat komentář

  • na jiné komentáře odkazujte zápisem např. [2]
  • vaše IP bude zaznamenána a zobrazena
  • můžete použít Texy! syntaxi. HTML značky nejsou povolené. Příklad syntaxe: "text odkazu":odkaz, **tučně**, *kurzíva*, `code`
  • můžeme si tykat
  • diskuse mohou být řešeny metodou Indiana Jones
Text komentáře
Kontakt (povinné)

(maskuje se)




Výtah na začátek článku na první komentář

Názory čtenářů v diskusích nejsou názory provozovatele webu, a ten za jejich obsah neodpovídá.

TOPlist

phpFashion © 2004, 2008 David Grudlo webu

Jakékoliv užití obsahu, včetně převzetí článků nebo jejich částí, je bez předchozího písemného svolení autora zakázáno.

Ukázky zdrojových kódů smíte používat s uvedením autora a URL tohoto webu bez dalších omezení.

Tipy: Nafukovací čluny, lodě, kajaky, rafty | Navigace GPS | Spolehlivý obchod | Pevné zdraví, léky, afrodiziaka, fitness, lékárna | Parfémy | Dieta, hubnutí