Na navigaci | Klávesové zkratky

Téměř v cíli: dibi 0.9b

Vývoj databázového layeru dibi se nezadržitelně blíží k finální verzi. Vyzkoušel jsem si trošku jiný přístup k open source, existoval pouze jediný článek o této knihovně a připomínkování probíhalo v komentářích nebo přes e-maily. Za podněty děkuji zejména Tomáši Bartoňovi. Komorní atmosféra vývoje mi sedla, nemusel jsem se tolik svazovat zpětnou kompatibilitou ani řešit podporu.

Co je dibi?

Jde o minimalistický databázový layer, dobře padnoucí do ruky. Jednosouborová verze obsahující ovladače pro 8 databází (MySQL, MySQLi, PostgreSQL, SQLite, ODBC a experimentální MS SQL, Oracle a PDO) má pouhých … a neřeknu :-) Tipněte si.

Dibi má plnit tyto tři cíle:

1) zapouzdřit funkce do intuitivního rozhraní

Samozřejmě do rozhraní objektového, s využitím syntaktických cukrlátek PHP 5 a vyhazováním výjimek. Prostě místo

$link = mysql_connect(...);

$res = mysql_query('...', $link);
while ($row = mysql_fetch_assoc($res)) {
   ...
}

a pozdějšího překopávání na mysqli_query, kde jsou navíc prohozené parametry, je víc sexy psát:

dibi::connect('driver=mysqli');

$res = dibi::query('...');
foreach ($res as $row) {
   ...
}

V tomto směru není dibi nikterak objevné, vlastně je jen náplastí na můj značně nedůvěřivý vztah k open source (a nejen můj), prostě co si nenapíšu sám…

Tip jak mít dibi vždy po ruce: v php.ini si jako auto_prepend_file nastavte soubor, který bude inkludovat dibi nebo tak učiní přes autoloading. Dále nastavte přihlašovací údaje do direktiv mysql.default_user, mysql.default_password a mysql.default_host. Pak stačí v kódu napsat jen dibi::connect(); a hned můžete databázovat.

Tip pro ladění: při ladění aplikací by se vám mohly hodit tyto statické proměnné:

echo dibi::$sql; // zobrazí poslední SQL příklaz
echo dibi::$elapsedTime; // jeho doba trvání v sec
echo dibi::$numOfQueries; // celkový počet SQL příkazů
echo dibi::$totalTime; // jejich celkový čas v sec

2) asistovat při psaní SQL dotazů

Jelikož píšu aplikace na míru konkrétním databázím, bod č. 1 by nikdy nebyl impulsem k psaní další vrstvy. Tím bylo nepohodlné a otravné skládání SQL dotazů. Potřeboval jsem něco, co mi dovolí

  • zapsat dotaz maximálně přehledně (kouknu a hned vidím)
  • bude uvozovat identifikátory a řetězce
  • konvertovat typy boolean nebo date do formátu databáze
  • bude to blbuvzdorné a navíc setsakramentsky rychlé

Hledání správné'n'kůl syntaxe trvalo nesmírně dlouho a dodnes nejde o uzavřenou kapitolu vývoje. Základ tvoří systém modifikátorů…

$name = "Sinead O'Connor";
$age = "41";

dibi::query('
	SELECT *
	FROM [people]
	WHERE [name]=%s', $name, 'AND [age]>%i', $age
);

// SQLite driver ->
//   SELECT * FROM [people] WHERE [name]='Sinead O''Connor' AND [age]>41
// MySQL driver ->
//   SELECT * FROM `people` WHERE `name`='Sinead O\'Connor' AND `age`>41

…a operace nad poli i políčky:

dibi::query('INSERT INTO `people`', array(
	'name' => $name,
	'active' => true,
	'password' => hash('md5', 'nbusr123'),
));

// SQLite -> INSERT INTO [people] ([name], [active], [password])
//  VALUES ('Sinead O''Connor', 1, 'c2750a7d522eb4df4e842980ed3f3e78')
//
// PostgreSQL -> INSERT INTO "people" ("name", "active", "password")
//  VALUES ('Sinead O''Connor', true, 'c2750a7d522eb4df4e842980ed3f3e78')

Dibi také disponuje podporou transakcí, má tzv. podmíněné modifikátory (ale váhám, zda je nezrušit) a pomocníka pro aplikování limitu či offsetu do SQL dotazu. Podrobnější informace o psaní SQL příkazů najdete ve zmíněném článku.

Tip: pokud nechce používat SQL asistenta, vykonávejte příkazy metodou dibi::nativeQuery(...SQL...). Pokud si naopak chcete asistenta vyzkoušet a neprovádět žádné příkazy, zkuste dibi::test(...SQL...).

A ještě jeden: dibi umí zobrazit SQL příkaz s obarvenou syntaxí:

$sql = 'DELETE * FROM [catalog]';
dibi::dump($sql);

