Přejít k navigační liště

Zdroják » Databáze » Databáze v PHP elegantně s NotORM

Databáze v PHP elegantně s NotORM

Články Databáze, PHP, Různé

Většina webových aplikací potřebuje pracovat s propojenými daty uloženými v databázi. Pro pohodlnější práci s databázemi existuje velké množství mezivrstev a knihoven, které mají práci usnadnit. Novou PHP knihovnu, nazvanou NotORM, nám v článku představí její autor, Jakub Vrána.

 Psát SQL dotazy spojující třeba šest tabulek v databázi může být zpočátku docela zábavné, pak se z toho ale stane nudná rutina. Navíc spojení tabulek nemusí být vždy nejefektivnější, protože se znovu přenáší už jednou přenesená data. Takže někdy je výhodnější položit šest jednoduchých dotazů a propojit až jejich výsledky, což už je úplná nuda.

Elegance

Pro získání dat pomocí PHP proto vznikla knihovna, která propojená data z databáze dokáže elegantně a zároveň efektivně získat. Jmenuje se NotORM a připodobnil bych ji k PHP extenzi SimpleXML, která podobným způsobem pracuje s XML dokumenty. Základní API je jednoduché:

<?php
// připojení k databáziinclude "NotORM.php";
$pdo = new PDO("mysql:dbname=software");
$software = new NotORM($pdo);
// získání prvních deseti aplikací
foreach ($software->application()->order("title")->limit(10) as $application) {
    // vypsání jejich titulku
    echo $application["title"];
   
    // získání jména autora z propojené tabulky
    echo " (" . $application->author["name"] . ")n";
   
    // vypsání všech nálepek z propojené tabulky M:N
    foreach ($application->application_tag() as $application_tag) {
        echo "- " . $application_tag->tag["name"] . "n";
    }
}
?>

Efektivita

Jak už bylo řečeno, tak knihovna je nejen elegantní, ale zároveň i efektivní. Klade tedy jen minimální počet jednoduchých dotazů a z databáze přenáší jen ta data, která jsou potřeba. Dosahuje se toho dvěma způsoby:

  1. Získání souvisejících záznamů se provádí najednou pro všechny řádky výsledku jedním dotazem.
  2. Přenášejí se jen ty sloupce, které se nakonec skutečně použijí (při povolení této vlastnosti).

Díky těmto vlastnostem se pro zpracování příkladu položí jen čtyři jednoduché dotazy:

SELECT id, title, author_id FROM application ORDER BY title LIMIT 10;
SELECT id, name FROM author WHERE (id IN ('11', '12'));
SELECT application_id, tag_id FROM application_tag WHERE (application_id IN ('1', '4', '2', '3'));
SELECT id, name FROM tag WHERE (id IN ('21', '22', '23'));

Všimněte si, že z tabulek se přenáší jen ty sloupce, které se nakonec použijí, aniž by se jejich seznam musel kdekoliv specifikovat. To je zařízeno tak, že knihovna napoprvé získá všechny sloupce, pak si ale zapamatuje, které sloupce skutečně použila a příště už si vyžádá jen ty použité. Když se skript změní a chceme vypsat nějaké sloupce navíc, tak se automaticky zase získají všechny a seznam se rozšíří. Pro zajištění tohoto chování si knihovna potřebuje ukládat data, což lze nastavit třetím parametrem konstruktoru NotORM  – jsou připravena úložiště do session proměnných, souborů, databáze a sdílené paměti, další si můžete snadno doplnit.

Kešování výsledků dotazů knihovna nepoužívá, protože takováto keš zastarává a navíc zabírá paměť, kterou lze využít k něčemu užitečnějšímu. Lepší je data získávat efektivně rovnou od databáze, pro což dává knihovna výborné podmínky.

Struktura databáze

NotORM umí pracovat se všemi databázemi, které podporuje PDO, testovaná je s MySQL, SQLite, PostgreSQL a MS SQL. Kromě toho je NotORM k dispozici i pro oblíbenou knihovnu Dibi.

Co se pojmenování sloupců týče, tak se používá nejrozšířenější konvence – primární klíč je id, cizí klíč table_id, kde table je název odkazované tabulky. Předáním druhého parametru konstruktoru NotORM si ale můžeme nastavit i vlastní konvenci. K dispozici je i třída načítající informace o primárních a cizích klíčích přímo z meta-informací databáze (tabulky InnoDB v MySQL >= 5), ta má ale určitou režii, takže je lepší dodržovat nějakou konvenci.

Validace a ukládání dat

NotORM se nestará o ukládání a tím pádem ani validaci dat. Ve webových aplikacích je totiž ukládání obvykle mnohem méně časté než načítání a není problém ho zajistit konvenčními způsoby. Pro administrační rozhraní lze použít Adminer Editor, který ukládání vyřeší zadarmo.

