Ako dokážete fungovať bez ORM/mapperu?

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
tomasbauer
Člen | 2
+
0
-

Ahojte,

chcem sa s vami podeliť o môj pohľad na prácu s modelmi, pretože by som potreboval vedieť, či mi niečo náhodou neuniká.

Veľké percento ľudí z tohoto fóra preferuje NotORM, Database či dibi. Sú to perfektné knižnice, ktoré mi pomáhajú už roky (predtým dibi, teraz skôr NotORM) a použil som ich na veľmi veľkom množstve jednoduchých projektov či ako základy vlastných „ORM“.

Mám taký pocit, že veľa z vás používa NotORM, Database či dibi ako hlavnú knižnicu na prácu s databázou, čo mne príde neuveriteľné. Nerozumiem ako dokážete používať databázu bez mapovania záznamov do nejakých modelov. Nejaká časť z vás používa srigiho Jednoduchý Model s NotORM ktorého významu celkom nerozumiem v kontexte nasledujúceho.

Neviem si predstaviť, ako dokážete vyriešiť napríklad niečo takto triviálne (nasleduje veľmi zjednodušený príklad):

  • eshop, máme tabuľku s objednávkami (id, user_id, payment_status, order_status, created_at, …)
  • uživateľ má niekoľko objednávok
  • každá objednávka má dva typy stavov: „nová“, „odoslaná“, „stornovaná“ a tiež „zaplatená“, „nezaplatená“

A teraz to príde – potrebujeme zobraziť napríklad zoznam objednávok nejakého užívateľa (v jeho profile, eshopovej administrácii, …). Čo spravím ja v ľubovolnom/vlastnom ORM:

foreach ($orders as $order)
{
	echo sprintf("%s (%s) - %s", $order->getReadableId(), $order->getPaymentStatus(), $order->getOrderStatus());
}

Čoho výstupom bude niečo ako:

  • 10249444 (zaplatená) – odoslaná
  • 10249445 (zaplatená) – nová
  • 10249446 (nezaplatená) – nová

Ak by som využíval čisté dibi, Database či NotORM, dostanem iba holé dáta, to znamená že na každom mieste, kde by som potreboval zobraziť readableId, paymentStatus alebo orderStatus (uživateľsky čitateľné stringy, vyhodnotené na základe id, payment_status a order_status), musel by som buď opakovane porovnávať hodnotu stĺpca a na základe neho vypísať nejakú adekvátnu textovú hodnotu (1 = „zaplatená“, 0 = „nezaplatená“, …), alebo si na to vytvoriť nezávislý formátovací helper.

Tento príklad je naozaj triviálny, v skutočnosti sú metódy modelu zložitejšie a vykonávajú oveľa viac práce, ako iba konvertovanie číselných stavov na textový popisok, pretože motódy ako $post->publish(), $order->pay(), $customer->sendSMS() či $post->delete() môžu obsahovať aj niekoľko riadkov kódu/logiky.

Mňa teda hrozne zaujíma, ako bez použitia mapovania dokážete pracovať so svojimi modelmi v podobných situáciach.

Buď mi niečo uniká alebo som skutočne (po 10 rokoch programovania na webe) amatér, keď k podobným veciam potrebujem minimálne mapper databázových záznamov do objektov.

Ešte dodatok: samozrejme, riešením je použiť nejaké na Nette nezávislé ORM, napríklad Doctrine, ale jednak mi toto riešenie nevyhovuje pri menších projektoch a jednak môj „problém“ nespočíva v tom, ako vyriešiť vyššie popísané prípady, ale v tom, že nerozumiem, ako dokážete vy pracovať, napríklad, iba s Database či NotORM.

Ak by niečo nebolo jasné, dajte mi vedieť a dovysvetlím, pretože niekedy sa vyjadrujem ako idiot :-)

Editoval tomasbauer (15. 9. 2011 16:44)

bazo
Člen | 620
+
0
-

fungovat sa da velmi jednoducho: proste si to sql napises rucne

tomasbauer napsal(a):

