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

Zdroják » Databáze » Videotutoriál Doctrine 2 a NotORM: ukládání dat

Videotutoriál Doctrine 2 a NotORM: ukládání dat

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

V druhé části subjektivního srovnávání Doctrine 2 a NotORM se autor Jakub Vrána zaměří na ukládání dat do databáze v obou těchto systémech.

První část
videotutoriálu se zabývala získáváním dat. Silnou stránkou Doctrine 2
je ale podle některých názorů hlavně ukládání dat. Podle mě je sice
ukládání dat ta jednodušší část, na které se toho nedá moc pokazit ani
získat, ale podívejme se, jak se s touto částí aplikace Doctrine 2
vypořádá.

Doctrine 2 – ukládání dat

Screencast

Zdrojové kódy

Co se mi na řešení v Doctrine nelíbí?

  1. Pro získání referencí na entity se pokládají zbytečné dotazy.
  2. Pokus o přiřazení neplatné entity nezpůsobí žádnou chybu. Místo toho se prostě přiřadí hodnota NULL.
  3. Ani přiřazení hodnoty do neexistujícího sloupce nezpůsobí žádnou
    chybu. To si musíme ošetřit sami pomocí setterů, Doctrine nám s tím
    nijak nepomůže.
  4. Doctrine sice má informace o omezení jednotlivých sloupců (např.
    maximální délka řetězce), ale při nastavování hodnot je nijak nevyužívá.
    O kontrolu se opět musíme postarat sami.
  5. Pro zvýšení počtu návštěv se položí dva dotazy. Ty se navíc
    neprovedou atomicky, takže při konkurenčním přístupu se některé návštěvy
    nezapočtou. Řešením je ruční obsluha transakce, což se mi zdá poněkud
    nízkoúrovňové.
  6. Vynulování počtu návštěv u všech článků si vyžádá tolik dotazů, kolik je článků.
  7. Doctrine automaticky vytváří jakési proxy soubory, které bychom neměli ručně měnit.

Některé připomínky lze naštěstí poměrně snadno vyřešit. Body 1 a 2 zmizí v případě, že místo metody find použijeme metodu getReference. U nich jde tedy spíše o kritiku seriálu na Zdrojáku, který sice ukládání dat věnuje jeden díl, ale o této důležité metodě se nezmiňuje.

Bod 4 přímo řeší některé databázové systémy. Např. v MySQL lze zapnout striktní režim,
který při pokusu o vložení neplatných dat způsobí chybu, ale třeba v
SQLite nic takového neexistuje. Doctrine obsahuje vrstvu pro abstrakci
databázových systémů, která by chování měla sjednocovat.

Body 5 a 6 lze vyřešit použitím DQL. To už jsme rozebírali v prvním
díle – jednak bych se u tak vysoké vrstvy, jakou Doctrine je, chtěl
použití DQL pokud možno úplně vyhnout. A za druhé mi stejně jako v
prvním díle vadí, že se dvě podobné věci dělají zcela odlišným způsobem –
když chci vynulovat počet návštěv u jednoho článku, tak k tomu můžu
použít práci s entitami, když u více článků, tak je vhodné použít DQL
(obdobně zvyšování počtu návštěv oproti nastavování pevného počtu).

NotORM – ukládání dat

Screencast

Zdrojové kódy

Ve srovnání s Doctrine vidím tyto rozdíly:

  • NotORM se nepokouší o abstrakci databázových systémů, takže i datum je nutné zformátovat pro tu kterou databázi.
  • Pokus o přiřazení do neplatného sloupce nebo neplatné reference
    způsobí podle očekávání chybu. Chování při přiřazení neplatné hodnoty je
    závislé na databázovém systému.
  • Zvýšení počtu návštěv se dělá stejně jednoduše jako přiřazení pevné
    hodnoty. Není zapotřebí vlastní zahajování transakce ani ruční psaní
    dotazu.
  • Aktualizace všech záznamů se zvládne jediným dotazem.

Závěr

Ani při ukládání dat mi knihovna Doctrine 2 příliš nevyhovovala. Ve třetí části se podíváme na definici modelu.

Odpovědi autora Doctrine jsou opět na autorově blogu.

Disclaimer: Autor textu je zároveň autorem popisované knihovny NotORM.

Komentáře

Subscribe
Upozornit na
guest
38 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
Honza Marek

