Na navigaci | Klávesové zkratky

dibi – pokrokový databázový layer


Uplynulo sedm měsíců od doby, kdy jsem tu poprvé psal o databázovém layeru dibi. Nechtěl jsem předvádět hotové řešení, spíš otevřít diskusi. Ale místo podnětů mi přišlo několik desítek žádostí o zdrojové kódy ;)

Konečně mohu všechny žadatele potěšit. Náhledová verze je k dispozici:

Upozornění: Knihovna dibi se neustále vyvíjí. Její popis v tomto článku postupně aktualizuji, takže některé komentáře pod ním mohou být již nesouvisející. Aktuální informace najdete na webu dibiphp.com.

Řešení, které jsem navrhoval v původním článku, dnes považuji z více důvodů za překonané. Co se však nezměnilo, to jsou cíle tohoto layeru:

  • maximálně ulehčit práci programátorům. Jak?
    • zjednodušit zápis SQL příkazů, co to jen půjde
    • snadný přístup k metodám, i bez globálních proměnných
    • funkce pro několik rutinních úkonů
  • eliminovat výskyt chyby. Jak?
    • přehledný zápis SQL příkazů
  • přenositelnost mezi databázovými systémy
    • automatická podpora konvencí (escapování/slashování, uvozování identifikátorů)
    • automatické formátování spec. typů, např. datum, řetězec
    • sjednocení základních fcí (připojení k db, vykonání příkazu, získání výsledku)
  • a především KISS (Keep It Simple, Stupid)
    • zachovat maximální jednoduchost
    • raději jeden geniální nápad, než 10.000 hloupých řádků kódu

A naopak záležitosti, o které mi vůbec nejde:

  • zajištění kompatibility SQL příkazů
  • emulace funkcí chybějících některým databázím
  • vytvoření bohatých knihoven plných funkcí
  • nechci konkurovat ActiveRecords apod., jde mi jen o čisté SQL

A také neřeším následující věci (a vysvětlím proč):

  • funkce pro zkoumání struktury databáze a tabulek
  • prepared SQL statements

Pokud neprogramujete aplikaci typu phpMyAdmin, tak žádné funkce na zkoumání databázové struktury nepotřebujete. Vlastně bych řekl, že jejich potřeba vypovídá o špatně navržené aplikaci. Dokud tuto funkčnost nebudu potřebovat, nebo ji nenaprogramuje někdo jiný, tak v dibi nebude 😉

Prepared SQL statements jsem taktéž shledal zbytečnými. Proč? Především se mi nikdy nestalo, že bych v jednom skriptu volal tolikrát tentýž SQL příkaz lišící se jen v hodnotách parametrů. Za druhé se podle mých měření zrychlení dosažené pomocí prepare pohybuje v řádu procent. V reálném nasazení je tedy naprosto zanedbatelné. Oproti tomu takový vícenásobný INSERT, který dibi podporuje, umí zrychlit vkládání až tisícinásobně. A nakonec – výhody, které prepared statements přináší z programátorského hlediska, tedy pohodlné vkládání proměnných, řeší dibi výrazně lépe.

Takže pojďme se podívat, jak to celé funguje.

Připojení k databázi

Každé spojení je reprezentováno objektem DibiConnection. To komunikuje s databází přes ovladač (třída implementující IDibiDriver). Který ovladač použít zvolíme při vytváření objektu:

$options = array(
	'driver'   => 'mysql',
	'host'	 => 'localhost',
	'username' => 'root',
	'password' => '***',
	'database' => 'table',
);

// v případě chyby vyhodí DibiException
$connection = new DibiConnection($options);
$connection->query('TRUNCATE `table`');

Ale na tento způsob můžete klidně zapomenout 🙂 Je tu totiž statický registr dibi. Ten má za úkol udržovat v globálně dostupném úložišti objekt (či objekty) spojení a nad nimi volat potřebné funkce:

dibi::connect(array(
	'driver'   => 'mysql',
	'host'	 => 'localhost',
	'username' => 'root',
	'password' => '***',
	'database' => 'test',
	'charset'  => 'utf8',
));

Statická třída dibijednu báječnou výhodu – kdekoliv je po ruce. Nemusíte získávat instanci připojení, prostě napíšete dibi:: a máte vystaráno.

Není to sice obvyklé, ale může se stát, že budete v aplikaci používat více připojení, třeba k různým databázím. Pak si každé připojení pojmenujete při připojování

dibi::connect($options1, 'prvni pripojeni');
dibi::connect($options2, 'druhe pripojeni');

a kdykoliv si je buď vytáhnete z registru…

$connection = dibi::getConnection('druhe pripojeni');
$connection->query(...);

…nebo jej tzv. aktivujete a voláte přes třídu dibi:

dibi::activate('prvni pripojeni');
dibi::query(...);

Poznámka: připojování ve stylu DSN, kdy popis připojení je uložen v řetězci připomínajícím URI, se v praxi ukázalo jako nepraktické. Používám raději pole, přípustný je však i řetězec, a to ve standardizovaném formátu HTTP query.

SQL příkazy – tak to je bomba!

Přiznám se, že způsob zápisu SQL příkazů jsem hledal šíleně dlouho. Nakonec jsem dospěl k technice, která je nesmírně prostá, intuitivní a doslova návyková:

dibi::query('SELECT * FROM [table] WHERE [id] = %i', $id);

$arr = array(
	'pole' => 'hodnota',
	'bit'  => true,
);
dibi::query('INSERT INTO [table]', $arr);

dibi::query('UPDATE `table` SET ', $arr, 'WHERE `id`=%i', $x);

Jak vidíte, SQL příkaz se zapisuje jako série parametrů a před vložením proměnné uvedeme modifikátor (např. %i). Pokud ho neuvedeme, zjistí se typ automaticky (samozřejmě nelze zjistit typy jako je datum apod).

Upozornění: modifikátor se musí nacházet zcela na konci řetězce.

Proměnná na naformátuje do výsledného SQL podle pravidel aktivní databáze. Tak třeba true bude v MS SQL jako –1, jinde jako ‚1‘. Stejně tak se zformátují řetězce, časové údaje, atd.

Modifikátory jsou následující:

%s string
%sn string, ale '' se přeloží jako null
%b boolean
%i %u integer
%f float
%d datum (očekává string nebo integer)
%t datum & čas (také string či integer)
%n identifikátor (tedy název tabulky či sloupce)
%sql SQL – řetězec ponechá beze změny
%lmt speciální – určuje limit
%ofs speciální – určuje offset
%ex speciální – expanduje pole

Pokud za modifikátorem následuje null, vloží se do databáze null. Pokud následuje pole, tak se modifikátor aplikuje na všechny jeho prvky. Ty se pak vloží do SQL oddělené čárkama.

Vždy používejte modifikátor %s před proměnnou s řetězcem. Dibi by pak nemohlo rozlišit, co je SQL příkaz (tzv. embedded SQL) a co řetězec. V tomto příkladu je funkce dibi::query volána s dvěma argumenty, první je řetězec představující (embedded) SQL, druhý je řetězec představující řetězec. Modifikátor %s to odliší:

$text = "I'm fine";
dibi::query('UPDATE `table` SET `text`=%s', $text);
// MySQL: UPDATE `table` SET `text`='I\'m fine'
// ODBC:  UPDATE [table] SET [text]='I''m fine'

Proč používám termín embedded SQL? Protože jak vidno, i toto SQL prochází zpracováním, aby vyhovovalo konvencím dané databáze. Identifikátory (jména tabulek a sloupců) uvozuji do hranatých závorek nebo zpětných uvozovek (je to jedno), dále řetězce značím jednoduchými či dvojitými uvozovkami, ale na výstup se dostane vždy to, co databáze žádá. Příklad

dibi::query("UPDATE `table` SET [text]='I''m fine'");

// MySQL: UPDATE `table` SET `text`='I\'m fine'
// ODBC:  UPDATE [table] SET [text]='I''m fine'

Ještě doplním, že uvozovka se uvnitř řetězce v embedded SQL zapisuje zdvojením. Lomítko má totiž v PHP řetězci zvláštní význam, muselo by se tedy použít dvojité, což leda komplikuje život a cílem dibi je opak.

Formátování polí

Jak jsem už psal, modifikátor je možné aplikovat také na všechny prvky pole, které se pak oddělené čárkami vloží do SQL. Ovšem můžeme využít také dvou speciálních modifikátorů %a nebo %v.

%a assoc [key]=val, [key2]="val2", ...
%v values ([key], [key2], ...) VALUES (val, "val2", ...)
jiný list val, val2, ...

Také si můžeme dovolit luxus žádný modifikátor před polem neuvést. V tom případě dibi použije tuto dedukci: jde-li o příkaz INSERT či REPLACE, zvol %v, jinak %a (platí pro asociativní pole).

Takže příklad:

$arr = array(
	'a' => 'hello',
	'b'  => true,
);
dibi::query('INSERT INTO [table]', $arr);
// INSERT INTO `table` (`a`, `b`) VALUES ('hello', 1)

dibi::query('UPDATE `table` SET ', $arr);
// UPDATE `table` SET `a`='hello', `b`=1

Speciální typy – objekty

Parametrem může být také objekt. Musí implementovat rozhraní IDibiVariable s metodou toSql(). Té se předá cílový ovladač a případný modifikátor a ona vrátí SQL řetězec. Jako příklad jsou v dibi takto řešeny objekty, které nesou datum a čas.

Standardní implementací IDibiVariable je třída DibiVariable. Konstruktoru předáme hodnotu a modifikátor:

dibi::query('UPDATE `table` SET ', array(
	'time' => new DibiVariable(time(), 'd'),
	'number' => new DibiVariable('RAND()', 'sql'),// %sql means SQL ;)
));
// UPDATE `table` SET ('2008-01-01', RAND())

Můžete použít také šikovější továrny na tyto objekty: dibi::date() a dibi::datetime(). Jako parametr akceptují kromě číselné hodnoty timestamp i řetězce.

Postupné skládání dotazu

Dibi disponuje také podporou pro postupné skládání SQL dotazu:

$query[] = 'SELECT * FROM [table]';
if ($where){
	array_push($query, 'WHERE [id]=%d', $where);
}

// a nyní předáme pole
$result = dibi::query($query);

Nebo lze použít expanzi pole přes speciální modifikátor %ex.

Podmíněné SQL příkazy

Podmíněné SQL příkazy jsou velmi silným nástrojem. Ovládají se pomocí tří klíčových slov %if, %else a %end. První z nich %if se musí, obdobně jako modifikátor, nacházet zcela na konci řetězce představujícího SQL:

$user = ???

dibi::query('
SELECT *
FROM [table]
%if', isset($user), 'WHERE [user]=%s', $user
);

Závěrečné %end je možno vynechat (nebo bude lepší na něm trvat?).

Podmínku lze rozšířit o část %else:

dibi::query('
SELECT *
FROM %if', $cond, '[one_table] %else [second_table]'
);

Podmínky můžete zanořovat do libovolné hloubky!

Prefixy & substituce

Názvy tabulek a sloupců mohou obsahovat proměnné části. Ty si nejprve nadefinujeme:

// create new substitution :blog:  ==>  wp_
dibi::addSubst('blog', 'wp_');

a poté použijeme v SQL. Všimněte si, že v SQL jsou uvozeny dvojtečkama:

dibi::test("UPDATE [:blog:items] SET [text]='Hello World'");
// UPDATE `wp_items` SET `text`='Hello World'

Testování query()

Abyste si mohli trošku s dibi hrát, je tu připravena funkce dibi::test(), které předáte parametry stejně jako dibi::query(), ovšem místo provedení SQL příkazu se tento barevně vypíše na obrazovku.

Možná by vás zajímalo, co celé to parsování a skládání dotazu stojí. Napsal jsem tyto funkce co nejoptimálněji a situace je taková, že zaberou jen zlomek času, který si ukousne samotné vykonání SQL příkazu. Můžete si ověřit.

Získávání výsledků

Nejjednodušší cesta vede přes klasickou iteraci

$result = dibi::query('SELECT * FROM table');

foreach ($result as $n => $row) {
	print_r($row);
}

unset($result);

Všimněte si, že zdroje se uvolní automaticky při zrušení objektu.

Je možné také nastavit offset a eventuálně i limit

$result = dibi::query('SELECT * FROM table');

$offset = 10;
$limit = 3;

foreach ($result->getIterator($offset, $limit)
		  as $n => $row) {
	print_r($row);
}

Můžeme získat jen první políčko výsledku

$value = $result->fetchSingle();

Nebo celou tabulku do indexovaného pole:

$all = $result->fetchAll();

A pak tu máme k dispozici jednu mocnou funkci:

$assoc = $result->fetchAssoc('id');

Získá celou tabulku do asociativního a klíčem je políčko ‚id‘. Největší síla funkce se projeví tehdy, pokud provedete asociaci podle více políček. Takto lze nesmírně elegantně získávat data z dotazů, ve kterých spojujeme více tabulek. Příklad si nechám na příště.

Užitečná je také funkce pro získávání dat v podobě asociativního pole klíč ⇒ hodnota

$pairs = $result->fetchPairs('customerID', 'name');

Počet řádků zjistíme voláním:

$rows = count($result);

// přesun kurzoru:
$result->seek($row);

Datové typy

Stále to není všechno, jedeme dále. Při získávání záznamů můžeme specifikovat datový typ jednotlivých sloupců a dibi je bude automaticky převádět.

$result->setType('id', Dibi::FIELD_INTEGER);
$record = $res->fetch();

if (is_int($record['id']))
	echo 'yes, it is integer';

A ještě maličkost. Dibi vrací záznamy pouze jako asociativní pole ‚název sloupce‘ ⇒ hodnota. Nelze přepnout na jinou metodu, protože jiné metody jsou špatné. Máte-li jiný názor, tak blahopřeji, ale nic se tím nezmění.

Výjimky, logování chyb a profiler