Validaci dat je lepší řešit na úrovni databáze pomocí unikátních a cizích klíčů, datových typů, povinných sloupců a dalších kontrol. Jinak totiž hrozí, že se nám do databáze dostanou nekonzistentní data, se kterými aplikace stejně bude muset umět pracovat. Ohlašování chyb uživateli je zase vhodné řešit co nejblíže místu jejich vzniku, výborně se o to starají třeba formuláře Nette.

Větší příklad

NotORM se báječně hodí pro malé webíky se třemi propojenými tabulkami stejně jako pro velké projekty s desítkami složitě propojených tabulek. U těchto projektů dává NotORM pocit klidu, že se bez velkého přemýšlení vyhodnocují všechny dotazy efektivně (samozřejmě pokud jsou definované indexy na vyhledávání a třídění). Ukážeme si proto použití u obchodu, který organizuje produkty do kategorií a u každého produktu eviduje jeho možné dodavatele. Kromě toho se k produktům evidují parametry, které se načítají z tabulky všech možných hodnot (ta se používá třeba i pro sestavení vyhledávacího formuláře), která se zase odkazuje do tabulky všech možných názvů parametrů. Na obrázku je pouze relevantní část schématu celé databáze a jsou vynechány nevyužité sloupce.

<?php
include "NotORM.php";
$pdo = new PDO("mysql:dbname=shop");
$shop = new NotORM($pdo, null, new NotORM_Cache_Database($pdo));
$category = $shop->category[$_GET["id"]];
if (!$category) {
    // 404
    exit;
}
echo "<h1>" . htmlspecialchars($category["name"]) . "</h1>n";
$products = $category->product()
    ->where("disabled", 0)
    ->order("price")
    ->limit(10)
;
foreach ($products as $product) {
    echo "<h3>" . htmlspecialchars($product["name"]) . "</h3>n";
    list($suppliers) = $product->product_supplier()->aggregation("COUNT(*)");
    echo "<p>Cena: $product[price] Kč, dodavatelů: $suppliers</p>n";
   
    $product_parameters = $product->product_parameter();
    if (count($product_parameters)) {
        echo "<table cellspacing='0'>n";
        foreach ($product_parameters as $product_parameter) {
            $parameter = $product_parameter->parameter;
            $parameter_name = $parameter->parameter_name;
            echo "<tr>";
            echo "<th>" . htmlspecialchars($parameter_name["name"]) . "</th>";
            echo "<td>" . htmlspecialchars("$parameter[value] $parameter_name[unit]") . "</td>";
            echo "</tr>n";
        }
        echo "</table>n";
    }
}
?>

Při spuštění se položí následující dotazy:

SELECT id, name FROM category WHERE (id = '25');
SELECT id, category_id, name, price FROM product WHERE (category_id IN ('25')) AND (disabled = '0') ORDER BY price LIMIT 10;
SELECT product_id, COUNT(*) FROM product_supplier WHERE product_id IN ('102920', '102915', '116549', '102902', '108993', '102907', '102900', '102922', '102930', '102909') GROUP BY product_id;
SELECT product_id, parameter_id FROM product_parameter WHERE (product_id IN ('102920', '102915', '116549', '102902', '108993', '102907', '102900', '102922', '102930', '102909'));
SELECT id, parameter_name_id, value FROM parameter WHERE (id IN ('1', '131', '3780', '6109', '2561', '2576', '6110', '3787', '5374', '6111', '6113', '6114', '8783'));
SELECT id, name, unit FROM parameter_name WHERE (id IN ('1', '46', '120', '187'));

Dávat tyto dotazy dohromady ručně nebo konstruovat dotazy spojující více tabulek by se mi opravdu nechtělo. Obslužná logika pro jejich projití by navíc byla prakticky stejná jako u NotORM. Navíc bych nejspíš skoro všude z pohodlnosti použil SELECT *, abych nemusel sloupec dodatečně přidaný do výpisu dopisovat ještě do dotazu.

Závěr

Knihovna NotORM se dá použít pro pohodlné a efektivní procházení propojených záznamů v databázi, ať už u malých nebo u velkých projektů. Neřeší validaci ani ukládání dat, které je obvykle lepší řešit na jiné úrovni.

Komentáře

Subscribe
Upozornit na
guest
69 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
Michal Kleiner

Ahoj,

knihovnu NotORM se zájmem sleduji a chystám se vyzkoušet. Rád bych se ale předem zeptal, jak se bude chovat v situacích, kdy by se do WHERE IN (…) podmínky měly dostat desítky tisíc ID z nadřazené tabulky.. nemá query string omezenou délku?

Díky za případnou odpověď.

MK

radik