3: Přiřadit do neexistujícího sloupce prostě nejde. Nenamapovaná proměnná není považována za sloupec.

4: Doctrine nechává validaci na vyšší vrstvě. Já používám validátory ze Symfony a jsem s tímto řešením naprosto spokojen. Kontrola probíhá pomocí onFlush události, takže se děje tak nějak sama.

6: Nechápu proč píšeš, že to nejde, když o odstavec dál napíšeš, že to jde.

7: No a? Proxy třídám jednou nastavíš adresář, kam se mají vyrábět, potom se o ně už opravdu nemusíš starat.

Cechjos

To je ale odpovědnost (a „problém“ vzhledem k __get/set metodám) programátora, aby udržel objekty v požadovaném stavu. Co když bude chtít před persistnutím ještě použít nějaké metody daného objektu (které používají property, u které se uklepl) – bude problém Doctrine, že se nějakou černou magií nedozvěděl o problému v objektu, o kterém nemůže mít páru? A mají ho zajímat property, u kterých mu programátor neřekl, že je chce ukládat do dtb? (Odpověď může být: Dle dokumentace; ale stejně je to v prvé řadě odpovědnost programátora.)

paranoiq

tuhle věc řeší třeba v Nette NObject. není to práce persistenční vrstvy

Oldis

neni, jde o chybu, a tudiz by o ni mel programatora nekdo spravit, misto toho aby se preklepl, pokracoval a za tyden hledal proc neco nefunguje jak ma, proc se neco neuklada, a pidil se kodem kde to tak asi muze bejt, zbytecna ztrata casu a prostredku.

jos

1. sme v PHP, smiř se s tim, nebo pošli patch; očekávej že nebude přijat, protože autoři PHP si váží víc bastlířů/amatérů než programátorů (asi kvůli poměru 100:1)

2. jestli nemáš unittesty, seš nahranej tak jako tak

3. jestli používáš knihovny který ti brání v unittestování, tvuj problém (nevim jak je na tom Doctrine)

Oldis

dostatecnej error reporting dobre predchazi potrebe monstroznich konstrukci testů

jos

eh, tak to ani náhodou, to bys musel tu aplikaci celou proklikat všema možnejma cestama po každým commitu

jos

1. může to dávat smysl v databázích bez cizích klíčů.

2. sám píšeš, že to vynutit jde, kvalita zdejšího tutoriálu je nesouvisející věc.

3. nezapomínej že de o instanci PHP třídy. kdo nemá error_reporting = -1 tak má pravděpodobně víc problémů; u NotORM by se naopak někdo mohl zeptat: a to si to pole musim dycky osekat sám? proč notor nezná strukturu db? (odpověď na tuhle otázku je zároveň odpovědí proč je hloupost srovnávat Doctrine a NotORM)

4. to je hustý, pošli jim patch.

5. jednak koukam, že (viditelná) podpora transakcí je v NotORMu až od 5. ledna 2011 (ehm?); druhak si sám tu transakci v příkládku nestartuješ (předpokládam stejnou konfiguraci db v obou případech), což je tragédie; beztak já osobně považuju transakce za něco, do čeho mi sebechytřejší udělátko nemá co kecat a jako programátor se o to rád postaram sám

dál – píšeš, že insert vrací NotORM_Row; za předpokladu, že update to vrací taky: co bude v atributu visits?

6. to sis našel pořádnej klacek, to je jako bych o nějakým autě tvrdil že jezdí pomalu a ještě bych drze řekl že sem jezdil pozpátku.

7. jak píše %první%, ten adresář se dá nastavit

disclaimer: autor názoru je zároveň autorem svého vlastního db bazmeku a nepoužívá ani Doctrine NotORM

Filip Procházka

ad 5: Jakub na tohle transakci nepotřebuje, protože jeden update, kde na úrovni databáze zvýšíš hodnotu políčka +1 je vždy atomický. Ten záznam si tam získává pouze pro vytvoření objektu $article, přes který se to musí updatovat, protože se to tváří jako ActiveRecord

jos

v tom umělohmotným příkladě to nepotřebuje, v reálným světě by mu za to ten co by přišel o data zlomil obě ruce

jos

jo tak, to je jen zkratka, OK

neznám komplexně přístup Doctrine, vyhovuje mi toto:

1. instance držící spojení do databáze odmítne spustit dotaz pokud není nastartovaná transakce

2. v destruktoru rollbackne všechny necommitnutý transakce