dibi::dump(); // bez parametru: zobrazí posledně vykonaný příkaz

3) zapouzdřit result-set a asistovat při zpracování

Po dotazu následuje odpověď a tou je buď true (u příkazů INSERT, UPDATE, …), množina výsledků (SELECT, DESCRIBE, EXPLAIN) nebo výjimka.

U množiny výsledků se zastavím. Přesněji řečeno u objektu, který ji reprezentuje.

$res = dibi::query('SELECT ...');

// můžeme ji číst po řádcích:
while ($row = $res->fetch()) {
   ...
}

// nebo ráz naráz:
$table = $res->fetchAll();

// lze nad ní iterovat:
foreach ($res as $row) ...

// a je možné dokonce rozsah iterace omezit:
foreach ($res->getIterator(3, 5) as $row) ...

// užitečná je i metoda pro získání hodnoty prvního sloupce:
dibi::query('SELECT [name] FROM ...')->fetchSingle();

Statická třída dibi umí cestu k výsledkům ještě zkrátit. Místo dibi::query()->fetch() je možné psát dibi::fetch()

$row = dibi::fetch('SELECT ...');

$field = dibi::fetchSingle('SELECT ...');

$table = dibi::fetchAll('SELECT ...');

Experimentálně jsem zkusil do dibi implementovat práci s automatickým přetypováním vrácených výsledků podle metadat, ale sám to nepoužívám.

Skutečným killerem jsou nenápadné metody fetchPairs() a především fetchAssoc(). První z nich vrátí výsledek v podobě asociativního pole ve tvaru key => value. Který sloupec představuje klíč a který hodnotu je možné volitelně specifikovat parametry.

$pairs = dibi::query('SELECT [name], [age] FROM [people]')->fetchPairs();
// --> array(
//   'Jane Doe' => 25,
//   'John Doe' => 29,
//   ...

Splněným mokrým snem programátorů je však fetchAssoc. Máte SQL dotaz spojující několik tabulek s různými typy vazeb. Databáze z toho udělá prachobyčejnou plochou tabulku. fetchAssoc jí vrátí přirozený tvar a krásu.

Příklad. Mějme tabulku zákazníků a objednávek (vazba N:M) a položíme dotaz:

$res = dibi::query('
  SELECT customer_id, customers.name, order_id, orders.number, ...
  FROM customers
  INNER JOIN orders USING (customer_id)
  WHERE ...
');

A rádi bychom získali vnořené asociativní pole podle ID zákazníka a poté podle ID objednávky:

$all = $res->fetchAssoc('customer_id,order_id');

// budeme jej procházet takto:
foreach ($all as $customerId => $orders) {
   foreach ($orders as $orderId => $order) {
	   ...
   }
}

Asociativní deskriptor má obdobnou syntax, jako když pole píšete pomocí přiřazení v PHP. Tedy 'customer_id,order_id' představuje sérii přiřazení $all[$customerId][$orderId] = $row;, postupně pro všechny řádky.

Někdy by se hodilo, aby se asociovalo podle jména zákazníka namísto jeho ID:

$all = $res->fetchAssoc('name,order_id');

// k prvkům pak přistupujeme třeba takto:
$order = $all['Arnold Rimmer'][$orderId];

Co když ale existuje více zákazníků se stejným jménem? Tabulka by měla mít spíš tvar:

$data = $all['Arnold Rimmer'][0][...];
$data = $all['Arnold Rimmer'][1][...];
...

Rozlišujeme tedy více možných Rimmerů pomocí klasického pole. Asociativní deskriptor má opět formát podobný přiřazování, s tím, že sekvenční pole představuje mřížka:

$all = $res->fetchAssoc('name,#,order_id');

// iterujeme všechny Arnoldy ve výsledcích
foreach ($all['Arnold Rimmer'] as $arnoldOrders) {
   foreach ($arnoldOrders as $orderId => $order) {
	   ...
   }
}

Je to srozumitelné? Pokud ne, zkuste si to přečíst ještě jednou. Bude následovat další level.

Vrátím se k příkladu s deskriptorem 'customer_id,order_id' a zkusím vypsat objednávky jednotlivých zákazníků:

$all = $res->fetchAssoc('customer_id,order_id');

foreach ($all as $customerId => $orders) {
   echo "Objednávky zákazníka $customerId":

   foreach ($orders as $orderId => $order) {
	   echo 'Číslo dokladu: ',  $order['number'];
	   // jméno zákazníka je v $order['name'];
   }
}

Bylo by hezké místo ID zákazníka vypsat jeho jméno. Jenže to bych musel dohledávat v poli $orders. Docela by se šiklo, kdyby výsledky byly v takovémto nějakém tvaru:

$all[$customerId]['name'] = "John Doe";
$all[$customerId]['order_id'][$orderId] = $row;
$all[$customerId]['order_id'][$orderId2] = $row2;

Tedy mezi $customerId a $orderId vrazit ještě mezičlánek. Tentokrát ne číslované indexy, jaké jsem použil pro odlišení jednotlivých Rimmerů, ale rovnou databázový záznam. Řešení je velmi podobné, jen si stačí zapamatovat, že záznam symbolizuje rovnítko:

$all = $res->fetchAssoc('customer_id,=,order_id');

foreach ($all as $customerId => $record) {
   echo "Objednávky zákazníka $record[name]":

   foreach ($record['order_id'] as $orderId => $order) {
	   echo 'Číslo dokladu: ',  $order['number'];
   }
}

Docela by mě zajímalo, jestli jste teď zmatení nebo nadšení :-) Asociativní deskriptor je úžasně trefný způsob, jak popsat strukturu libovolně složitého vnořeného pole, jenže uvažovat v několika rozměrech dává lidskému mozku docela zabrat. Přiznám se, že sám občas docela tápu. Celá nápad s deskriptory a realizaci metody fetchAssoc připisuji nějakému vyššímu vnuknutí, protože ji pokaždé musím vstřebávat znovu a znovu. Ano, tento článek si píšu jako tahák pro vlastní kód :-)