Ja myslim ze tak ta otazka nebyla minena… Pokud ten prvni select vrati treba 1000 zaznamu (cemuz bych se mel rekneme vyhnout, ale muze se stat, ale to same plati i pro 100 redku, coz uz je bezne), tak abych mohl sestavit ten IN, tak musi knihovna projit vracene radky a posladat z nich IN. Pak tenhleten rekneme dlooooooouhy dotaz poslu do MySQL, ten to nejdriv musi parsovat a zase ty IDcka ze stringu lovit, pak to musi chudak nejak vykonat (nerad bych podcenoval jeho optimalizator, ale optimalni to jiste nebude) a pak to posle na server, kde se to musi znovu prochazet a joinovat ty vracene radky… Vubec radeji nemluvim o tom, jak je to v pripade, ze je tech joinu vic nebo se dela join pres dalsi tabulku.

Nemuzu si pomoct, ale kdyz proti tomu postavim jeden rucne psany JOIN, na ktery je server optimalizovany, prijde mi to dost nesrovnatelne…

radik

Zmeril jsem… Tvrdit o cemkoli, ze je „prakticky okamzite“ je podle me dost odvazne, ale budiz. Na realnych datech, kde se z tabulky s miliony radku vybira asi tisicovka a ta se joinuje s tabulkou s desetitisici radku mi to vyslo tak, ze jen provedeni dvou selectu (jeden s IN) trvalo 150–200% casu JOINu… coz podle me nemi na zatizenych webech zanedbatelne (nepocitam narust zateze na strane PHP, ktery bude prakticky zadny, ale presto nejaky). A joinovani je mozna nehodne slovo, ale pokud je join=spojit a „spojovani v PHP“ nahrazuje onen sql JOIN, a myslim, ze si jiste rozumime ;)

tiso

Ty niekde potrebuješ VYPÍSAŤ asi 1000 záznamov z databázy? Môžeš uviesť konkrétny príklad?

Mastodont

Všimněte si, že z tabulek se přenáší jen ty sloupce, které se nakonec použijí, aniž by se jejich seznam musel kdekoliv specifikovat. To je zařízeno tak, že knihovna napoprvé získá všechny sloupce, pak si ale zapamatuje, které sloupce skutečně použila a příště už si vyžádá jen ty použité.

To je myšleno jako další level v podporování lenosti programátorů? :)

okbob

Ale pro větší data a projekty je to stejně nebezpečné jako klasické ORM. Navíc to vede k naivnímu způsobu programování, kdy bombarduji SQL databázi jednoduchými dotazy – což vyhovuje MySQL, ale všem ostatním databázím to dobře nedělá, přičemž je to stejně hloupost, protože ostatní db si dokáží poradit i s komplikova­nějšími než triviálními dotazy.

okbob

Nebudu se přít, že o tom, že denormalizovaná data jsou větší – to je jasné. Na druhou stranu – pokud nejsou extrémně delší, tak je mnohem větší brzdou režie na straně serveru – každý dotaz se musí poslat po síti, musí se parsovat, musí se generovat plán, musí se generovat snapshot. Navíc obcházíš planner – v případě chtřejších db, lze efektivně provést JOIN aniž bys musel mít index na tabulkách – čímž zase zrychluji DML operace. Případně planner si může vybrat z několika metod implementace JOINu – v tvém případě je to vždy NESTED LOOP, což je ta nejpomalejší metoda – byť je v některých případech tou správnou.

Minimálně jsou tvá doporučení hodně diskutabilní – pokud bych měl problém s pomalou sítí – pak bych spíš měl použít uloženou proceduru. Opět není problém přímo v uložené proceduře přeformátovat výsledek do JSON, XML, HTML formátu. Co se týče mne, tak rozhodně nedoporučuji tvůj přistup aplikovat pro PostgreSQL a netriviální projekty – pokud budu řešit menší eshop – nic se neděje. Při větších objemech, při komplexnějších operacích by to nedopadlo dobře – resp. výsledek by byl zbytečně zoufale pomalý.

okbob

Myslím si, že stačí, když dojde k situaci, kdy ORM vygeneruje dotaz … IN (několik stovek id). Tam budeš zbytečně tlačit mezi serverem a klientem data, která vůbec nepotřebuješ. Ty to dokážeš obejít skrz nested metody – ok, kolik programátorů se k tomu dopracuje? Navíc kód, který to vygeneruje je minimálně pro MySQL menší než 5.5 neefektivní (v případě větších objemů).

Můj primární protiargument je ten, že tvé NotORM dokáže v rukách průměrného programátora napachát stejnou škodu jako klasické ORM – stejně si musí výsledně programátor ověřovat dotazy, které tvoje knihovna vygeneruje, tudíž úspora práce je minimální a čitelnost celku horší. Z mého pohledu má knihovna NotORM stejné nevýhody jako klasické ORM – jen je menší – spíš bych ji pojmenoval SimpleORM.

Formátování výsledku v SQL může mít svůj význam – a) tehdy kdy řeším výkon, b) kdy db požívá víc aplikací – a třeba PHP je pouze jedním z možných interface – samozřejmě – nesmí se to přehánět – je blbost generovat v SQL komplet html stránku.