jos

tak sem si chtěl sám odpovědět na otázku „insert vrací NotORM_Row; za předpokladu, že update to vrací taky: co bude v atributu visits?“ a bylo to velmi zábavné

předně – odpověď je, že update vrací počet ovlivněnejch řádků nikoliv NotORM_Row; když chci vědět jakou hodnotu tam vlastně máme, musíme si tam extra šáhnout (s tim nemam problém)

takže stáhnul sem, rozbalil, a připojil se k db; prdnul sem si to do skriptu kterej používam na pokusy v projektu co hákuju, takže už v něm bylo zhruba toto:

<?php
// require všeho možnýho
$env = dejsemenv();
$cfg = $env()->cfg();
$db = $env->db();
$db->trans_begin();

// ... tady si hraju, dále pískoviště

$db->trans_rollback();

na pískoviště sem rutinně vložil

<?php
...
$pdo = new PDO(
    sprintf(
        "odbc:Driver={SQL Server Native Client 10.0};Server=%s;Database=%s;Trusted_Connection=no"
      , $cfg->get('db.host')
      , $cfg->get('db.dbName')
    )
  , $cfg->get('db.user')
  , $cfg->get('db.passwd')
);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db = new NotORM($pdo);

z naší db vrstvy rázem vyletělo


Warning: ROLLING BACK UNCOMMITED TRANSACTION

což je E_USER_WARNING generovanej z destruktoru naší db vrstvy

jak sem rutinně postupoval, tak sem si nevšiml, že proměnná $db je už obsazená a ono ->trans_rollback() se zavolalo na NotORMu

dovolím si na tomto místě parafrázi:

Já očekávám, že když někde udělám překlep, tak mi nějaká vrstva oznámí chybu. Ukazuje se, že NotORM touto vrstvou bohužel není.

ale což, dyť se skoro nic nestalo, že … instance NotORM je od teď $no

jedu dál, vyrobil sem tabuli

create table dbo.lolek (
  id int not null constraint lolek_pk primary key
)
insert into dbo.lolek (id) select 1

chci vysosat ten řádek

<?php
...
$pdo = new PDO(...);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$no = new NotORM($pdo);
$row = $no->dbo.lolek[1];

aha, parse error, no nic, na schémata můžu zapomenout, ještě že to defaultuje na dbo

<?php
...
$pdo = new PDO(...);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$no = new NotORM($pdo);
$row = $no->lolek[1];

výsledek:

Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[42000]: Syntax error or access violation: 102 [Microsoft][SQL Server Native Client 10.0][SQL Server]Incorrect syntax near ')'. (SQLExecute[102] at extpdo_odbcodbc_stmt.c:254)' in D:Hyposdvl-branchesjostrunkLibNotORMResult.php:122
Stack trace:
#0 LibNotORMResult.php(122): PDOStatement->execute(Array)
#1 LibNotORMResult.php(379): NotORM_Result->query('SELECT * FROM l...')
#2 NotORMResult.php(413): NotORM_Result->execute()
#3 LibNotORMResult.php(505): NotORM_Result->fetch()
#4 wwwfoo.php(69): NotORM_Result->offsetGet(1)
#5 {main}
  thrown in LibNotORMResult.php on line 122

copak se asi stalo? nastavim $no->debug = true;

Notice: Use of undefined constant STDERR - assumed 'STDERR' in LibNotORMResult.php on line 116
Warning: fwrite() expects parameter 1 to be resource, string given in LibNotORMResult.php on line 116

ksakru, s widlema se s timhle můžu jít klouzat, dobrá

$no->debug = function() {var_dump(func_get_args());};
$row = $no->lolek[1];

výsledek:

array(2) {
  [0]=>
  string(33) "SELECT * FROM lolek WHERE (id = )"
  [1]=>
  array(0) {
  }
}

aha, vono se na webu sice píše, že s MSSQL to bylo testovaný, no asi sem neměl použít ODBC, ale ten experimentální dblib, kterej funguje zas jen na unixech;

na vině je totiž NotORM_Result::qu­ote()
$this->notORM->connection->quote($val)
(btw takhle zformátovanej vnořenej ternární výraz, to se jen tak nevidí)

takže šup, vohákovat, na quotování kašlu, jen chci vidět co to vrací

<?php
...
var_dump($row->update(array('id' => new NotORM_Literal('id + 1'))));
// int(1)