Ak by som využíval čisté dibi, Database či NotORM, dostanem iba holé dáta, to znamená že na každom mieste, kde by som potreboval zobraziť readableId, paymentStatus alebo orderStatus (uživateľsky čitateľné stringy, vyhodnotené na základe id, payment_status a order_status), musel by som buď opakovane porovnávať hodnotu stĺpca a na základe neho vypísať nejakú adekvátnu textovú hodnotu (1 = „zaplatená“, 0 = „nezaplatená“, …), alebo si na to vytvoriť nezávislý formátovací helper.

existuju aj joiny, takze ziadne helpery netreba, textove hodnoty budes mat v ciselnikoch

Editoval bazo (15. 9. 2011 16:53)

petr.pavel
Člen | 535
+
0
-

Já myslím, že od toho je tu model. Osobně bych měl
class OrderModel extends BaseModel

V něm statické metody getReadableId, getPaymentStatus, getOrderStatus a volal bych je

foreach ($orders as $order)
{
        echo sprintf("%s (%s) - %s", OrderModel::getReadableId($order), OrderModel::getPaymentStatus($order), OrderModel::getOrderStatus($order));
}

Kdybys chtěl mít $order jako potomka nějaké své vlastní třídy, tak bys asi musel rozšiřovat NotORM/Database a to nevím, jestli by šlo. Předpokládám, že bys stál o to zachovat lazyness dotazů do databáze. Protože jestli ne, tak bys mohl dotaz jednoduše vykonat hned a data přelít do vlastního objektu.

studna
Člen | 181
+
0
-

Zkus pročíst toto téma https://forum.nette.org/…ny-na-notorm. ;)

Editoval studna (15. 9. 2011 21:24)

smasty
Člen | 90
+
0
-

Aj NotORM aj dibi (o Nette\DB netuším) majú možnosť nastaviť triedu reprezentujúcu riadok v tabuľke. Stačí teda vytvoriť potomka DibiRow/NotORM_Row a metódy ako getReadableId si dopísať.

Nox
Člen | 378
+
0
-

Tak ono jde udělat všechno i ve strojovým kódu, nevim přesně jaký má tohle téma účel/dotaz, i když sám mám ORM rád.

Ono SQL dotazy jsou to ruční mapování, tak je to prostě na rozmyšlení jestli to chceš sám nebo knihovnu, jestli se to pro projekt hodí nebo ne… normálně používám Doctrine 2 ORM/ODM, pro školní projekt jsem teď zvolil NotORM

D2 je u projektu, kde je mnoho interakce mezi nemnoha objekty, které jsou skutečně objekty a ne tolik tabulková data*, je tam spousta manipulujících objektů, spousta přístupů přes relace atd.
NotORM zase u projektu kde ta manipulace s daty bude centralizovanější, víc hromadnější…i když ten objektový přístup by asi taky nebyl zlý, není to černobílé

*) i když se dá asi o čemkoli uvažovat jak tabulkově, tak objektově

Kdyby ORM byly výkonnější, asi by – spolu s generováním schémat, tzn. bez duplicitní definice – bylo o dost míň debat. D2 na tom myslim není zdaleka tak špatně jak se povídá, ale zase lightweight to není … (už jen inicializace managera je trochu ťafka)

Nejsem superprofík a nemám zdaleka tolik praxe (natož 30 let bankovních aplikací v Javě), jen mých 36 halířů

tomasbauer
Člen | 2
+
0
-

bazo: jasné, tvoja odpoveď vlastne smeruje k tomu, presunúť všetku logiku do SQL (procedúr, triggerov, selectov). Niečo mi však hovorí, že toto nie je riešenie, ktoré využíva väčšina ľudí nepoužívajúcich ORM.

petr.pavel: tvoje riešenie sú defacto spomínané helpery, implementované cez priame statické volania, čo z hľadiska konzistencie tried nie je príliš vhodné. Je to takmer identické s volaním obyčajných funkcií.

studna: ten link mi ušiel, veľmi pekne ďakujem za tip.

smasty: toto bohužial nie je riešenie. To, čo v mojich príkladoch potrebuješ docieliť je, aby (pre zjednodušenie) každý riadok z tabuľky bol samostatným objektom príslušnej triedy. To znamená riadky tabuľky articles musia byť inštanciou triedy Article, tabuľky users zasa User a tak ďalej a stĺpce tabuľky musia byť namapované do atribútov objektu (members).