okbob

Lišíme se v názoru na to, co je horší – jestli objem přenesených dat nebo počet SQL dotazů – v tom se neshodneme. Řešit přenos po lokální síti mi přijde irelevantní (v běžných dimenzí .. do 100KB výsledku) – něco jiného je čtení/zápis na disk.

Ohledně poddotazů – v materiálech k 5.5 (a k 5.4) jsou informace o tom, že poddotazy jsou znatelně rychlejší (v těchto verzích). Z toho vycházim.

logik

Ne že bych měl něco proti tomudle frameworku, na spoustu věcí se velmi hodí, ale IMHO při složitějších aplikacích někdy selhat musí.

Vycházím z jednoduché premisy: Proč by databázové plannery byly tak sofistikované a často jim trvalo několik verzí (např. Mysql), než se podaří by generovaly alespoň trochu rozumné plány pro složitější dotazy, když by takovýto v podstatě triviální algoritmus vracel optimální nebo skoro optimální řešení?

logik

No, pokud bych jako bernou minci bral projekt, na kterém pracuju teď, tak tam by si NotORM myslím zuby nevylámalo.
Ale z úplně jednoduchého důvodu – buďto používám jednoduché selecty,
kde je úplně jedno, co se použije, nebo vnořené poddotazy, kde IMHO notOrm takovou výhodu nemá (rychlost i srozumitelnost ručně napsaného dotazu je zhruba podobná zápisu v notorm) (ručně je samo nepíšu), nebo stored procedury, často obalující různé rekurzivní SQL dotazy, což by pomocí notORM nešlo asi vůbec?

Jinak v podstatě souhlasim, že u databáze, která vnořené dotazy umí správně optimalizovat (s čímž třeba mysql měla problémy – myslím ale, že větší problém byl u dotazů typu NOT IN – umí je notOrm?) tak správně napsané dotazy v notOrm by neměly bejt (moc :-)) špatné. „Problém“ spíš vidím v tom, že u složitějších projektů použiju různý kombinace poddotazů, rekurzivní poddotazy a podobný věci, a ty v notOrm zas tak hezky popř. vůbec napsat nepůjdou.

Takže to, že notOrm většinou (klidně si to změň na vždy) generuje dobrý plány imho neni tim, že by to byl „univerzálně použitelnej princip“, ale spíš že notOrm v základu velmi hezkým způsobem obaluje pouze část toho, co databáze umí. Pokud to člověku v projektu stačí, pak notOrm může bejt dobrá volba. Mě by to ale v projektech co píšu většinou nestačilo.
Pokud by se ale přepsalo tak, aby umělo i stored procedury, pak by to byl asi opravdu hezkej frontend pro aplikace, který maj většinu logiky v databázi.
Anebo naopak na aplikace, kde člověku stačí to, co notOrm pokrejvá hezky. Ale když ona je stromová struktura tak „přirozená“ a rekurzivní dotazy tak hezký :-)

fos4

Pokud se nepletu pri pouziti NOT IN ci != neumi mysql pouzit index.

fos4

Jinak by mne zajimalo jak se resi nasledujici problem:
Jsou tri tabulky: produkty (id, nazev, kategorie_id), cenoveskupiny (id, nazev) a ceny (cenoveskupiny_id, produkt_id, cena).

Jak efektivne vybrat produkty z dane cenove skupiny (a nejake dalsi podminky jako kategorie apod.) s cenou od do a seradit od nejlevnejsiho.

danaketh

Pomocí VIEW :) Nezkoušel jsem výkon na MySQL ani PostgreSQL ale třeba na MSSQL mám odzkoušeno, že je rychlejší udělat pohled, než posílat několik dotazů a pak to šroubovat k sobě.

Databáze si sestaví nějaké schéma (zase tak moc do střev tomu nevidím) a pak už jsou selecty sranda, protože databáze ví kudy postupovat (za předpokladu, že existují indexy – bez nich se taky dá databáze úplně sestřelit) a dotazy tak trvají chviličku a je to menší zátěž pro server.

fos4

Díky, NotORM mně přijde dobrý na dotazy které jsou ± stejné a a podmínky v nich se moc nemění.
Pokud by se v předchozím příkladu změnily podmínky a uživatel už by nechtěl filtrovat podle ceny ale jen podle kategorie tak už se musí změnit celý zápis, neboť programátor už musel sám určit z která tabulky se bude vybírat jako první.

fos4

jde mně o to že uživatel většinou určuje podmínky a dle čeho seřadit a mysql v tomto ohledu většinou pomůže tím že správně vybere první tabulku.

Vlastně ani moc nejde o NotORM ale jak vyřesit strukturu v mysql aby tyhle dotazy byly co nejvíce efektivní.

logik