no a jak sem to napráskal na začátku, vrací to počet ovlivněnejch řádků

no není ten NotORM úžasný?

BTW toto:

// friend visibility emulation
abstract class NotORM_Abstract

je MASAKR! (a není to jediná věc co mě při běhání po zdrojácích NotORMu rozesmála, odteď když budu někde číst o tom, jak se v PHP prasí, tak si vzpomenu na Jakuba Vránu)

jos

zdovolením se budu držet tykání, je tomu tak od začátku tohohle threadu

ad parafráze: stěžoval sem si na to, že ať zavolam na instanci NotORM cokoliv, tak to bude mlčet do chvíle než to začnu třeba iterovat, zkus si to přečíst ještě jednou

ad $no->{„dbo.lolek“}: FUJ!

ad STDERR: oukej, souvislost se SAPI mi nedošla; když se tady ale oháníš tim jak je NotORM jednoduchej na používání (což nerozporuju), tak by sis tam měl sapi očichat a podle toho se zachovat

ad PDO_DBLIB:
a) v tom bodě sem si nestěžoval na NotORM, ale na PDO (BTW informace s čim a na čem si to testoval sou na notorm.com strohý)
b) http://cz.php.net/manual/en/ref.pdo-dblib.php – je experimentální a s PHP 5.3 se už nedodává (používam 5.3.3)
c) z dblib sme (ještě za časů 5.2) slezli, je to zabugovaný
d) splet sem si to s něčim nad FreeTDS, to je buřt
e) v tom pdo_odbc mají zřejmě chybu, klidně jí reportuj jestli chceš, databází s ODBC rozhraním je dost

ad „public pocit“: ani náhodou, mělo by to bejt private; taky se v PHP snažim hrát si na OOP, ale neznamená to, že sem ochotnej dělat takovýhle prasečinky;

ad „lepší způsob“: třeba to něčemu nastrkat do konstruktoru? ale ty seš tady veterán a já jen cucák, tak co bych ti radil jak programovat

BTW tváří se to, že si můžu napískat jaká třída se má instancovat pro řádky výsledku, bohužel to musí dědit NotORM_Abstract

ad um PHP: zdá se že sme dva, ale jenom jeden měl tu drzost napsat o PHP knížku

ad fungování dle dokumentace: dyť sem se tomu nedivil, jen sem položil otázku, nedostal sem odpověď, tak sem to šel vyzkoušet a pak sem popsal co sem zažil než sem se dobral odpovědi

svým příspěvkem sem se snažil ukázat dvě věci:

1. Vrána je pololhář a demagog (dle zdejší diskuze je vidět že nejsem sám kdo si to myslí)
2. dokáže to každej (bejt demagog)

jo a ještě k tomu NotORM_Literal kterej mě tak zajímal (jen jako proof of misconcept):

CREATE TABLE whatever (id DATETIME NOT NULL CONSTRAINT plk PRIMARY KEY);

$no = new NotORM($pdo);
$row = $no->whatever()->insert(array('id' => new NotORM_Literal('CURRENT_TIMESTAMP')));
var_dump($row->delete());
// int(0)

a co teprve kdyby to bylo součástí podmínky „je menší než“

jos

Za vykání se omlouvám.

já zase za tu ošklivou rétoriku co sem zvolil

Je nějaký důvod, proč bys s výsledkem nic nedělal?

není, ale tu chybu že zavolam metodu na jiný proměnný klidně udělat můžu a nedozvim se to

Jak bych se v případě webového SAPI měl zachovat? Vypsat to na standardní výstup a riskovat, že se to zobrazí uživateli na ostrém webu?

já bych to risknul, když už chce někdo debugovat na ostrým serveru, tak snad ví co dělá a nenakonfiguruje si NotORM tak aby debugovací hlášky viděl uživatel

kdejakej začátečník by za to asi byl vděčnej, protože IMHO typicky nahodí na localhostě nějakej LAMP a pak chce rychle v browseru vidět jak mu to funguje a ne googlit co to je to STDERR a co má místo true přiřadit do NotORM::$debug

na druhou stranu, ze svýho začátečnickýho období si pamatuju že sem nerad četl manuály, takže to může bejt i dobrá lekce

Nebo generovat PHP chyby, i když to žádné chyby nejsou?

tak když nevadí že to píšeš na stderr, tak E_USER_NOTICE mi nepřipadá zas tak odlišný