Tip: při ladění aplikace se může hodit vykreslení množiny výsledků v podobě HTML tabulky:

$res = dibi::query('SELECT ...');
$res->dump();

Téměř v cíli

Teď pracuji na logování a profilování provozu. Dibi umožní volat před a po každém SQL příkazu handler, takže bude možné sledovat provoz vlastními nástroji. Stejně jako celé dibi, tak i tato funkčnost se rodí a formuje za chodu. S tím, jak zjišťuji, co vlastně potřebuji a co mi nejlépe vyhovuje. Trvá to sice déle, ale jen tak může vzniknout skutečně užitečný nástroj.

A co Active Record, Object-relational mapping? Dibi bylo zamýšleno jinak, jako pomocník při psaní SQL příkazů. Faktem je, že by mohlo fungovat i jako spolehlivá low-level vrstva pro ORM nadstavbu, ale nemám v plánu to programovat. Tyto techniky sice elegantně řeší určitou sortu úloh, ale vybírají si za to daň v podobě zcela nových problémů. Nejsem jejich fanda (toho času).

Co dibi versus PDO? Obojí jsou data-access abstraction layer, liší se však v jedné podstatné věci: dibi je. Kdežto narazit na hosting s pdo_mysql je úkol hodný zlatokopa. Navíc po dvou letech vývoje mi dibi vyhovuje natolik, že přejít k PDO by byl skok zpět (dokonce i výkonostní). V PDO také chybí některé důležité funkce, např. vyznačení identifikátorů. Stále jej považuji za nedodělek, nicméně má budoucnost jistou, proto existuje PDO driver pro dibi.

Jo, a ještě licence. Dibi šířím pod Dibi licencí (překlad), což je licence ve stylu velmi volné BSD, přidal jsem však několik omezení navíc, které se běžných uživatelů nedotknou. Dibi můžete použít prakticky v jakémkoliv projektu (komerčním, nekomerčním), nesmíte však odstranit nebo zatajit copyrighty. To byste se nedostali do nebe!

Download dibi