Ohejbat realitu (zachycenou DB) tomu, že chci použít nějakej nástroj je IMHO většinou cesta do pekel. Respektive na to člověk pak hrozně nadává, když je třeba provést v projektu změna. A ať je udělaná analýza sebepečlivěji, na spoustu věcí se vždy přijde až během vývoje (nemusí to bejt ani vina programátora, často je to vinou zákazníka a zadání, nebo šéfa :-)) a změny jsou tedy nutný.
V tu chvíli díky „optimalizovaný“ struktuře databáze začne člověk prskat jako kocour a buďto dělat věci natřikrát přes ruku, nebo přepisuje půlku aplikace. Proto radši nechám realitu tak jak chci, a případný „upravený“ dotazy si taham pomocí views či uloženejch procedur.

Traverzování kolem stromu je sice hezký, ale do tý doby, než člověk záznamy v daný tabulce chce i upravovat. Takovej update celý tabulky kvůli vložení jednoho záznamu je cool. Konkurenční transakce prakticky nemaj šanci.
Nemluvě o tom, kdybych náhodou chtěl logovat všechny změny provedené v databázi (odsypávat staré záznamy do vedlejších tabulek). Kvůli změně jednoho řádku kopírovat celou tabulku… Opět je to velmi dobrá technika a nezřídka ji používám, ale její použití je omezený.

Not in dotazy se daj, ten zápis mi ale nepřipadá jednodušší než
klasickej framework, co žere SQL.

Jinak opakuju, proti notorm nic menám a dokážu si představit spoustu příkladů, kde by to byla jedna z nejvhodnějších knihoven na přístup k
databázi. Jen se IMHO prostě nehodí na vše :-)

PS: A nad ťěma stored procedure se zamyslete, dopsat by tam šly snadno – podědit objekt tabulky tak, aby jako argumenty žraly argumenty SP. Ve větších kouscích software bych se aspoň osobně bez SP obcházel těžko…

logik

No, v tom bych zrovna problém neviděl, protože i když jmenný prostor nesdílí, tak je imho prasečina je pojmenovávat stejně.

Asi bych to osobně implementoval tak, že někde v konfiguráku (škoda že php nemá aplikační server, tam by se krásně šlo vše načítat z db bez ztráty výkonu) názvy stored procedur. %db->nazev() by pak volal stored proceduru – pokud by byla v tom konfiguráku. Když by se to doplnilo ještě
speciální metodou _table($tablename) a _proc($tablename), tak by to řešilo i ty výjimky, kdy je člověk čuník, nebo nechce psát konfigurák.

V současnym projektu to moc nepoužiju, ale jestli budu někdy pracovat na projektu, kde se mi tadle knihovna bude hodit, tak si to dopíšu :-), bude to IMHO pár řádek kódu…

jos

co přesně znamená uložené procedury nesdílí jmenný prostor s tabulkami ?

jestli si měl na mysli to co já, tak je to závislý na implementaci

MSSQL:

create table dbo.foo
(id int not null)
GO

create procedure dbo.foo
as
select * from dbo.foo
GO

Msg 2714, Level 16, State 3, Procedure foo, Line 3
There is already an object named ‚foo‘ in the database.

okbob

Pořadí spojování tabulek se u moderních plannerů řídí statistikou – kdežto u tebe zápisem – to bych viděl spíš jako krok zpět. Tak asi na projekty na které se nasazuje PHP to třeba úplně tragické být nemusí – už vzhledem k tomu, že NotORM vygeneruje indexy, které jsou pro některé programátory tabu. Což na jednu stranu je výhoda, na druhou stranu nevýhoda, poněvadž pro některé dál může zůstat db black box, což je špatně.

okbob

No já používám většinou INNER JOIN i na vazby 1:N. Použití INNER nebo OUTER JOINU nemá vztah ke kardinalitě vazby.

František Kučera

Ad „Ale jestli kvůli stejnému výpisu položím 1 dotaz (který přenáší hodně dat) nebo 3 dotazy (které přenáší málo dat), tak to bude velmi podobné“

Ty to ale bereš tak, že jakmile máš ty data v PHP, pracuješ s nimi „zadarmo“, že drahá je jen práce databáze. Ale tak to není. Sice ušetříš nějakou práci databázi, ale pak ji musíš udělat v PHP*

*) ono to vypadá snadno, použiješ hash-tabulku, ale i její sestavení něco stojí, je to v podstatě indexování (přičemž takto indexovaná data za chvilku zahodíš) a vyhledávání sice znamená napsat data[„klíč“], což je pár znaků v kódu, ale z hlediska výkonu to taky něco stojí. Proč by měl být tvůj kód v PHP efektivnější, než kód relační databáze? Ta může navíc používat mezipaměť, která přetrvává i mezi dotazy/požadavky.

František Kučera

„spíš bych ji pojmenoval SimpleORM“