Nox: účel je zistiť, ako pracujú s dátami z databázy ľudia, ktorí nepoužívajú Doctrine či iný ORM a pracujú s NotORM, Database, dibi alebo kľudne priamo s PDO :-)

Editoval tomasbauer (16. 9. 2011 11:15)

smasty
Člen | 90
+
0
-

tomasbauer wrote:

…aby každý riadok z tabuľky bol samostatným objektom príslušnej triedy.

To predsa vôbec nie je problém. stačí si nad dibi/NotORM postaviť mini nadstavbu, ktorá sa o to postará. Je to otázka jednej metódy. Prípadne si podediť DibiResult/NotORM_Result a chovanie si upraviť tam.

studna
Člen | 181
+
0
-

U NotORM jde nastavit vracená třída pouze globálně. Takže vracet různé entity (User, Article, Car, ..) není moc dobře řešitelné.

Lopo
Člen | 277
+
0
-

Ja som v poslednom worku fungoval s cistym dibi, vacsinu casu dokonca bez fluent.
vypolyvalo to s charakteru prace – obrovske kvanta dat (stovky tabuliek, statisice az miliony zaznamov), z ktorych sa vytvarali rozne reporty. Tzn ze jeden dotaz kludne joinoval aj 10 tabuliek, pricom vysledok pouzil len ako zdrojove data pre dalsi podobny dotaz …

V takom pripade uz je vacmene akekolvek mapovanie vykonove zdrzovanie a treba ist k datam na co najnizsej urovni

A kedze dotycny system z ktoreho sa to generovalo (postaveny nad Ingres DB) mal dost spatne riesene lockovanie tabuliek, pre urychlenie pocitania aj pre zmensenie casu zamknutia tabuliek (casto to dokazalo odstavit celu firmu) sa vela dat (ciastocne spocitanych aj vyslednych) prelievalo do MySQL ktora tym padom sluzila ako cache (do povodneho Ingres-u sme vacmene nemohli vobec zapisovat, dali sa vyuzivat len temporary tabulky, ktorych pouzitie bolo vo vela pripadoch hodne problematicke).

Tym pouzitim 2 roznych DB uz by zase bol len dalsi problem pri pouziti nejakeho mapovania
takze ideal riesenim bolo pouzivanie cisteho dibi, vacsinu bez fluentov – aj to dibi bolo pouzite len pre zjednodusenie aby sa nemuseli stale pouzivat uplne PHP nativne funkcie na pracu s DB

Ale aj ovela jednoduchsie veci co tam boli tak sa robili len s dibi – aby bolo vsade rovnake rozhranie a pretoze sa mi nechcelo ziadne ORM/mapper/whatever ucit (ale hlavne na to ucenie ani nebol cas)

zaver: su pripady ked akekolvek ORM/mapper je skor na skodu, ci uz kvoli vykonu alebo z dovodu kombinacie viacerych systemov nad ktorymi sa pracuje, ale urcite by sa naslo vela dalsich pripadov

obecne by som povedal ze v podnikovej sfere je pouzitie beznych ORM/mapperov dost problematicke

mkoubik
Člen | 728
+
0
-

Úlohou mapperu není 1:1 mapovat atributy entity na sloupce tabulky, to co jsi popsal na začátku (hromada složitých dotazů) je vlastně celé mapper, a pokud bys chtěl pracovat s entitama, tak bys je vytvářel až na konci toho procesu z celkových výsledků.

Lopo
Člen | 277
+
0
-

mno lenze na konci procesu tie vysledne data tiez nemaju jednoznacne clenenie pouzitelne na urcenie entit … niektore data sa navzajom prekryvaju pouzitelnostou, niektore sa potom vo vyslednej prezentacii zase skladaju z viacerych tabuliek a pod. – proste hodne zlozity a specificky system kde sa nedalo jednoznacne nadefinovanie mapovania entit na DB

preto sa aj data vacsinou brali z model vrstvy vo forme dibiresult, v niektorych zlozitejsich pripadoch dokonca array of dibiresult, tj pole s viacerymi dibiresult polozkami – presne na mieru pre dalsie pouzitie