Komentáře

  1. Jakub Lucký #1

    avatar

    >> To byste se nedostali do nebe!
    Pokud narážíš na Švejka, tak tam je: To by vás ani nesměli pohřbít na hřbitově! (Scéna s Janem Marvanem jako velitelem četnické stanice a jejich posluhovačkou)

    Jinak dibi licence vypadá velmi dobře, protože nejde o úchylné GPL ale rozumné BSD

    před 16 lety | reagoval [13] tark
  2. Ivan #2

    avatar

    Tejto vety sa stale desim: Hledání správné‚n‘kůl syntaxe trvalo nesmírně dlouho a dodnes nejde o uzavřenou kapitolu vývoje.

    BTW: ako to, ze tato kadibudka nema PR, je v tom nejaka certovina?

    před 16 lety | reagoval [15] David Grudl
  3. Tom #3

    hip hip hurá, na tohle se těším víc než na Vánoce :)

    před 16 lety | reagoval [15] David Grudl
  4. Milan #4

    No ještě dokončit Nette a bude to paráda

    před 16 lety | reagoval [6] veena
  5. Honza #5

    Super práce, díky.

    před 16 lety
  6. veena #6

    #4 Milane, Nechcete někdo přeložit článek https://phpfashion.com/…bazovy-layer do angličtiny a přidat k němu zpracování odpovědi z tohoto článku?
    Ať má David čas na programování? ?

    před 16 lety | reagoval [13] tark [15] David Grudl
  7. veena #7

    S tim fetchAssoc je to skvělý nápad. Před prvním použitím bych si to sice musel znovat přečíst, ale chápu, o co ti jde. A věřim, že to rychle přeleze do krve.

    V této souvislosti mě napadlo toto. Když má člověk model typicky jako objekty, tak by se mu hodilo aby tam bylo i něco jako fetchObject(), které by nacpalo výsledek do atributů objektu.
    Což by chtělo zas nějak pěkně pořešit výsledek ze spojených tabulek, že se třeba instancují objekty do pole…

    Příklad by pak mohl vypadat takto:

    $res = dibi::query('
      SELECT customer_id, customers.name, order_id, orders.number, ...
      FROM customers
      INNER JOIN orders USING (customer_id)
      WHERE ...
    ');

    Získaní dat:

    $customers = $res->fetchObject('customer_id,order_id');

    Struktura metody fetchObject by mohla vypadat nějak takhle:

    function fetchObj() {
    $customers = array();
    $i = 0;
    while ($row = $this->res->fetch()) {
      $customers[$i] = new Customer($row['customer_id']);
    /* nebo variata takhle:
      $customers[$row['customer_id']] = new Customer($row['customer_id']);
    */
      $customers->setName($row['name']);
      $customers->orders = array();
      for ($j = 0; $j < pocet objednavek; $j++) {
        $customers->orders[$j] = new Order($row['order_id']);
        $customers->orders[$j]->setNumber($row['number']);
      }
      $i++
    }
    return $customers;
    }
    
    // budeme jej procházet takto:
    foreach ($customers as $customer) {
       foreach ($customer->orders as $order) {
           ...
       }
    }

    Ještě by se mohla mít metoda fetchObj() volitelné parametry jestli instancovat třídy (a jaké na které idčka) pomocí new, nebo jestli získat objekt pomocí (tovární) metody. A které atributy nastavit set metodama a které narvat přímo do konstruktoru.

    Neni to ORM a přitom dostaneš výsledek jako bys použil ORM ? A nemusíš se trápit s ruční jednotvárnou prací vytvářenim objektů po každym selektu (opruz ☹

    Pak by byl new Order nastolen ?

    před 16 lety | reagoval [9] rarouš [16] David Grudl
  8. Taco #8

    avatar

    Mám takový pocit, že už nejsi tak kategoricky proti ORM jako na začátku. Že by jsi při svém vývoji dibi narazil na její omezení? ?

    před 16 lety
  9. rarouš #9

    avatar

    #7 veeno, to co navrhuješ je ORM, ne že neni.

    před 16 lety | reagoval [11] veena
  10. veena #10

    [smazáno] nejsem si jistý, jestli to, co píšete, se dá hodit právě na dibi. Jak tuhle knihovnu chápu, tak si můžete napsat libovolný sql příkaz.

    S různě formátovanými daty (i datumy) v různých db enginech by právě mělo dibi pomoci. Viz odstavec modifikátory v dokumentaci dibi na:
    https://phpfashion.com/…bazovy-layer
    Jasně asi Vám to příliš nepomůže s procedurami, rozšířeními SQL jazyka apod. To ale Vámi zmiňované knihovny také ne.

    PDO je povedený nástroj v php. Ale není to database abstraction layer ale data-access abstraction layer. Takže s úpravou sql dotazů pro různé db enginy vám nepomůže.
    Nicméně jak David dneska popsal, zaimplementoval i experimentální adaptér na PDO.

    Ze začátku jsem byl k dibi skeptický. Nyní ale oceňuji dobrý návrh a užitečné funkce, které programátorům šetří čas. Pokud myslíte, že vám ne, tak to samozřejmě nepoužívejte.

    před 16 lety | reagoval [15] David Grudl
  11. veena #11

    #9 rarouši, Já myslim, že ne. ORM by mělo fungovat obousměrně. O->R i R->O. V ORM nepíšeš žádný SQL dotaz, ale voláš metodu mapperu, která pozná, jaké SQL má zkonstruovat.

    To, co navrhuju, je „jen“ jednosměrný mapper výsledku do objektů. R->O.

    před 16 lety | reagoval [30] rarouš
  12. David Majda #12

    avatar

    „Po dotazu následuje odpověď a tou je buď true (u příkazů INSERT, UPDATE, …)“

    A nebylo by lepší vracet automatické ID vloženého/upravovaného záznamu, existuje-li? Docela se to hodí v případě, kdy s ním potřebuju dál pracovat (vložím záznam a pak chci vložit další záznamy do jiné tabulky, která je s tou původní v relaci 1:N nebo N:M).

    před 16 lety | reagoval [15] David Grudl [26] Milan Svoboda
  13. tark #13

    avatar

    Vypadá to fajnově, nemůžu se dočkat, až vyzkouším kombinaci Dibi + Texy + Nette. Nemáš v záloze ještě nějaký sexy projekt? ;)

    #1 Jakube Lucký, JJ, GPL je úchylné, pokud není (např. jako u Texy!) možnost použít jinou licenci v uzavřené aplikaci… Pak má člověk chuť aplikaci pro jistotu nepoužít, protože pak není možnost distribuovat aplikaci jinak, třeba komerčně pro klienty s uzavřeným zdrojákem a tak.

    #6 veeno, Já bych to klidně udělal, jenže problém je v tom, že anglicky nějak moc neumím, takže bych Davidovi udělal leda ostudu ?

    před 16 lety | reagoval [15] David Grudl
  14. Keff #14

    avatar

    DGX, můžu se zeptat jaké jsou ty problémy které ORM přináší? Momentálně jsem ve stadiu téměř nekritického nadšení a upravil jsem si jeden malý ORM framework, ale rád bych věděl co mě čeká :)

    před 16 lety | reagoval [15] David Grudl
  15. David Grudl #15

    avatar

    #2 Ivane, obáváš se toho kvůli nekompatibilitě? Už mi dibi běží na tolika projektech, že bych do toho nešáhl.

    #3 Tome, ovšem to bude chyba ve výchově a rodiném zázemí ?

    #6 veeno, vydržte, chodili by mi sem lidi ;)

    #10 veeno, joj, než jsem si všiml, že reaguješ, tak doběhl Indiana Jones a pána smazal… On je někdy strašně ukvapený, kluk ušatá.

    #12 Davide Majdo, nad tím jsem váhal, ale nakonec ve prospěch čitelnosti kódu aplikací, které dibi používají, to zavrhl. Nehledě na další problémy s tím spojené (jak zjistit který SQL dotaz ID vrací, co když je dotazů víc, jak to řešit u databází s odlišným přístupem, atd). Nad čím ale uvažuju, tak je fluent interface a vracení $this.

    #13 tarku, obvykle je nejjednodušší napsat autorovi a domluvit se s ním. GPL vůbec nezavrhuji, jen mám pocit, že se pro tento typ knihovny nehodí.

    #14 Keffe, to je značně subjektivní. Souvisí to s pocitem, kdy se dostaneš do situace, kterou bys uměl hned a snadno vyřešit v SQL, ale s ORM je to hodně složitější. Nebo budeš muset zoptimalizovat operace nad databází, v SQL by věděl hned, ale s ORM to třeba vůbec nepůjde.

    před 16 lety | reagoval [17] tark
  16. David Grudl #16

    avatar

    #7 veeno, tak teď si ale držte klouboky ?

    // znáte od tvůrců jazyka JavaScript
    function DibiResult_prototype_fetchObject(DibiResult $obj, $param, $param, ...)
    {
        $customers = array();
        ... // Veenův kód
        return $customers;
    }
    
    $res = dibi::query('
        SELECT customer_id, customers.name, order_id, ...
        FROM customers
        INNER JOIN orders USING (customer_id)
    ');
    
    $customers = $res->fetchObject('customer_id,order_id');
    // $customers je naplněn tvou metodou!

    Jinými slovy, můžete si přidávat vlastní metody do každé třídy dibi (tedy nestatické). Naznačoval jsem to na frameworkové konferenci, v dibi už to běží naostro.

    Takže pokud ráčíte používat třeba novou fetch metodu, netřeba mě přesvědčovat o její implementaci, snadno si ji doplníte sami ?

    před 16 lety | reagoval [17] tark [20] veena
  17. tark #17

    avatar

    #15 Davide Grudle, Jo, to je pravda, to mi nedošlo ;)

    #16 Davide Grudle, Fajné a pěkné ;)

    před 16 lety
  18. Dundee #18

    avatar

    Ta funkce fetchAssoc vypadá opravdu zajímavě. Nikdy mě nic podobného nenapadlo.

    před 16 lety
  19. Inza #19

    Problematika DB se nám teď, ze dne na den, posunula na novou úroveň. Fascinující. Velmi se těším:-) – Jestli se o někom z naší republiky dá říct že UMÍ programovat v PHP, tak je to David – On není jako my ostatní, co se v tom jen patláme:-)…on programuje vysoko, kam mi nedosáhneme – o důvod víc, proč ho podporovat:-) – jen tak dál!!

    před 16 lety
  20. veena #20

    #16 Davide Grudle, kurňa, teď nevím, jestli mám jásat, nebo plakat, že si to musím naprogramovat sám ;-D

    před 16 lety
  21. johno #21

    avatar

    Čo mi dáte, keď nad DiBi spravím pekný, malý a šikovný OR mapper?

    před 16 lety | reagoval [22] tark [23] David Grudl [25] veena
  22. tark #22

    avatar

    #21 johno, To je co, smím-li se zeptat? ;)

    před 16 lety | reagoval [24] johno
  23. David Grudl #23

    avatar

    #21 johno, polovina ekonomického zisku z dibi je tvá ?

    (pak ti pošlu své číslo účtu, abysme se vyrovnali)

    před 16 lety | reagoval [24] johno
  24. johno #24

    avatar

    #22 tarku, Počkejte, ttto to jsem se měl přece zeptat já.

    #23 Davide Grudle, Pričom, ten prevod peňazí by ma stál asi najviac.

    před 16 lety
  25. veena #25

    #21 johno, johno podvádí, von už to má vymyšlený dopředu ?

    P.S. jdi do toho!

    před 16 lety
  26. Milan Svoboda #26

    #12 Davide Majdo, To co navrhuješ není zase tak triviální. Některé DB systémy jsou takovou informaci schopny vracet, ale bohužel najdou se i systémy, které pokud tě ID zajímá musíš ho nejdříve získat a poté teprve s ním pracovat, tzn. musíš ho použít přímo do insertu.

    před 16 lety
  27. David Grudl #27

    avatar

    Aktualizace: do článku jsem doplnil odstaveček na téma „dibi vs. PDO“ a pět praktických tipů ze života dibaře

    před 16 lety
  28. Tomik #28

    avatar

    Davide, moc se mi to líbí! Zatím jsem to nikde nepoužil, ale už se vyloženě těším. ?

    Kdyby se rozdávali Nobelovy ceny za PHP, byl bych svými všemi 104 klávesami pro udělení té ceny Tobě.

    před 16 lety
  29. Tomas Lembacher #29

    avatar

    Ahoj, nevim jestli to tu uz nekdo napsal, nebo jestli jste na to uz nahodou neprisel…ale mam pocit, ze jsem nasel malou chybu ve zdrojovem kodu dibi. V souboru DibiTranslator.php na radku 116 jsem presvedcen, ze by misto „REPLAC“ melo byt „REPLACE“.

    S pozdravem
    Tomas Lembacher

    před 16 lety | reagoval [32] David Grudl
  30. rarouš #30

    avatar

    #11 veeno, ORM je proces při kterém mapuješ relační data na objekty či naopak. Je celkem fuk, jestli k tomu použiješ nějakej framework (mapper) nebo všechno uděláš růčo. Tak či tak jde o ORM.

    před 16 lety
  31. Washo #31

    avatar

    Jak je to s chybovosti? Docela me zklamalo, kdyz jsem zahodil PHPDoctrine, protoze jsem prisel kazdou chvili na chyby, ktere me nastvaly. Znate to: Pouzivate cizi kod, nevite jestli chybu delate vy a nebo knihovna ⇒ nekolik hodin v haji kvuli blbosti.

    Chtel jsem se proto zeptat o kolika chybach vis, resp. kolik jich ocekavas?

    před 16 lety | reagoval [32] David Grudl
  32. David Grudl #32

    avatar

    #29 Tomasi Lembachere, díky za report, ale chyba to není.

    #31 Washo, dibi sem o nějaké věděl, tak bych ji už opravil

    před 16 lety
  33. Pavel Stehule #33

    avatar

    Ahoj.

    podporuje to prepared statements? Mám na mysli hlavně Asistenta?

    před 16 lety | reagoval [34] David Grudl
  34. David Grudl #34

    avatar

    #33 Pavle Stehule, prepared statements podporované nejsou, vysvětlení zde. Technicky není problém je do knihovny přidat, ale smysl v tom nevidím.

    (prepared statements prý v MySQL zhoršují výkon)

    před 16 lety
  35. Michal Till #35

    Velmi pěkné!

    Mě se ještě často hodí funkce, co vrátí jednu jedinou hodnotu z SQL výsledku, něco jako

    $count = dibi::fetchValue(„SELECT count(id) FROM …“);

    před 16 lety | reagoval [37] Jan Škrášek [38] David Grudl
  36. PIF #36

    avatar

    žeru MyDateTime :))

    před 16 lety | reagoval [38] David Grudl
  37. Jan Škrášek #37

    avatar

    #35 Michale Tille,

    // užitečná je i metoda pro získání hodnoty prvního sloupce:
    dibi::query('SELECT [name] FROM ...')->fetchSingle();
    před 16 lety
  38. David Grudl #38

    avatar

    #35 Michale Tille, dibi::fetch($sql), dibi::fetchSingle($sql) a dibi::fetchAll($sql) tam je

    #36 PIFe, tak dobrou chuť ;)

    před 16 lety
  39. Luke #39

    avatar

    Nefungoval mi driver postgre a tak po úprave súboru v drivers, konkretne >

    $this->connection = @pg_pconnect("host=".$config['host']." user=".$config['username']." password=".$config['password']." dbname=".$config['database'], PGSQL_CONNECT_FORCE_NEW);

    to už ide … neviem ci to bolo spôsobené len tým že som sa pripájal na iný host alebo či si len robil dáke zmeny a na tento driver si zabudol.

    před 16 lety | reagoval [40] David Grudl
  40. David Grudl #40

    avatar

    #39 Luke, no, já předávám funkci pg_connect přímo ten poskládaný connection_string, ale asi by nebylo špatné přidat i skládací funkci. Tak zkus aktuální verzi:

    dibi::connect(array(
        'driver'     => 'postgre',
        'host'       => 'localhost',
        'port'       => 5432,
        'dbname'     => 'mary',
    ));
    před 16 lety | reagoval [46] Luke
  41. Mastodont #41

    No já tedy nevím, ale „správnou a cool syntaxi“ psaní SQL dotazů bych si představoval úplně jinak. O asistenci se taky moc mluvit nedá.

    Když už, tak bych si nepomáhal jednou metodou, ale používal bych jich víc:

    dao->getAllRows("orders", Array("id", "date"));
    // = select id, date from orders
    dao->getSomeRows("orders", Array("id", "date",), "customer_id", 6);
    // = select id, date from orders where customer_id = 6)

    A pokud se týká zpracování výsledků, tak to by měla být starost jiné třídy, ne db layeru. Přičemž rozhraní mezi třídami tvoří result.

    echo formatter::Table(dao->getRows(...));
    před 16 lety | reagoval [42] David Grudl
  42. David Grudl #42

    avatar

    #41 Mastodonte, jenže to už není „psaní SQL dotazů“. Všimni si podstatné věci – tvůj zápis je delší a mnohem méně intuitivní, než pod tím vygenerovaný SQL příkaz. Proto to hodnotím jako krok zpět (viz Zlo si říká Database Abstraction Layer). Jdu jinou cestou, která tu přímočarost SQL příkazů zachovává.

    Experimentuji taky s více inteligentním zapouzdřením příkazů SELECT, INSERT nebo UPDATE, vlastně už pár let, ale stále nejsem spokojen.

    ad zpracování výsledků: dibi samozřejmě končí u poskytnutí resultu, výsledky nezpracovává.

    před 16 lety
  43. Honza M. #43

    avatar

    Fatal error: Class ‚NClass‘ not found in C:\…\dibi.compact.php on line 326.

    Kde vezmu NClass? Bývalo to v souboru NObject.php, ale teď už se asi NClass nevyrábí…

    před 16 lety | reagoval [44] David Grudl
  44. David Grudl #44

    avatar

    #43 Honzo M., divné, nic se totiž neměnilo. Zkus stáhnout aktuální verzi. Podívat se, jestli to nekoliduje s jinou třídou, jiným souborem…

    (update: NClass je nyní v samostatném souboru, ale stále součástí dibi.compact.php)

    před 16 lety | reagoval [45] Honza M.
  45. Honza M. #45

    avatar

    #44 Davide Grudle, Tlouklo se mi to se souborem NObject.php z NFormů…
    Doufám, že tyhlety NObejcty a NClassy od dibi a NForm jsou navzájem kompatibilní ? Zatim to vypadá dobře.

    před 16 lety
  46. Luke #46

    avatar

    #40 Davide Grudle, To som samozrejme skúsil a nešlo to, overil som to teraz ešte raz … podla mňa je asi problém v skladaní toho connection stringu, kde názov databázy je s podtržítkom … aspon z vyhodenej výnimky tomu tak rozumiem …

    před 16 lety
  47. Luke #47

    avatar

    A neviem ci mam niekde chybu, ale nevracia mi insertId posledne idcko, ak je postgre verzia 8.0 …

    před 16 lety | reagoval [48] David Grudl
  48. David Grudl #48

    avatar

    #47 Luke, v postgre se používá dibi::insertId($sequenceName);. Každopádně si stáhni poslední verzi, v rev. 99 byl bug.

    před 16 lety
  49. liquid #49

    Ahoj,
    mám takovej problém s regexp, tak mi tu snad někdo poradí. Přepsal jsem si dibi do C++, ale umí to zatím jenom nativeQuery. Chtěl bych udělat i query, ale nemůžu se poprat s jedním použitým regexp v C++. Konkrétně se jedná o tenhleten:

    preg_replace_callback(
    ‚/(?=|\[|\'|"|%)(?:(.+?)`|\[(.+?)\]|(\‘)((?:\‚\‘|[^\‚]))\‘|(„)((?:""|[^“]))„|%(else|end)|%([a-zA-Z]{1,3})$|(\‚|“))/s‘,
    array($this, ‚cb‘),
    substr($value, $toSkip)
     );

    umístěný v Translatoru. Díky za každej tip, kterej mi to pomůze vyřešit.

    před 16 lety
  50. Honza M. #50

    avatar

    Ahoj, jako vždy mám geniální nápad… Co kdyby dibi umělo vybírat více položek podle pole.

    $res = dibi::query("select * from [table] where [id]=%or", array(1, 6, 7, 4));
    // select * from `table` where (`id`=1 or `id`=6 or `id`=7 or `id`=4)
    
    // nebo
    
    $res = dibi::query("select * from [table] where [id]=%and", array(1, 6, 7, 4));
    // select * from `table` where (`id`=1 and `id`=6 and `id`=7 and `id`=4)

    Jistě by to bylo prospěšné, teď to řeším jakousi externí funkcí.

    $res = dibi::query("select * from [table] where " . sqlOr("id", array(1, 6, 7, 4))); // nebo sqlOr("id", "1,6,7,4");
    před 16 lety | reagoval [51] David Grudl
  51. David Grudl #51

    avatar

    #50 Honzo M., to lze:

    $res = dibi::query("
    select *
    from [table]
    where [id] IN ", array(1, 6, 7, 4)
    );
    před 16 lety
  52. Finwe #52

    Mám nějakou možnost zjistit (nejlépe při volání dibi::dump()), na kterém řádku v mém kódu byla volána metoda dibi::query(), resp. dibi::fetch() a ostatní?

    přidávám si (za určitých podmínek pro testování) handler

    function dumpAfterQuery() {
    	dibi::dump();
    }

    a tam by se toto značně hodilo

    před 16 lety | reagoval [53] David Grudl [55] Finwe
  53. David Grudl #53

    avatar

    #52 Finwe, zkus analyzovat debug_backtrace.

    před 16 lety | reagoval [54] Finwe
  54. Finwe #54

    #53 Davide Grudle, No jasně, tam je pod klíčem 5, myslel jsem, jestli to dibi neumí nějak interně.

    Systém handlerů je boží a bude ještě víc, až bude hotov a zdokumentován :) Takhle se mi ta query dumpuje dvakrát…

    před 16 lety
  55. Finwe #55

    Zatím asi ideální řešení na #52 Finwe je subclassovat dibi. Například takto:

    class db extends dibi
    {
    
    	public static $debug = false;
    
    	public static function setDebug($debug = true)
    	{
    		self::$debug = $debug;
    	}
    
    
    	public static function query($args)
    	{
    		$return = parent::query($args);
    
    		if (self::$debug) {
    			$bt = debug_backtrace();
    
    			echo '<pre>';
    			echo $bt&#91;0&#93;['file'] . ':' . $bt&#91;0&#93;['line'];
    			echo '</pre>';
    
    			dibi::dump();
    		}
    
    		return $return;
    	}
    }

    pokud je pak v kódu nastaveno db::setDebug(), po db::query() se vypíše dump spolu se souborem a číslem řádku.

    bohužel jsem ještě nepřišel na to, jak zajistit, aby se to tak chovalo automaticky i pro metody db::fetch() a ostatní :(

    před 16 lety
  56. miloš #56

    ahoj, nechcem ti to tu meniť na poradňu, ale s takýmto errorom si ako poradím? :)

    Fatal error: Interface 'Countable' not found in .../www_root/dibi.compact.php on line 206

    všetko čo som o interface Countable vygooglil je, že releasol vo verzii 5.1. Hmm phpversion() mi vráti 5.2.4. Niečo som zabudol, alebo som hlúpy? :). Na localhoste mi to beži, na webe nie.
    Všetko čo robím je: dibi::connect(…)

    Ďakujem za prípadné odpovede.

    před 16 lety | reagoval [57] David Grudl
  57. David Grudl #57

    avatar

    #56 miloši, leda že jde o nějaký obskurní hosting, na kterém není SPL. Poznáš to z phpinfo()

    před 16 lety
  58. Finwe #58

    Podle toho, co jsem zjistil, načítá dibi drivery sama a používá k tomu class_exists – tato funkce ovšem defaultně používá autoload, a to může aplikaci s pro dibi netypickým formátem autoloadu solidně zmást. Pokud tedy nemám explicitně spolu s dibi requirovat php soubor ovladače, nebylo by lepší místo

    if (!class_exists($class)) {

    použít

    if (!class_exists($class, false)) {

    ?

    Jde o soubor DibiConnection.php:77 rev. 104 (v revizi 110 je to stejně)

    před 16 lety | reagoval [59] David Grudl
  59. David Grudl #59

    avatar

    #58 Finwe, to je správná připomínka, false tam skutečné chybí. Jen nějak nerozumím tomu zmatení a netypickému formátu autoloadu…

    před 16 lety | reagoval [60] Finwe
  60. Finwe #60

    #59 Davide Grudle, Zmást = nechat vyhodit fatal error ? A netypický = „blbý“ (například ve stylu „očekávám třídu ve formátu {classname}.class.php a ne jinak“).

    Nic víc ?

    před 16 lety
  61. Petr #61

    avatar

    Ahoj, narazil jsem na drobný problém, a sice, jak se při výjímce dostat k číselnému kódu poslední chyby? Konkrétně u ovladače pro sqlite. Jde o PHP funkci sqlite_last_error, eventuelně metodu SQLiteDatabase->lastError(). Prolezl jsem zdrojáky i dokumentaci API, ale žádnou cestu jsem nenašel :(

    před 16 lety

Tento článek byl uzavřen. Už není možné k němu přidávat komentáře.


phpFashion © 2004, 2024 David Grudl | o blogu

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