Já bych to ani SimpleORM nenazval – že to má některé nevýhody ORM, ještě neznamená, že to je ORM. Já tu nikde nevidím ty objekty, spíš mi to přijde jako jiný pohled na výsledkovou sadu. Jakubův název (NotORM) je tedy v pořádku*.

„nesmí se to přehánět – je blbost generovat v SQL komplet html stránku.“
Souhlas, v databázi se dá vytvořit nějaký „mezitvar“, třeba v XML – může to být lepší forma, než obyčejná výsledková sada (tabulka/matice).

*) i když tahle móda negací…

František Kučera

„Nutí mě vytvářet nebo generovat nějaké třídy, které mi jsou k ničemu.“

WTF?

„Kladou neefektivní dotazy“

V takovém případě můžeš ten dotaz „přebít“ vlastním – donutit ORM, aby použilo tebou napsané SQL (ukážeš mu cestu), ale mapování už zase provádí to ORM (udělá za tebe tu nudnou práci). Nevýhodou je spíš to, že zakrývají relační databáze, zvyšují složitost systému, znepřehledňují*, svádí k jednoduchým (a často hloupým) řešením…

*) ale zároveň i zpřehledňují – přidají totiž jednu horizontální vrstvu, ale ve „vertikálním“ směru aplikaci zase zjednoduší

logik

1. A kdo jinej by měl vědět, např. co se s daným objektem dá dělat, jaké poskytuje možné akce, jaké má vlastnosti atd…? V současnym projektu je
poměrně velká část logiky právě v modelech postavených na ORM.
ORMM rozhodně není ten „blbej moloch“, kterej jen zesložiťuje apliakci a pokládá blbý otázky. Jen se prostě ORM nehodí na všechno a musí se vědět jestli, kdy a jak ho použít. Pak je to výtečnej nástroj zjednodušující programátorovi ži­vot.

2. Zajímavé – používám ORM, a rozhodně do DB špatný dotazy nepokládám
(teda, aspoň jsem o tom dosti silně přesvědčen),
Ono by koneckonců ani nebyl problém udělat ORM nad notORM :-)

logik

1. A kdo mi zabrání, abych jako model použíl třídu, kterou z ORM podědím?

2. Používám ORM vlastní výroby. Používá se zrhuba takto:

$apps=Applica­tions::find(a­rray(‚lmit‘ ⇒ 10));
$apps->findChild(‚au­tori‘)
$apps->findChild(‚na­lepky‘)
foreach($apps as $app)
{
echo $apps[‚nazev‘];
foreach($apps->childs[‚autor‘] as $autor)
echo $autor[‚jmeno‘];
foreach($apps->childs[‚nalepka‘] as $nalepka)
echo $nalepka[‚nazev‘];
 }

Vygenerovany dotazy:
SELECT * FROM application LIMIT 10;
SELECT * FROM autor WHERE id IN (SELECT autor_id FROM applicationautor WHERE ID in (1,2,3,4,5,6,­7,8,9,10))
SELECT * FROM nalepky WHERE ID in (1,2,3,4,5,6,­7,8,9,10)

logik

1) Pokud jsou optimální selecty, které získávají data z db složitý, mám dvě možnosti:
a) vždy je napsat ručně (byť za mírné podpory nějaké leighweight knihovny typu notOrm – protože v sestavení správných where a join podmínek mi žádná taková knihovna nepomůže)
b) zabudovat genrování správných sql dotazů přímo do modelů: opět k čemu mezivrstvu. Teda pokud nepočítám knihovny typu dibi apod, který trochu zpřehledněj SQL zápis.

Kód přitom zas tak složitej není, protože obecný ORM modely se napíšou jednou a pořádně se otestujou, a v jednotlivých konkrétní modelech se pak píše aplikační logika a popř. upřesnění, jak má knihovna se záznamem nakládat (např. podle kterých sloupců má kontrolovat uživatelská práva).
Až na výjimky se v nich nemusí použít SQL – ale když je potřeba, tak to jde.

Jelikož např. práva jdou efektivněji vyřešit když vím, že dělám select
na děti jednoho rodiče, od nějž jediného mohou zdědit práva než když to nevím, tak jaksi model do generování SQL dotazů tak jako tak šahat musí – takže si nedokážu představit, jak oddělit model a databázovou vrstvu a přitom zachovat efektivní generování uživatelských práv.

Nehledě na to, že např. pro získání podřízených objektů model taky musí nějak „vědět“, jaké jsou jeho podřízené objekty a jaká je relace mezi nimi. A od toho je ke generování SQL dotazů takovej kousek, že vrážet tam další vrstvu se mi zdá jako zesložitění, nikoli zjednodušení.

2) Samozřejmě, že je to v notOrm o něco jednodušší – taky je to knihovna pro jednoduchej snadnej přístup k databázi. Platí za to flexibilitou a možností rozšíření.