Jakákoliv chyba vzniklá během operace s databázovým serverem vyhodí výjimku DibiException nebo potomka DibiDriverException. Pokud dojde k chybě během vykonávání SQL příkazu, je i tento předán jako výjimce.

Užitečnou vlastností je logování provozu:

dibi::startLogger('log.txt', true);

Druhý parametr určuje, zda se budou zaznamenávat pouze chyby (hodnota false) nebo vše (true). Což se hodí při ladění. Tehdy se uplatní i velmi jednoduchý profiler:

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

Dibi disponuje rozhraním pro připojení vlastního profileru nebo logovací knihovny. API uveřejním později.

Co dál?

Zatím jde o vývojovou verzi dibi. Sice by neměla obsahovat žádné chyby (běží na ní už několik ostrých webů), ale stále se mohu měnit některé vlastnosti.

Nicméně testujte, zkoumejte, experimentujte.

Komentáře

  1. rADo #1

    avatar

    Vypadá to zajímavě. Ale máme zde PDO, a to je prostě zkompilované v základní instalaci, všudypřítomné, a snad i rychlejší: https://www.php.net/pdo

    před 18 lety | reagoval [4] David Grudl
  2. jow #2

    avatar

    Na zacatku, kdy ses poprve zminil o dibi jsem se trochu bal, aby to nebylo dalsi monstrum, kterych uz na db existuje hodne a ktere by se od ostatnich odlisovalo jenom hezci syntaxi samotnych sql prikazu. Kdyz se na to tedka divam, tak se mi to zacina libit a myslim si ze se dibi muze stat peknou alternativou nekterych monster a ze by mohlo dokonale vyhovovat nejakym mensim projektum. Jeste nakonec otazka :). Budes na eventuelnim vyvoji dibi pracovat sam nebo uz si nekde rozchodil subversion a traca a vrhnes se na cestu open source projektu se sirsi vyvojarskou zakladnou :D (a to nejen u dibi ale i u tveho zatim pro me zahadneho frameworku)? …

    před 18 lety | reagoval [4] David Grudl
  3. jow #3

    avatar

    S timhle se da celkem polemizovat. Podle me je dobry, ze se obejvila dalsi (jak znam texy i ciste napsana) alternativa, ktera se od zacatku tvari celkem ohebne a o rychlost bych se fakt nebal ;)…

    před 18 lety
  4. David Grudl #4

    avatar

    #1 rADo, PDO je velmi těžkopádné. Neřeší nic z toho, co dibi. Navíc je stále málo dostupné (obvykle jen s sqlite driverem). Nicméně počítám s tím, že udělám do dibi driver pro PDO 😉 Tedy zkombinuji výhody obou.

    p.s. od tlustého chlapce mi v poslední době nejvíc frčí hodinové sety

    #2 jowe, dibi je v podstatě hotový produkt, zbývá dodělat pár věcí, vytvořit stránky, vybrat licenci. Nechci aby to bobtnalo, dibi = db + mini. Prioritou je Nette a některé další projekty.

    před 18 lety | reagoval [19] Adam Hošek
  5. Aleš Dostál #5

    Chtěl jsem se zeptat jak je na tom podpora stored procedure?
    V mysqli je potreba pouzit zapis multi_query, jinak se po volaní stored procedure uzavře spojení s DB.

    před 18 lety
  6. Borek #6

    Dobrá práce, líbí se mi hlavně detekce typů. Ještě lepší by bylo, kdyby se nemuselo explicitně volat setType().

    před 18 lety
  7. who #7

    chtel jsem se zeptat jak je to s sablonama, abyse do html napsalo neco jako <li>{jmeno}</li>

    PS: od tlusteho chlapce mam giga setu, ale tak nak apex twin mi pride the best of

    před 18 lety
  8. Jakub Podhorský #8

    avatar

    nepoužíváš vracení vyjímek pomocí return aby to bylo zpětně kompatibilní s PHP4? kde pro PHP4 si uděláš navíc třídu Exception?

    imho to už není používání vyjímek tak jak se mají :)

    před 18 lety | reagoval [9] raver
  9. raver #9

    #8 Jakube Podhorský, trúfam si povedať že to nebude ten správny dôvod

    if (version_compare(PHP_VERSION , '5.0.3', '<'))
        die('dibi needs PHP 5.0.3 or newer');
    před 18 lety | reagoval [12] Jakub Podhorský
  10. medden #10

    avatar

    Napadla ma jedna vec, ktorá by sa do dibi mohla dosť hodiť – automatické pridávanie prefixov do názvov tabuliek. Aj keď neviem, či by to nebolo príliš náročné (časovo) zisťovať, či toto sa má prefixovať a toto nie. Čo vy na to? Trebárs by sa meno tabuľky, ktorá sa má prefixovať, uzavrelo do {}.

    před 18 lety
  11. lego #11

    Nesuhlasim s tym ze prepared statements nemaju vyznam.
    hlavne v pripade ORACLE kde parsovanie SQL, overovanie pristupovych prav zabera pri kratkych query cca 70–80% casu.
    okrem toho ORACLE si kazdy uz raz vykonany query ulozi do cache pre buduce pouzitie… bez bind premenych su to 1000 querov roznych iba napr. v ID.
    Momentalna MySQL implementacia je taka ze prepared stmt to cele zpomaluju ale je mozne ze sa aj oni raz dopracuju k tomu aby sa dali nazyvat databaza:-)

    před 18 lety
  12. Jakub Podhorský #12

    avatar

    #9 ravere, aha no já jsem si zdrojáky neprohlížel takže nevím :) ale jenom jsem tak tipoval podle tohodle článku

    před 18 lety
  13. tomashv #13

    Vypada to vazne hezky, ale prepared statements by urcite nemely chybet…

    před 18 lety
  14. Tomik #14

    avatar

    Je to přesně to, co jsem od dibi očekával → velké ulehčení a hlavně zpřehlednění zdrojáků.

    Škoda jen, že teď dokončuji jeden projekt, kde by se možná i dibi docela hodilo.. 🙂

    No možná se dokopu a nějak jej tam ještě před dokončením vecpu… 😉

    Jinak určitě Ti, dgx, patří moje velké díky. Napsal jsem si sice svého času vlastní db layer, který fungoval a funguje spolehlivě, ale tohle je něco úplně jiného. 😁

    Díky…

    před 18 lety
  15. johno #15

    avatar

    Je to pekné, ale ostávam zatiaľ pri prepared statements a Finder/Gateway triedach podľa Fowlera.

    A prepared statements by som takto nezavrhával. Podľa mňa sa to cachuje skôr na strane DB servera, takže tam to môže hrať väčšiu rolu ako pri znovupoužití toho istého statementu v jednom behu skriptu. To sa mne zatiaľ podarilo tiež len raz.

    před 18 lety
  16. pb #16

    avatar

    public function applyLimit(&$sql, $offset, $limit)

    … tohle asi bude potřeba vymyslet jinak, protože to určitě není takhle snadno implementovatelné. Teda obecně. Protože to je u každé db jinak:

    • MySQL – LIMIT od X do Y
    • MS SQL – TOP X aplikovaný na dvou vnořených selectech s opačným řazením
    • Oracle 9 – opět dva vnořené selecty, ve vnitřním navíc seznam položek rozšířen o číslo řádku, ve vnějším IDROW BETWEEN x AND y
    • Oracle 8 – tři vnořené selecty

    atd.

    před 18 lety
  17. Pavel Zbytovský #17

    avatar

    Ahoj,
    potřeboval bych prosím pomoc. Tohle mi z nějakého důvodu nefunguje, nic se nevypíše:

    $result = dibi::query('SELECT * FROM table');
    
    foreach ($result as $row => $fields) {
        print_r($fields);
    }

    Nevím jak ty jsou řádky výsledku uložené, ale pokud jsou stejně jako v manuálu, tak by to fungovat mělo.

    Ještě doplním, že používám PHP 5.0.4 a že místo $result mi funguje $result->fetchAll(), které ovšem není tak elegantní.

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

    avatar

    #17 Pavle Zbytovský, je to opraveno

    před 18 lety
  19. Adam Hošek #19

    avatar

    #4 Davide Grudle, Prioritou je Nette a některé další projekty.

    Takže na Nette se stále usilovně pracuje? Už se mi zdálo, že to nějak vyšumělo… pár úvodních článků a pak nic? To přece nedává smysl 🙂.

    Mimochodem mimo téma: jaký smysl má ten rozmmázlý rozsypaný čaj tady dole? 😉

    před 18 lety
  20. David Grudl #20

    avatar

    Nová verze 0.6b

    Změny se týkají přehodnocení zkratek modifikátorů, především ve vztahu k polím. Doporučuji se podívat na příslušnou pasáž v článku, aktualizoval jsem ho.

    Mění se tedy modifikátory data (nyní %d), času (nyní %t), nově přidán modifikátor %p. Odstraněny třídy pro datum a čas – očekává se buď integer (unix timestamp) nebo řetězec (pro strtotime).

    Pro pole nyní existují jen dva speciální modifikátory %a a %v. Lze však použít celou škálu výše zmíněných modifikátorů. Dále lze přiřadit modifikátor i konkrétnímu prvku v poli. Popsáno v článku.

    před 18 lety
  21. David Grudl #21

    avatar

    Jo, a ještě jsem přidal, namísto queryStart() a queryAdd(), podmíněné SQL. Zdá se, že by to mohl být velmi silný nástroj:

    dibi::query('
    SELECT *
    FROM %if', $cond, '[one_table] %else [second_table]'
    );
    před 18 lety | reagoval [22] medden
  22. medden #22

    avatar

    #21 Davide Grudle, A nemal by si to jednoduchšie takto?

    dibi::query('SELECT * FROM '.(($cond) ? '[one_table]' : '[second_table]'));

    (alebo cez to query add)
    A asi by to bolo aj trochu rýchlejšie a imho prehľadnejšie.
    Myslím si že postupným pridávaním takýchto vecí sa z dibi stane zbytočne veľký moloch, čo by bola škoda…

    před 18 lety | reagoval [23] David Grudl
  23. David Grudl #23

    avatar

    #22 meddene, máš samozřejmě pravdu.

    Dibi jsem pustil ven jako vedlejší produkt jiné činnosti, která mě teď žrala veškerý čas a proto jsem nestíhal vytvořit smysluplné příklady a demonstrovat tak skutečné možnosti. Připravím nový článek, ze kterého bude všechno lépe patrné.

    před 18 lety
  24. Pavel Zbytovský #24

    avatar

    Také bych prosil další článek, pořád na to čekám 🙂

    Můžeš alespoň naznačit, kdy ten článek vyjde? nebo vy nesmíte ani naznačovat? 😁 (Posel z liptákova)

    před 18 lety
  25. MiSHAK #25

    avatar

    DiBi rulezz https://mishak.net/ DiBi ±Texy! + Froggy (můj blog system 0.2b). Asi první nasazení DiBi 0.6b v akci :) nebo mě někdo předběhl? Jen jsem musel přepisovat na %if else :(.

    A komentáře lze psát jak je libo (html/text/Texy!(výchozí).

    BTW: komentáře nejdou → zítra uploadnu novou verzi :)

    Díky ti!

    před 18 lety
  26. SneakerXZ #26

    avatar

    No super :) uplně stejná věc už existuje a je přistupná v systémech Invision (IPB, ICB, IG, IPD). Proč ji dělat znovu?

    před 18 lety | reagoval [29] David Grudl
  27. Pong #27

    Skoda, ze dibi nepocita s vice parametrama u WHERE :-/

    před 18 lety | reagoval [29] David Grudl
  28. medden #28

    avatar

    Neskúšal som dibi síce, ale % sa v SQL zapíše ako %%? Lebo niekedy znak % je dosti potrebný… aj v SQL

    před 18 lety | reagoval [29] David Grudl
  29. David Grudl #29

    avatar

    #26 SneakerXZi, hledal jsem ta klíčová slova a nic smysluplného nenašel. Můžete být konkrétnější?

    #27 Pongu, Proč by ne? Žádné omezení neexistuje.

    #28 meddene, Znak % má speciální význam jen tehdy, je-li následován platným modifikátorem a poté hned ukončen řetězec. Takže % může být v pohodě součástí SQL

    před 18 lety | reagoval [30] Pong
  30. Pong #30

    #29 Davide Grudle, Vubec se mi nedarilo udelat dotaz ve stylu

    SELECT * FROM user WHERE username = 'uzivatel' AND password = 'heslo'

    Vkladal jsem tenhle prikaz SQL pro dibi

    SELECT * FROM [user] WHERE [username] = %s AND [password] = %s

    Ukazkovy PHP kod

    $login['username'] = $_GET['username'];
    $login['password'] = sha1 ($_GET['password']);
    
    dibi::query ('SELECT * FROM [user] WHERE [username] = %s AND [password] = %s', $login);

    A tenhle vysledny SQL prikaz jsem ziskal

    SELECT * FROM `user` WHERE `username` = %s AND `password` = 'username', 'password'

    Bralo pouze posledni %s. Mel jsem dibi 0.6b. Snad nebyla chyba u me mezi stolem a zidli.

    Jeste k tomu formatovani poli bych pridal tvar pro generovani retezku pro WHERE. U WHERE musi mit jednotlive sloupce mezi sebou rozdelene AND. Je tomu tak teda u MySQL. U jinych SQL nejsem si jist. Mohlo to byt treba %w. Nylo by to podobne %a, jen s tim rozdilem, ze by bylo pouzito AND misto „,“ (carky).

    před 18 lety | reagoval [31] David Grudl
  31. David Grudl #31

    avatar

    #30 Pongu, jo, to je chyba mezi židlí a klávesnicí ;)

    dibi::query ('
      SELECT * FROM [user]
      WHERE [username] = %s', $user, ' AND [password] = %s', $pass);

    Parametry se vkládají ihned na místo určení, proto není nutné si dopředu vytvářet pole.

    před 18 lety
  32. Pong #32

    [smazáno] Dekuji, bylo tim. Spatne precteno cast clanku. Omlouvam se za pozdni podekovani. Vim, neni to zdvorile ;o)

    před 18 lety
  33. Teki #33

    avatar

    Nejsem si jistý, do jaké míry je to záměr, ale jedna věc může být zavádějící. Když mám složitější podmínku WHERE, nesmím zapomenout na to, že musím vkládat parametr ihned za místo určení.
    Pokud jsem zvyklý závorkovat, může to být matoucí. Malý příklad:
    špatně:

    $par1 = 'a';
    $par2 = 'b';
    
    $res = dibi::test('SELECT cosi FROM [cosi_tbl] WHERE (([par1] = %s)', $par1,' OR ([par2] = %s))', $par2,' AND (c=1)');

    správně:

     $par1 = 'a';
    $par2 = 'b';
    
    $res = dibi::test('SELECT cosi FROM [cosi_tbl] WHERE (([par1] = %s', $par1,') OR ([par2] = %s', $par2,')) AND (c=1)');

    Možná by stálo za to zdůraznit, že to funguje právě takto. Někdo si tím může ušetřit trochu koumání (jako já).

    Kdy se můžeme těšit na další článek o dibi?

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

    avatar

    Další testovací verze je venku.

    • obsahuje driver pro PostgreSql (netestován)
    • chyba při připojování k DB vyhodí výjimku (throw new DibiException)
    • podpora pro prefixy (viz příklad table-prefix.php)
    • parametry připojení lze zadat formou řetězce (viz příklad connect.php)
    • lze postupně skládat dotaz (viz aktualizovaný text v tomto článku)

    Logování, zpracování chyb a výjimek je ve fázi vývoje.


    #33 Teki, ano, modifikátor se musí nacházet zcela na konci řetězce. V článku jsem to tedy raději zdůraznil.

    před 18 lety
  35. David Grudl #35

    avatar

    Tak venku je verze 0.7. Stále testovací, ale jde o horkého kandidáta na finální 🙂

    • zásadní změna se týká vkládání proměnných do SQL. Pokud je (PHP) proměnná null, bude do databáze vloženo null, nezávisle na použitém modifikátoru. Dříve se null přetypovalo podle modifikátoru. Nové chování je logičtější, ale dejte pozor na případné problémy s upgradem.
    • přibyl nový modifikátor %sn (popsáno v článku), %p je nyní %sql
    • získávání dat: celou tabulku vrátí fetchAll(), asociovanou podle jednoho čí více polí fetchAssoc($field, ...) (dříve obojí řešilo fetchAll, nyní rozděleno mezi dvě funkce)
    • logování a zpracování chyb – viz aktualizovaný text článku. Výsledná podoba logu a chybových hlášek je zatím „předmětem výzkumu“ 🙂
    před 18 lety
  36. mat #36

    Skvelé dgx. Idem hneď otestovať. Trošku zabrdnem. A čo Nette? 🙂

    před 18 lety | reagoval [37] tark
  37. tark #37

    avatar

    #36 mate, To by se ukrutně hodilo. Tak co? Kdy bude? :)

    před 18 lety
  38. David Grudl #38

    avatar

    Na základě tipu Tomáše Bartoně jsem ještě změnil volání SET CHARACTER SET na SET NAMES (já vím, ideální není ani jedno) a opravil drobný bug ⇒ dibi 0.7b

    před 18 lety
  39. MiSHAK #39

    avatar

    Pracuju nad tím 🙂

    Chtělo by to ještě pár věcí: Trošku rozepsat popis fetchSingle() (je nepřesný). Pro příkazy s polem by chtělo doplnit nějak podporu Time, Date a DateTime. Objekt TDateTime se má nebo nemá používat?

    před 18 lety
  40. David Grudl #40

    avatar

    Verze 0.7d:

    Podpora pro výchozí parametry připojení, které lze definovat např. v souboru PHP.INI. Tedy jde o direktivy mysql.default_user a podobně (viz dokumentace PHP).

    Pak lze dibi připojit k databázi skutečně velmi jednoduše:

    dibi::connect('driver=mysql');
    před 18 lety
  41. David Grudl #41

    avatar

    Verze 0.8:

    • podpora pro „lazy connections“, k připojení k databázi dojde až v okamžiku, kdy je potřeba. Mezi parametry připojení přidejte 'lazy' => true.
    • opravena řada chyb, refactoring kódu
    před 17 lety
  42. MiSHAK #42

    avatar

    Bude někdy i changelog.txt BTW todo.txt je stále stejný :)

    před 17 lety
  43. yed_ #43

    Pomoci si od tohoto…

    $res = dibi::query("SELECT foo FROM [table] WHERE whatever...");
    $value = $res->fetchSingle();

    …něčím takovýmto…

    $value = dibi::select("fetchSingle", "SELECT foo FROM [table] WHERE whatever...");

    …je mimo cíle dibi?

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

    avatar

    #43 yed_, zkus

    $value = dibi::query("SELECT foo FROM [table]
    WHERE whatever...")->fetchSingle();
    před 17 lety | reagoval [45] yed_
  45. yed_ #45

    #44 Davide Grudle, Ach, já nedůvtipa .)

    před 17 lety
  46. Whitek #46

    skoda, ten minuly titulek se me libil vice a radeji jsem se sem i vracel(tento je prilis suchy). Holt kazdy chce byt ‚seo‘.
    Ale diky za novou verzi!

    před 17 lety
  47. MiSHAK #47

    avatar

    Zajimavé, žě v RSS se titulek neměni… Lazy RSS nuklea asi

    před 17 lety
  48. Non_E #48

    Ahoj, narazil jsem na problém s postupným skládáním dotazu. Když to hodně zjednoduším:

    <?php
    //připojeno k db
    
    $query[] = "SELECT %sql";
    $query[] = "NOW()";
    
    dibi::test($query);
    $arr_res = dibi::query($query)->fetchAll();
    ?>

    mi vypíše toto:

    SELECT NOW()

    ( ! ) Warning: dibi: [smazáno] You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near SELECT %sql', 'NOW() at line 1 in /var/www/project/htdocs/ext-libs/dibi/libs/driver.php on line 136
    Pokud nepoužiju pole, tak se chyba neprojeví. Je dost dobře možné, že je chyba mezi židlí a klávesnicí, ale po hodině mě už nic nenapadá.

    Dík

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

    avatar

    #48 Non_E, Díky za bugfix a patch co jsi mi poslal emailem! Opravenou verzi (0.8b) hnedle uploadnu.

    před 17 lety
  50. silebis #50

    avatar

    Skvely, zrovna tohle potrebuji, akorat mi chybi podpora pro ms sql.
    Kdy planujes vypustit novou verzi s podporou microsoftiho sqlka?

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

    avatar

    #50 silebisi, myslíš driver pro funkce mssql_***? Což o to, základ bych měl, ale nemám ho jak otestovat a netuším, jakou funkcí zjistit chybový kód serveru. Zhostíš se toho?

    před 17 lety | reagoval [52] silebis
  52. silebis #52

    #51 Davide Grudle,
    Zkusim, sam tedka zacinam v mssql 🙂
    Zkus si nainstalova MS SQL 2005 Express. Pro zkouseni idealni a kupodivu zadara.

    před 17 lety
  53. silebis #53

    ehm, no spatna chyba, predeslou chybu neberte vazne, spravna je:

    Warning: dibi: in \plugins\dibi\libs\driver.php on line 137

    Fatal error: Call to a member function fetchAll() on a non-object in \test.php on line 19

    před 17 lety | reagoval [54] silebis
  54. silebis #54

    #53 silebisi, jak jsem si tedka vsiml, hlavni zprava nedorazila, tak znovu.

    Pri vicenasobnem pripojeni na jednom typu databaze vse jede v pohode, ale kdyz se pripojim na dva typy databazi, uz dibi vyhazuje chybu. Mozna je chyba u me, mozna v dibi.

    <?php
    dibi::connect(array(
            'driver'   => 'mysql',
            'host'     => $ConfigDBServer,
            'username' => $ConfigDBUser,
            'password' => $ConfigDBPass,
            'database' => $ConfigDBDatabase,
            //'charset'  => 'win1250',
        ),1);
    
        dibi::connect(array(
            'driver'   => 'mssql',
            'host'     => $ConfigDBMSServer,
            'username' => $ConfigDBMSUser,
            'password' => $ConfigDBMSPass,
            'database' => $ConfigDBMSDatabase,
        ),2);
    
    dibi::getConnection(1);
    
    $res = dibi::query('select krestni, prijmeni, idUzivatele from core_users')->fetchAll();
    
    foreach ($res as $key => $row) {
        echo $row["krestni"]." ".$row["prijmeni"]." - ".$row["idUzivatele"];
        //print_r($fields);
        echo '<hr>';
    }
    ?>
    před 17 lety | reagoval [55] David Grudl
  55. David Grudl #55

    avatar

    #54 silebisi, $res není objekt, funkce query zřejmě vrátila false.

    Jinak getConnection() funguje trošku jinak. Vrací objekt driveru, na kterém lze pak volat funkce přímo, například:

    $conn = dibi::getConnection(1);
    $res = $conn->query('SELECT ....');

    Pro přepínání aktivního připojení pro statický registr dibi volej activate():

    dibi::activate(1);
    dibi::query('INSERT .... ');
    před 17 lety
  56. silebis #56

    Mozna chybka? Jakmile pridam atribut s domenou ntext (v mssql databazi) okamzite mi dibi vyhodi chybu na radku 137 v driver.php
    Zeby nepodporovana domena?

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

    avatar

    #56 silebisi, fixnul jsem to – stáhni si znovu dibi-0.8c.zip

    před 17 lety
  58. silebis #58

    diky
    nevis plz jakou hodnotu nastavit v charsetu pro mssql abych dostal vysledky „spravne cesky“?
    Dostavam jen same hacky (Ostatnˇ)

    před 17 lety
  59. silebis #59

    Bohuzel, stale stejna chyba – driver.php on line 137

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

    avatar

    #59 silebisi, to je User warning, který vyhodí přímo dibi, protože máš nějakou chybu v SQL dotazu. Nevím jak zjistit text nebo kód té chyby. Nicméně MSSQL není tématem, držme se čistě dibi. Pokud vylepšíš driver mssql, pošli mi ho prosím mailem, díky

    před 17 lety
  61. Silebis #61

    Budu zkouset. Co jen mohu s jistotou rici, ze v dotazu chybu nemam.

    před 17 lety
  62. Acci #62

    avatar

    Bude dibi v příští verzi podporovat vícenásobný INSERT? Celkem by se mi to (a nejspíše nejen mně) hodilo.

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

    avatar

    #62 Acci, doplněno do verze 0.8d

    dibi::query('INSERT INTO [table]', $arr, $arr, $arr, $arr);
    před 17 lety | reagoval [64] Acci [72] David Grudl
  64. Acci #64

    avatar

    #63 Davide Grudle, Díky, to je fakt luxus!

    před 17 lety
  65. sefik #65

    avatar

    chtěl bych jenom upozornit, pokud zadáte do dotazu … %i ',$cislo, … a mezi %i a uvozovku dáte mezeru, tak to nebude fungovat

    před 17 lety | reagoval [66] MiSHAK
  66. MiSHAK #66

    avatar

    #65 sefiku, No to je normální chování, dibi zpracovává jen metaznaky na konci řetězce.

    před 17 lety
  67. LamiCZ #67

    Zdravím, narazil jsem na tento blog i utility od DGXho a musím říct, že smekám, seznamuji se pomalu s PHP5 OOP (velmi pomalu :)) na mém systemu (viz link) a Texy! se mi velmi zamlouvá, už jsem ho dal na novinky a knihu návštěv. Nyní testuji dibi a začíná se mi velmi libit, měl jsem použity PDO, ale je to strašně zabugovaná věc nebo já v tom neumím psat, nevím… (Asi spíše to b) je správně…).
    Každopádně GW dgx a doufám, že bude dibi používána i vyvíjena ;)

    před 17 lety
  68. David Grudl #68

    avatar

    Verze 0.8e podporuje extra dlouhá a hexadecimální čísla:

    dibi::test("SELECT %i", '-123456789123456789123456789');
    // -> SELECT -123456789123456789123456789
    
    dibi::test("SELECT %f", '-.12345678912345678912345678e10');
    // -> -.12345678912345678912345678e10
    
    dibi::test("SELECT %i", '0x11');
    // -> SELECT 17

    Za impuls děkuji Tomáši Bartoňovi.

    před 17 lety
  69. zajDee #69

    avatar

    Mozna to tu pisu zbytecne, ale pokud by se s tim nekdo setkal, tak muze mit min prace:

    Metoda fetchAll vrati pole, ve kterem jsou jednotlive indexy shodne se jmenem sloupce. Az potud OK. Jenze ve chvili, kdy mam SQL dotaz, ktery spojuje dve tabulky, ve kterych je sloupec se stejnym jmenem, vrati dibi jen ten posledni zmineny v SQL dotazu (tedy, tak se to chovalo mne).

    Reseni bylo proste, pouzit AS jmenoSloupceVeVysledku, tj. napr. SELECT [tabulka1.id] AS [sloupecVTabulce1],[tabulka2.id] AS [sloupecVTabulce2] FROM (…)

    Jo a psát česky, to bych rád, ale zapomínám na to 🙂

    před 17 lety | reagoval [72] David Grudl
  70. Milan Svoboda #70

    Uvažuješ i o podpoře ORACLE?

    před 17 lety | reagoval [72] David Grudl
  71. Blekii #71

    avatar

    Hoj vespolek, zajímavá pomůcka a jen tak bez přemýšlení se zeptám…a co podpora pro vícenásobný UPDATE a INSERT..(v MYSQL).. existuje v DIBI?

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

    avatar

    #69 zajDee, to není záležitost dibi, ale databázového ovladače. Jinak se chová MySQL a jinak třeba SQLite.

    #70 Milane Svobodo, neuvažuju, ORACLE nemám

    #71 Blekii, ano, viz #63 David Grudl

    před 17 lety
  73. Blekii #73

    avatar

    Je to fakt bomba. Vícenásobný insert mi sice nejede, ale to jen tázka správného pochopení. Ale chceš vybírat-udělá to DIBI, chceš vkládat udělá to dibi, chceš na záchod, já sedím ale DIBI jde…

    před 17 lety
  74. Josef Průša #74

    avatar

    Ahoj, jak používat v DIBI + PostgreSQL schémata?

    dibi::query(‚SELECT * FROM [schema.table] WHERE [id] = %i‘, $id);

    Nebo to jde nějak elegantněji?

    před 17 lety
  75. sairon #75

    avatar

    Tak jsem se stal taky závislým na dibi. Po pochopení základních principů je práce s ním intuitivní a odpadá plno starostí, prostě perfektní. Kompaktní verze navíc není zbytečně velká a poskytuje stejný (a v mnoha případech větší) komfort, jako ostatní DB layery; licence také není nijak omezující. Smekám :)

    před 17 lety
  76. miniak #76

    dibi je uplne krasny kus kodu, velmi sa mi paci. pochvala DGXovi.
    v praci som musel pouzivat ADOdb, ktore mi pripada uchylne. oproti tomu je dibi uplna krasa. jednoduche, male, rychle

    akurat pri praci so substituciami som narazil na drobnosti, ktory by ulahcili zivot:

    1. funkcia dibi::addSubst() by mohla brat nielen nazov a hodnotu, ale aj pole, aby som ju nemusel opakovane volat pre viacero poloziek.
    2. podobne u dibi::removeSubst(), a tam by nebolo zle, keby to zrusilo vsetky substitucie pri zavolani bez parametra
    před 17 lety | reagoval [77] David Grudl
  77. David Grudl #77

    avatar

    díky za pochvaly!

    #76 miniaku, doplním volání removeSubst(true), ktere zruší všechny substitutky.

    před 17 lety
  78. Honza M. #78

    avatar

    Jak nejjednodušeji zjistím počet vrácených výsledků, smazaných řádků apod.? mysql_num_rows() a mysql_affected_rows().

    Nemohlo by existovat něco jako $result->length() ?

    Třeba by se mi to hodilo, když nevim jestli vypsat seznam výsledků nebo napsat, že nic neexistuje.

    před 17 lety | reagoval [80] David Grudl
  79. MiSHAK #79

    avatar

    count($result) podkud se nepletu. :)

    před 17 lety
  80. David Grudl #80

    avatar

    #78 Honzo M.,

    • počet řádků: $result->rowCount() nebo count($result)
    • ovlivněné řádky: dibi::affectedRows() nebo $driver->affectedRows()
    • insert id: dibi::insertId() nebo $driver->insertId()

    v případě PostgreSql lze volat dibi::insertId(‚nazev sekvence‘)

    před 17 lety
  81. David Grudl #81

    avatar

    Verze 0.9a má zcela přepracovaný systém logování a profilování – základy najdete v příkladech logger.php a profiler.php. Bude možné nastavit vlastní profiler nebo logger, ale API se ještě změní, takže ho zatím nebudu zveřejňovat.

    Zrušil jsem vlastnost dibi::$throwExceptions – dibi nyní vyhazuje výjimky vždy. Jen tak je možné bezpečně volat třeba:

    dibi::query(...)->fetchAll();

    Když by totiž query vypsalo chybu a vrátilo false, tak volání fetchAll() by způsobilo Fatal error: Call to a member function fetchAll() on a non-object. Naopak výjimky lze zachytit a zpracovat.

    Plán: dibi už je pomalu hotové, v plánu mám ještě dořešit logování & profiler.

    před 17 lety | reagoval [83] MiSHAK
  82. sairon #82

    avatar

    Původně vracelo volání dibi::query(„INSERT INTO …“) při úspěchu true, teď vrací null. Je to tak záměrně?

    před 17 lety | reagoval [84] David Grudl
  83. MiSHAK #83

    avatar

    #81 Davide Grudle, Má tedy cenu přecházet na 0.9 nebo si „chvilku“ počkat?

    před 17 lety
  84. David Grudl #84

    avatar

    #82 sairone, hmmm, to by mohlo být problematické kvůli zpětné kompatibilitě. Opravil jsem, nyní vrací v případě úspěchu opět true.

    před 17 lety
  85. Honza M. #85

    avatar

    Existuje nějaký elegantnější způsob, jak získat rovnou první řádek do asociativního pole než

    $res = dibi::query("......")->fetchAll();
    $res = $res[ 0];

    ?

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

    avatar

    #85 Honzo M.,

    $row = dibi::query("......")->fetch();
    před 17 lety
  87. Pavel Černý #87

    avatar

    Ahoj, rád bych použil DIBI u mnou vyvíjené aplikace (placené)…
    Jak se domluvíme?

    Blekii

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

    avatar

    #87 Pavle Černý, dodrž licenci

    před 17 lety | reagoval [89] zirafka
  89. zirafka #89

    avatar

    #88 Davide Grudle, dodrz licenci, posilis mir!

    před 17 lety
  90. Pavel Černý #90

    avatar

    Moc té licenci nerozumím…(myslím tím podstatu).
    Řekni mi to jako malému dítěti…

    před 17 lety
  91. hvge #91

    Pavel Černý: pebéébe pebéébe ;]

    před 17 lety
  92. Pavel Černý #92

    avatar

    LOL

    před 17 lety
  93. Josef Průša #93

    avatar

    DGX se docela divil když jsem mu chtěl poslat příspěvek na dibi :) myslím, že stojí mnohem víc za podporu než Texy ;)

    před 17 lety
  94. Hrach #94

    avatar

    Je možné nějak aby Dibi volalo po každém dotazu mou funkci – zřejmě nějak pomocí handleru a eventu, ale zatím sem vůbec nepřišel jak.. :(
    chtělo by to dokumentaci ;)

    před 17 lety | reagoval [95] Hrach
  95. Hrach #95

    avatar

    #94 Hrachu, tak, už jsem na to přišel, ale nějak nemůžu dosáhnout funkčnosti v tomto případě:

    dibi::addHandler(array($this, 'afterQuery'));

    daří se mi volat pouze funkci, ale ne metodu třídy

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

    avatar

    #95 Hrachu, toto je API, které se bude ještě určitě měnit. Proto není zdokumentované.

    před 17 lety
  97. veena #97

    Prošel sem si zdrojáky dibi a měl bych tu pár návrhů na zlepšení:

    1. chtělo by to zdokumentovat, jaké všechny parametry mohou být v poli cpaném do dibi::connect()
    2. kupodivu jsem tam nenašel metodu close() na uzavření spojení. Opravdu tam není? To by asi měla. (nejlépe s možností se u fetch metod rozhodnout, zda ho zavřít)
    3. nemělo by být v mysql/i driverech kormě SET NAMES voláno i SET CHARACTER SET pro správné řazení?

    viz https://alberton.info/…plained.html

    1. kontrolu spojení if (!$this->connection) { jsem používal také. Než se mi stalo, že v dlouho běžícím skriptu (z příkazové řádky) jsem si neuzavřel spojení k db. Ono vypršelo po timetoutu, který má nastavena mysql. No jo, ale v phpku resource v proměnné $this->connection stále zůstane, i když je už vzdálený zdroj odpojen.

    Já sem nakonec vyřešil metodu connect takto:

    function connect()
    {
        if (false === $this->resource = mysql_connect($this->host, $this->user, $this->password, $this->newConn)) {
            return false;
        }
        if ($this->newConn and !empty($this->chset)) {
            mysql_query('SET CHARACTER SET '.$this->chset, $this->resource);
            mysql_query('SET NAMES '.$this->chset, $this->resource);
        }
        $this->newConn = false;
        return true;
    }
    1. přidal bych podporu mysql_unbuffered_query(). Šetří to paměť hlavně u velkých dotazů a umožňuje to procházet jejich výsledky hned bez čekání na všechna data.

    Více inspirace viz na
    https://web.archive.org/…mysql41.phps

    před 17 lety | reagoval [98] David Grudl [99] David Grudl
  98. David Grudl #98

    avatar

    #97 veeno, díky za připomínky.

    ad 1) ano, dokumentace chybí, zdaleka nejen u této metody.

    ad 2) pro klid duší to tam dám 🙂

    ad 3) SET NAMES a SET CHARACTER SET fungují částečně protichůdně, takže volat by se mělo jedno nebo druhé (a když obojí, tak ve správném pořadí). Praktičtější se mi zdá SET NAMES, které se nespoléhá na nastavení databáze.

    ad 4) jestli to dobře chápu, tak ty před každým query oveříš a případně obnovíš spojení přes mysql_connect(…, false)? To není špatný nápad.

    ad 5) to je taky dobrá připomínka, jen zatím neslučitelná s filosofií „nikdy nepřídávej funkčnost, která by se mohla někdy hodit, ale jen to, co skutečně potřebuješ“

    před 17 lety | reagoval [100] veena
  99. David Grudl #99

    avatar

    Verze 0.9 rev. 77

    • drivery mají metodu disconnect()
    • navazování spojení (viz #97 veena bod 4) jsem nakonec neimplementoval. Mohlo by to způsobit průšvih, když bych v průběhu spojení změnil databázi, ono by se to tiše rekonektlo na původní a pak bych nic netuše pracoval s jinou.
    • driver SQlite při chybě vrací její podrobnější popis

    Minimální požadovaná verze PHP je 5.1.0. Dále prosím netestujte návratovou hodnotu způsobem if (dibi::query(...)), v případě chyby dibi vrací výjimku.

    před 17 lety | reagoval [100] veena
  100. veena #100

    #98 Davide Grudle, #99 David Grudl
    ad 1) 2) ok
    ad 3) popravdě se v tom zas tolik nevyznám, ale dal sem to tam poté, co jsem v jedné diskuzi četl, že to někomu pomohlo s řazením.

    ad 4) jo obnovuju pokaždé. Nevim, jestli to trvá déle. Nekoukal sem do zdrojáků mysql_connect() jak funguje, ale snad pokaždé neposílá login do databáze ale zjistí si jinak, jestli je resource funkční.

    Ještě by to šlo udělat přes volání mysql_ping()

    S tou změnou databáze to je samozřejmě u singletonu problém!
    Musel bys vždy volat mysql_select() po connectu. To by asi zase chtělo nějaký benchmark.

    Obecně bych u singletonu to přepínání databází nepoužíval. Vázat ten singleton k objektu databáze a ne ke spojení.
    Když někdo potřebuje více databází (já třeba občas), tak si prostě udělá víc instancí databázovýho objektu.

    Ono je to stejně ošemetné když někde v půlce kódu změníš databázi v tom singletonu a pak někde daleko máš práci s tou databází, tak je to nepřehledné. To aby ses furt koukal, který kód se vykonal předtím a kolikrát si tu databázi switchnul. Čo?

    ad 5) mysql_unbuffered_query() používám defaultně u všech fetch metod který získávaj data do polí, protože sem ji objevil hned na začátku co jsem si db knihovnu vytvářel. Zase, benchmark sem si nedělal. Nicméně mi to nedělalo problém zapojit to do návrhu, takže sem to tam dal jako parametr u těchle fetch metod. Možná u tebe by to znamenalo překopat toho víc a měnit API, takže je to na zvážení. Teda pokud bys to neudělal jako jeden parametr do $option pro mysql a pak by to platilo pro volání všech fetch metod tý instance.

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

    avatar

    #100 veeno, ten příklad se změnou databáze byl jen ilustrační. Tichý rekonekt může způsobit víc problémů, poškodí otevřenou transakci, vymaže nastavené proměnné (například LAST_INSERT_ID) atd. Jako obecné řešení to chce najít něco jiného. Co třeba MYSQL_CLIENT_INTERACTIVE?

    ad unbuffered: o změnu API ani tak nejde, jako spíš o odlišnou logiku chování. Tedy že dokud nedočteš jedno query, nemůžeš volat další. Ale jako volba pro celé connection to není špatné řešení. Přidám.

    před 17 lety
  102. David Grudl #102

    avatar

    Verze 0.9 rev. 83

    • implementovány unbuffered query pro MySQL, MySQLi a SQLite drivery. Zapnou se volbou 'unbuffered' => true v parametrech připojení.
    • dopsal jsem dokumentaci jednotlivých parametrů (viz třeba DibiMySqlDriver)
    • v unbuffered režimu vyhazuje DibiResult::seek() nebo DibiDriver::rowCount() výjimku
    • aby bylo chování konzistentní, vyhazuje DibiResult::seek() při chybě výjimku vždy, tedy i v buffered režimu
    • DibiDriver::errorInfo je deprecated (chyby vyhazují výjimky)
    • Důkladný refactoring celého kódu. Nemělo by to mít vliv na funkci (rostlináře).
    před 17 lety
  103. David Grudl #103

    avatar

    Verze 0.9 rev. 94

    • metody dibi::date() a dibi::datetime generují datum a čas ve formátu pro databázi (zabaleno jako DibiVariable objekt)
    • insertId() a affectedRows() v případě chyby generují výjimku
    • špatné pořadí volání begin(), commit(), rollback() generuje výjimku
    • rozšířeny parametry pro připojování k PostgreSQL (viz DibiPostgreDriver
    • opraveno několik chyb
    před 17 lety
  104. David Grudl #104

    avatar

    Verze 0.9 rev. 98

    novinka: možnost nechat kvalifikovat název každého sloupce názvem tabulky:

    $res = dibi::query('
        SELECT *
        FROM [products] AS aa
        INNER JOIN [products] AS bb ON aa.product_id <> bb.product_id
    ');
    
    $res->dump();
    
    // srovnej s:
    $res->withTables = true;
    $res->dump();
    • odstranil jsem některé experimentální vlastnosti jako autodetekce typů přes $result->setType(true);, metody getFields nebo getMetaData
    • metatypy se nyní oficiálně zjišťuji přes $result->getColumnsMeta();
    před 17 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í.