BTW my nastavujeme display_errors podle přihlášenýho uživatele

máš nějakou zkušenost s driverem přímo od Microsoftu

jo (se sqlsrv.dll, ne s PDO obalem), dokud sme nenarazili na jeden nepříjemnej bug, tak sme si to nemohli vynachválit (nechá si nastavit výstupní kódování, vyrábí příslušný PHP datový typy (s ostatníma je všechno string), kompletní chybový hlášky, ne jen „the statement has been terminated“ jako s mssql.dll)

ten bug – když v triggeru došlo k chybě která zmršila transakci (to sem poprvý viděl hlášku „transaction doomed“), tak se ten driver tvářil jako že je všechno OK a běžící skript si v lepším případě záhadně nabil držku (protože se snažil použít neexistující data), v horším to skončilo vyjímkou bez stackframe (možná kecam, je to už přes rok)

bug sme nareportovali tušim ještě když byla verze 1.1, ve verzi 2.0 byl pořád

a teď koukam, je venku 2.0.1, v changelogu (teda na tom blogu) nic nevidim, snad někde vyhrabu ten testcase a prubnu to

Podporu pro ODBC přidávat nechci – nefunkčnost quote je vlastnost, nikoliv bug

tak to je nemilý (že to takhle vzdali), a co si na uživateli v případě použití ODBC vynutit dodání quotovacího mechanismu? nebo ještě líp – zbavit se deklarovaný závislosti na PDO a dodávat s NotORM tenkej obal nad PDO aby uživatel mohl dodat svou implementaci?

a nebo pro ODBC použít prepared statements? a nebo pro všechno?

minimálně bych uživateli sdělil že s ODBC má zatim smůlu

„Lepší způsob“ – metodu nastrkat do konstruktoru? Raději toho opravdu necháme.

no já tam vidim kupu protected memberů a jednu metodu access() u který sem našel jen jedno volání na něčem jiným než $this, tak snad by to nebyl nepřekonatelnej problém

Co se toho dědění z NotORM_Abstract týče …

no kdyby někdo chtěl zapojit NotORM do existujícího projektu, přičemž by chtěl jako třídu výsledku použít něco svýho (nejlépe něčeho, co dědí netknutelnej kód dodanej třetí stranou), tak je nahranej

nicméně ten scénář je dost zhovadilej, to jo

… jsem si této slabiny vědom. Napadá tě, co s tím?

buď aspoň hlučně upozornit uživatele v manuálu (možná už to tam je?), nebo radši při přístupu na ten atribut prostě líně šáhnout do databáze

jak píšeš, teď v podstatě nutíš uživatele aby do tý databáze vlezl sám, protože přes ten Catchable fatal error co se vygeneruje při echování (toho atributu) se nedostane; přičemž uložení a použití od sebe může bejt v kódu dost daleko, takže to udělá pesimisticky pro každej případ

Honza Marek

Samozřejmě hlavním rozdílem při ukládání dat v Doctrine 2 a NotORM je v tom, že Doctrine 2 implementuje pattern Unit of Work. Zjednodušeně to znamená, že Doctrine má přehled o všech entitách a pokud na entity manageru zavolám metodu flush, tak UOW zjistí, co je nového, otevře transakci, vymyslí si nějaké SQL dotazy, provede je a commitne transakci. O veškeré sledování změn a případnou optimalizaci se v NotORM musí starat programátor.

Osobně nechápu, jak tato informace může být v porovnání opomenuta.

Nox

2. Type hinting?

3. Do neexistujícího sloupce? Doctrine výslovně operuje s tím, že entita != tabulka (entita může být v části tabulky ale i napříč několika tabulkami), tudíž mi tento bod nedává smysl.
Osobně místo docblock => databáze pracuji databáze => docblock, takže kromě úpravy relací mám všechny settery i s type hintingem automaticky = téměř 0 práce navíc (co se setterů týče, třeba to taky tak někomu bude vyhovovat).

5. Pokud z entity nechcete číst, stačí zvýšit pomocí DQL. Pokud chcete, 2 dotazy být musí. I když asi může nastat situace, kdy to není jasné dopředu…

6. DQL

7. Nechápu motivaci pro tento bod… mě osobně je jedno jestli si něco vytváří pokud to není enormní množství dat atp. Člověk někam nastaví složku a pak už o nějakých proxy nemusí vůbec vědět a když to smažete, nic se nestane… To je jako stěžovat si že framework vytváří cache soubor