Není potřeba předem určovat, se kterými tabulkami se bude pracovat.
Na druhou stranu, když budeš chtít pracovat pouze s autorem první aplikace, tak se ti stejně natáhnou všechny. Což příliš optimální není. Já si můžu vybrat. Nebo si myslíš, že tam dopsat automatický přednačítání všech podřízených záznamů by byl takovej problém? Asi tak na tři řádky. Je fakt, že tam můžu dát parametr, jestli to rovnou přednačíst nebo ne, to neni blbej nápad.

API zdá se vůbec nedovoluje přistoupit ke sloupci applicationau­tor.poradi.
Ale umožňuje, akorát jsem to psal z hlavy, tak jsem se nekouk, že autoriaplikace neni čistě vazební tabulka. Samozřejmě můžu přistupovat i pomocí

$app->childs('applicationautor')->childs('autor')

Akorát mám možnost dopsat objektu i složitější vazbu, pokud potřebuju.
Např. „ignorovat“ MxN vazební tabulku, která je v podstatě implementační detail.

Přenášení jen použitých sloupců je zajímavá featura, nicméně zatím jsem se nedostal do situace, kde bych masivně načítal takové množství záznamů, že by mě to pálilo. Taková kontrola uživatelskejch práv je daleko větší zdržení a to se udělat musí. Nicméně je to hezký nápad a nevylučuju, že se jím někdy neinspiruju.

No á závěrem :-) výhody mojí knihovny nevidíš, protože nevíš, co umí. Samozřejmě když porovnáš lehkou knihovnu zaměřenou na co nejjednodušší načítání dat z db se složitějším frameworkem, tak jednoduchá kniohovna mírně vyhraje snadností zápisu… Stejně jako když si dá silniční speciál na dálnici závody s offroadem.

Fór je v tom, že u toho funkce Tvojí knihovny končej, zatímco mojí začínaj, ať jde o modifikaci dat (ať už s automatickou nebo manuální synchronizací s databází), kontrolu práv (poměrně dosti netriviální, pomocí ACL, uživatelských skupin a dědění oprávnění z nadřazených objektů), kontrolu kvót na počet podřízených objektů daného typu, validace dat apod. A to všechno vzhledem k databázi šetrným způsobem.

Netvrdím, že se to hodí vždy a všude – dovedu si představit spoustu projektů, kde bych notORM použil radši, ale i naopak – jen prostě tvrdím, že jsou případy, kdy ORM přístup umí využít výhody ORM, aniž by ztratil schopnost pokládat inteligentní dotazy.

logik

Ad NAD knihovnou – právě podstata toho příspěvku nad tím byla ta, že pokud potřebuju do databáze přistupovat se složitejma kritériama, tak si stejně drtivou většinu podmínek a SQL logiky musím napsat sám (respektive použít knihovnu, která mi ji vygeneruje). Čímž se mi naprosto ztratí výhoda oddělení SQL od modelu.
Kdyby šlo napsat

VytvořModely(NotormVýraz)

tak by to smysl mělo, jenže to prostě nejde. Respektive nejde tak, abych pokládal dotazy efektivně, aniž by NotOrm výraz generoval samotnej model (respektive tableModel – čili objekt popisující záznamy jednoho typu, ale nechci zabejhat do podrobností).

V tom cachování máš pravdu – knihovna je ve vývoji, takže tam dopisuju co potřebuju. Todle je rozumný, takže volitelný automatický přednačítání dle parametru už tam je. :-)
Že se to používá výjimečně ale neni až tak úplně pravda – např. zobrazuju seznam věcí a u prvního (nebo i prvních třech z třicíti) zobrazuju víc podrobností. (např. seznam letenek, u první zobrazim i
kde se přesedá atd…). AJAXem pak na rozklik zobrazuju podrobnosti u dalších záznamů…

Ad ukládání – psal jsem o možnosti volby automatický synchronizace s databází. Ty máš jenom manuální. Nebo jak by Ti fungovalo

$application["id"]=10;
$application->update()

:-)
Ale zrovna jak ukládání, stejně jako (fakultativní) přednačítání, jsou naprosto nedůležitý detaily – stejně jako já si můžu dopsat načítání, tak ty synchronní ukládání nebo možnost měnit idčka. O takovejdle „blbinách“ se imho nemá smysl bavit, je to jen honění trika co koho zrovna napadlo a implementoval.

Co se týče update rank = rank + 1, tak takovýdle dotazy jsou často způsobený špatnym návrhem datový struktury – akce jednoho objektu by neměly vést k úpravě celé databáze. Navíc pokud nemáš aplikační logiku v databázi, tak jsou často i nelegální (obcházej nějakej kus aplikační logiky, takže časem při úpravách kódu může dojít k nekonzistenci).

Nicméně i pokud je nutné je použít a mám aplikační logiku v modelu, tak jediný místo, kde můžu aspoň trochu legálně takovejdle dotaz položit, tak je v modelu. A tam mam přístup k plnýmu SQL. A nevěřim, že v NOTorm bude ten zápis o tolik jednodušší než v SQL.