Palo

No neviem, mne osobne sa napriek všetkému úsiliu autorov Doctrine projektu NotORM zdá lepším riešením. Jednoduchšie sa v tom programuje a výsledok je efektívnejší.

6. bod je ukážkovým príkladom, prečo by som Doctrine nepoužil. Podľa jedného z autorov sa k tomu používa DQL, ktoré je abstrakciou nad SQL.
Lenže ja ju v jeho nasledujúcom príklade nevidím:
$em->createQuery("UP­DATE Article a SET a.visits = 0")->execute();
To som rovno mohol použiť:
$mysql->query("UPDATE Article a SET a.visits = 0");
Výsledok by bol rovnaký. Mám z toho taký pocit, že v Doctrine sa DQL používa všade tam, kde to ORM efektívne nedokáže. DQL by som teda prirovnal k takému „hackovaniu“ ORM.

v6ak

Toto zastávání nativního SQL před DQL je jako kritizovat OOP na jednoduchém příkladě, kdy nemá přínos.

DQL (HQL, …) nabízí práci s objekty, které nabízejí mapř. i dědičnost. V SQL je to problematické.

To, že jedno řešení je v jednom případě zhruba stejné jako druhé. ještě neznamená, že tomu tak bude vždy.

v6ak
notnotorm

A není právě pohodlná dědičnost tabulek výhodou Doctrine2 oproti NotORM? Zadání myslím není potřeba více upřesňovat. Pokud se totiž „špatně“ rozhodnu v Doctrine2, tak jednoduchou změnou konfigurace (metadat pro mapování) vyberu jiný typ dědičnosti a do aplikačního kódu vůbec nezasahuji.

František Kučera

Ad „Zkus prosím napsat nějaké zadání (co se má udělat, nikoliv jak se to má udělat – jako kdyby to psal manažer).“

Budu stručný: „napište to levně a rychle“

:-)

(samozřejmě ORM není všelék, při použití mizerné technologie to dopadne blbě, stejně tak špatný kód nebo použití ORM na nesprávném místě způsobí škodu – ale jinde to naopak ušetří peníze a zrychlí vývoj)

notnotorm

Manažer:
Zákazník chce do existující řešení přidat podporu pro vícejazyčnost a to tak, že nechce samostatné weby. Výchozí jazyk bude stávající. Nevyplněná data pro jiný jazyk se použijí z výchozího jazyku. A když už to budete dělat, tak tam prosím přidejte verzování změn.

Řešení pomocí Doctrine2:
Jelikož pro aplikaci platí že model != databáze je taková úprava velmi snadná.

  • Zavedou se rozhraní Translatable (např. metody getActualLocale(), setActualLocale() a getTranslatable­Fields()) a Versionable, které budou implementovat modely nebo jejich rodičovská třída.
  • Vytvoříme 2 třídy navíc, které nám vygenerují schémata (objekt -> tabulky) pro zvolený typ ukládání jazykových mutací a ukládání informací o verzích (způsob ukládání do tabulke je libovolný).
  • Celou logiku pro každý model budou vykonávat jen další 2 nové třídy – listenery s metodami onFlush(), postLoad(), …
  • Nastavení aktuálního jazyku, výhozího jazyku a registraci listenerů zařídíme změnou konfigurace Dependency Injection Containeru (např. ze Symfony2).

Nyní již máme vícejazyčný a verzovací web hotov – 4 nové třídy, změna třídy EntityRepository, ze které dědí všechny repository modelů a úprava konfigurace. Téměř žádný zásah do kódu samotné aplikace (závisí na objektovém návrhu, architektuře původní aplikace apod. – nesmí to být bastl). A možná spousta automaticky vygenerovaných tříd, které mi vytváří generátor kódu (např. pokud zvolím, že každá entita bude mít svou vlastní extra „tabulku“ pro překlady nebo verze).

notnotorm

Popis řešení v Doctrine2 jsem již načrtl. Nezlobte se na mě, ale vážně nemám čas Vás učit pracovat s Doctrine2. Jasně jsem napsal, že aplikace musí být navržena způsobem, že model != tabulka (jde o pohled na problém). Ukázková aplikace, na kterou vede odkaz, představuje spíš sadu skriptů pro práci s tabulkami a ne aplikaci s dobře definovaným modelem. Je politování hodné, že jsou neustále srovnávány dva nástroje pro práci s daty pouze z jedné strany a druhá strana je opomíjena (možná se Vám ji nedaří vidět). A tímto přístupem je veden celý seriál srovnávající NotORM a Doctrine2. Je potřeba si vážně uvědomit, že první písmeno ve zkratce ORM znamená Object. To znamená, že chci na straně aplikace pracovat s objekty, využívat veškeré výhody OOP a Doctrine2 mi má nějak zajistit namapování těchto objektů na databázi (ať už RDBMS nebo OODBMS).

Pro lepší pochopení načrtnutého řešení dodávám pár postřehů:

Jelikož je k dispozici dostupné řešení automatického překladu entit https://github.com/l3pp4rd/DoctrineExtensions, nemá myslím cenu psát znovu ukázkovou aplikaci.

notnotorm

Vždyť jsem psal, že už to někdo napsal. A řeší to úplně podobným způsobem http://www.gediminasm.org/article/translatable-behavior-extension-for-doctrine-2.

Zavádí rozhraní Translatable https://github.com/l3pp4rd/DoctrineExtensions/blob/master/lib/Gedmo/Translatable/Translatable.php a pro veškerou logiku používá jeden listener (z důvodu abstrakce pro použití jak RDBMS tak OODBMS je rozdělen na abstraktní třídu https://github.com/l3pp4rd/DoctrineExtensions/blob/master/lib/Gedmo/Translatable/AbstractTranslationListener.php a ORM nebo ODM třídu).

Už teď jsem zvědaví na třetí díl. Je zajímavé sledovat, jak ikona české scény PHP se snaží neustále všechny přesvědčit, jak je ORM téměř nanic (nebo aspoň, že Doctrine2 to dělá blbě). Mě to vážně připadá jako kdybych řekl, že na Hello World je nejlepší procedurální programování a tím pádem je vždy lepší než objektově orientované programování. A ještě zajímavější je sledovat, jak se nechají takhle zmanipulovat ostatní PHP programátoři http://php.vrana.cz/doctrine-lead-developer-explains-my-wtfs-part-2.php#d-11371.

František Kučera

Ad „Mám z toho taký pocit, že v Doctrine sa DQL používa všade tam, kde to ORM efektívne nedokáže.“

Pozor: tyhle *QL (jako DQL) jazyky jsou součástí ORM. ORM neznamená, že místo psaní dotazů budu jen volat nějaké metody – to je jen jedna z možností – ta druhá je psaní dotazů – pořád je to ORM, ale ten jazyk není SQL, ale je to trochu jiný dotazovací jazyk, který pracuje na úrovni objektů.

František Kučera

Ad $article->category = $values["cate­gory_id"]

Je něco překvapujícího na tom, že do proměnné, která má obsahovat objekt (kategorii), nemůžu přiřadit primitivní datový typ (číselné ID kategorie)? Mě na tom tedy nic nepřekvapuje. (ale možná jsem jen „zmlsaný“ staticky typovanými jazyky)

Ad „Pokus o přiřazení neplatné entity nezpůsobí žádnou chybu. Místo toho se prostě přiřadí hodnota NULL.“

Jako kategorii jsme nastavili null, tak se NULL uloží do databáze. V modelu asi není NOT NULL, tak se prostě uloží. Je na tom něco divného? Metoda find() vrací pro neexistující entity null, což je celkem standardní chování, podle mého nic nečekaného.

Ad „Vynulování počtu návštěv u všech článků si vyžádá tolik dotazů, kolik je článků.“

Když iteruji přes všechny články a každý samostatně ukládám, tak se logicky provede tolik UPDATů, kolik je článků. Framework prostě dělá to, co mu programátor řekne – podle mého opět nic překvapivého nebo záludného.

Disclaimer: autor názoru má rád SQL a rozhodně se ho neštítí. Má celkem rád i ORM*, ale respektuje, že ne na všechny úlohy je vhodné (což lze vyřešit použitím nativního SQL pro vybrané části aplikace).

*) obecně – Doctrine 2 nezná a nepoužívá

designerrr

Jen tak pro info ty proxy třídy tam nejsou pro nic za nic, ale kvuli lazy loadingu. Vygeneruje se třída, která obsahuje gettery pro všechny attributy a v případě přístupu přes getter se záznam nahraje až když je opravdu potřeba. Proto taky není moc chytré používat ty public attributy, při přístupu k nim se totiž těžko něco ověří.

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.