Ono třeba v tom tvym případě – je to jednoduchej dotaz: ale já bych zas u toho např. musel řešit práva – můžu to vůbec udělat? A zajistit, že pro všechny ty záznamy bude hodnota rank+1 povolená, promyslet, co se stane když tydle dotazy poběžej dva najednou atd…

Nicméně, protože kromě samotnejch modelů používám i tableModely, tak
pokud bych takovýdle update potřeboval tak často, že by byl opruz je psát v čistym SQL, tak by nedalo moc práce tam tydle metody dopsat – metody na generování where podmínek mám, na generování update SQL taky.

Borek Bernard

Ad 1) Tohle se nedá říct obecně, pokud vytváříš aplikaci s bohatým doménovým modelem, nejen že není tato vrstva navíc zbytečná, ale dokonce se stává jsou z nejdůležitějších částí kódu vůbec. NotORM vypadá šikovně pro primárně read-only stránky, ORM má zase svou sílu v „plnohodnotných“ aplikacích. Možná jsi to tak i myslel, jen zde v komentářích nějak zapadl kontext.

Patrik Štrba

Nebolo by lepšie pri ukladaní/loadovaní do/z cache robiť hash z toho namiesto ukladania celého dotazu ?


$this->notORM->cache->load("$this->table;" . implode(",", $this->conditions));

na


$this->notORM->cache->load(md5("$this->table;" . implode(",", $this->conditions)));

???

logik

S tim prozkoumávánim cache souhlasim, ale selektivně promazávat cache se stejně bude jen pro určitou tabulku či konkrétní dotaz – pak by mělo smysl uvažovat o dvojúrovňový cache [table][md5(con­ditions)] kde to md5kovaní by se dalo zapnout jen pro produkční server…

Oldis

a jak tahle knihovna resi omezeni vyberu zaznamu z tabulky jedne joinem dat z tabulky druhe?

roman

Autor tejto urcite zaujimavej kniznice ma velku prax v tvorbe PHP kodu, ale to mu nepomoze urobit lepsi nastroj, ako priamu tvorbu dotazov. Neda si vysvetlit, ze subselecty su do urcitych verzii MySQL(stupnovite) neefektivne a mysli si, ze eleganciou to nahradi. Objektovejsie sa to bude pisat, ale neefektivnejsie to bude fungovat. Je to pekna hracka a tak to aj treba brat.

srigi

Kcem sa opýtať či NotORM obsahuje profiler/rozhranie (najlepšie prepojitelne s Nette 0.9), kde zistim počet rq a ich znenie. Poprosim ukážku kódu. THX

logik

A co použít dibi knihovnu a profiler v ní?

jxp

Jak předám NotORM informace jak se konektnout k db (driver, password, username …)

jxp

Zajímavé. Proč Dibi a ne přístup k mysql co mám PHP nativně? Vždyť to musí NotORM spomalovat.
Řeší NotORM zápis do tablulky (insert)?

phper

Mám tabulku dbtest v které jsou dva sloupce id (auto increment) a data
for cyklem to chi po jednom řádku číst, když je to formulováno takto, tak
$data = $dbtests->data[$i];
mi laděnka hází chybu Undefined variable: dbtests
nevíte někdo co s tím

Sebastian

Dobrý den, bohužel v celé diskusi bych našel příspěvků, které by Vám nepsali, že NotORM je nesmysl, hloupost a (špatná) hračka, které se dají spočítat na jedné ruce.
Přiznám se, že o existenci NotORM vím už delší čas, ale na první pohled se mi nelíbílo, že neni objektový, proto jsem jej nestudoval, spíše jsem se mu vyhýbal a zavíral jsem před ním oči.
Ale dnes jsem si přečetl tento článek a příjde mi, že to neni hračka, pokud bych pracoval na nějaké projektu, kdy by byl pro mě přínosný, určitě bych po něm sáhnul jako po užitečném nástroji.
Výborná práce.
PS: Určitě by mu slušelo i možnost psát inserty ve stejné syntaxi, představte si situaci, kdy při vytváření nové aplikace chci zrovna i přidat novou nálepku. Vytvořím aplikaci → získám její id → vytvořím nálepku → získam její id → spojim je… :)

talpa

tohle je vazne prasecina, tohle vetsinou potrebujeme v jednom selectu a pak s tim dal pracujeme, efektivnejsi je ,se sql serveru dotazovat co nejmene a rovnou si vracet hotovou datovou kostku. To ze lidi neumi sql pak resi takovejma jak to nazvat… no…fuj

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.

Pocta C64

Za prvopočátek své programátorské kariéry vděčím počítači Commodore 64. Tehdy jsem genialitu návrhu nemohl docenit. Dnes dokážu lehce nahlédnout pod pokličku. Chtěl bych se o to s vámi podělit a vzdát mu hold.