YetORM – kvaziORM nad Nette\Database

uestla
Backer | 799
+
+3
-

Ahoj.

YetORM

Spojuje mapování na objekty a efektivitu dotazů Nette\Database.

Skládá se ze 3 tříd:

  • Repository – stará se o svoje entity – načítá, vrací instance, ukládá…
  • Entity – kompozicí dostává instanci ActiveRow. Její API je čistě na nás.
  • EntityCollection – lazy kolekce entit na styl Selection, tedy až při foreach nebo na zavolání toArray() načte data a namapuje na entity. Do té doby můžeme kolekci řadit a limitovat, aniž by se provedl jediný dotaz.

Uváživše hořejší a vybavivše si ukázkový příklad z dokumentace, přepíši jej následujícně:

$books = new BookRepository($context);

foreach ($books->findAll() as $book) { // $book instanceof Book
	$author = $book->getAuthor(); // instanceof Author
	echo $book->getTitle(), " (", $author->getName(), ")";

	foreach ($book->getTags() as $tag) { // $tag instanceof Tag
		echo $tag->getName(), ", ";
	}
}

Dotazy se položí naprosto ve stejném provedení jako při nativním volání Nette\Database.

Rozhraní BookRepository, entity Book, Author a Tag vč. jejich metod, je to, co si člověk musí vyrobit sám. K dispozici je ale API pro zvládnutí všech relací 1:N i M:N.

Např. Book vypadá zhruba takto:

class Book extends YetORM\Entity
{

	/** @return string */
	function getTitle()
	{
		return $this->record->title;
	}


	/** @return Author */
	function getAuthor()
	{
		return new Author($this->record->author);
	}


	/** @return YetORM\EntityCollection */
	function getTags()
	{
		$selection = $this->record->related('book_tag');
		return new YetORM\EntityCollection($selection, 'Tag', 'tag');
	}

}

Sources

GitHub – zdrojáky vč. testů, ze kterých to snad půjde lépe vyčíst.

Díky předem za nenávistné komentáře.

Editoval uestla (25. 5. 2014 15:15)

Šaman
Člen | 2661
+
0
-

Podporuje to i pěkný zápis do entity? Něco jako:

<?php
$book->addTag('foo');
$book->removeTag('bar');
?>

Editoval Šaman (10. 2. 2013 16:51)

uestla
Backer | 799
+
0
-

@Šaman: Viděl bych to asi na vlastní evidenci přidaných/odebraných tagů, podle toho upravenou metodu getTags(), a zohlednění persistence v BookRepository::edit(Book $book, $values). V tomhle je to takové dosti na nás, moc toho za nás automaticky nenamapuje.

llook
Člen | 407
+
0
-

Líbí se mi, jak je to jednoduché. Používám totiž ORM, které toho umí asi 10× víc, ale někdy je docela hukot se v tom vyznat a beztak většinou využijete jenom ten základ.

Ale to nezní moc nenávistně… takže mi tady schází hlavně tyto věci:

  • Magie, aby člověk nemusel pořád copypastovat ty pořád stejné [gs]ettery. Co třeba @property, @property-read apod.?
  • High-end CRUD operace, které by dělaly v podstatě totéž, co ty low-end, co tam jsou, ale proháněly by ta data přes entity, takže by na nich fungovala validace, custom setters atp.
  • Taky si nejsem jistý tou metodou rollback(). Přidal bych kontrolu, jestli $transactionCounter === 1, jinak exception.
redhead
Člen | 1313
+
0
-

Taky se mi to moc líbí. Díky!

Zrovna jsem to nasadil na nově zrefaktorovanej model a fakt hezky se to používá, na to jak je to jednoduché.

Ještě by se mi líbil nějaký lepší způsob ukládání, abych nepředával entitu a k ní hodnoty. Snad by mělo fungovat následující:

$book = $repository->getById(5);
$book->setTitle('Mistrovství v PHP5'); 	// $this->row->title = $title;
$repository->update($book);		// $book->getActiveRecord()->update();

Nebo mi něco ušlo?

Editoval redhead (10. 2. 2013 22:34)

Tharos
Člen | 1030
+
0
-

Moc hezký výtvor, líbí se mi „tenkost řešení“. Příkladný poměr počtu řádků a vytvořené hodnoty :).

uestla
Backer | 799
+
0
-

@redhead: Ty metody pro ukládání vůbec nejsou třeba, záleží na tobě, jak si to v Repository uděláš – já si to tam dal defaultně abych mohl např. použít rovnou data z formuláře. Čili bez problému vytvoříš (a tipuju, že sis i vytvořil) funkčnost, kterou ukazuješ.

uestla
Backer | 799
+
0
-

Na llookův popud jsem (zatím v boční branchi) přidal podporu pro anotace nahrazující primitivní gettery a settery.

Podporované jsou @property pro getter i setter a @property-read pro getter. Anotace je třeba zapsat kompletně, tj. i s typem, na který se hodnota přetypuje. Anotací lze využít jen pro primitivní datové typy.

Následující definice entity jsou ekvivalentní:

EDIT:

Místo předchozího přetypovávání se nyní striktně kontroluje typ, jinak se vyhodí výjimka (podle toho jsem i upravil kód níže).

  1. ruční gettery/settery
class Book extends YetORM\Entity
{
	function getId()
	{
		return (int) $this->row->id;
	}


	function getTitle()
	{
		return (string) $this->row->title;
	}


	function setTitle($title)
	{
		if (!is_string($title)) {
			throw new Nette\InvalidArgumentException("Invalid type - 'string' expected, '" . gettype($title) . "' given.");
		}

		$this->row->title = $title;
		return $this;
	}
}
  1. využití anotací
/**
 * @property-read int $id
 * @property string $title
 */
class Book extends YetORM\Entity
{}

Interně se tedy properties nastavují instanci ActiveRow spjaté s instancí entity. Pokud je zároveň definována anotace spolu s ručním [gs]etterem, přednost má ruční definice. Stejně tak je upřednostňováno výchozí Nette\Object properties chování. Při volání [gs]et<Property>() se převádí z CamelCase na podtržítkové jméno.

Příklady:

$book->setTitle('Book title'); // ekvivalentní s $book->title = 'Book title';
$title = $book->getTitle(); // ekvivalentní s $book->title
$book->setId(123); // zhavaruje, $id je read-only

Editoval uestla (1. 4. 2013 17:51)

Šaman
Člen | 2661
+
0
-

Moc pěkný, asi si to rovnou vyzkouším.

castamir
Člen | 629
+
0
-

Bude to schopné si poradit i s výsledkem přes query (tedy Statement místo Selection)?

uestla
Backer | 799
+
0
-

@castamir: Popravdě nevím, k čemu by to bylo dobré. Když má entita závislost ve více tabulkách, mělo by být možné si pro tuto závislost sahnout čistě přes API ActiveRow.

Jinak jsem properties nakonec mergnul do masteru + přidal možnost zapsat pomocí anotace entitní třídu a tabulku (obojí se doteď bralo buď z definované proměnné třídy, případně z názvu *Repository, pokud tato neexistovala).

castamir
Člen | 629
+
0
-

@uestla například, když potřebuješ vytvořit dotaz s joinem do stejné tabulky. NDB neumí pracovat s aliasy…

Šaman
Člen | 2661
+
0
-

Jen poznámka k těm anotacím. Ačkoliv v PHPDoc anotacích používám jako typ proměnné název třídy, tak u definování property je nutné použít slovo 'object'.

Při zpracování těch properties se totiž typ prožene přes funkci 'settype', který název třídy neumí zpracovat, slovo object ano.


A díky za tento miniORM, je to tak malý, že chápu zdrojáky a přitom to umí generovat objekty. Mám v tom teď zpracovaný ukázkový příklad se všemi druhy vazeb a poprvé od opuštění Dibi mě databáze (moc) neděsí :)
..ooOO(I když s tím souvisí i dvě nocovky nad čistou Nette\Database)


Edit: Nechceš ho dát do doplňků? I jako beta verzi? Jinak časem odroluje v diskuzi a kdo si na něj nevzpomene, ten už ho nenajde.

Editoval Šaman (5. 3. 2013 7:10)

uestla
Backer | 799
+
0
-

Díky, Šamane. Na doplňky to je možná ještě brzy,
teď mně ještě chybí nějaká možnost kešování – entity
nejde serializovat :-(

Nicméně jestli máš zpracovaný příklad a nebudeš proti,
můžeš mi ho poslat jako pullík na github coby README ;-)

Tharos
Člen | 1030
+
0
-

Ahoj,

tak jsem si s YetORM dneska hrál „naostro“ a pracovalo se mi s ním opravdu výborně. Využít kompozice a takto zapouzdřit ActiveRow považuji za skvělý nápad. Přesně takhle si představuji ORM vrstvu – tenká, ale principiálně nijak neomezená.

Protože jsi mi tímto dílem ušetřil nemálo času :), rád bych také věnoval nějaký svůj čas pro něj. Chci se Tě zeptat, co bys nejvíce ocenil.

Napadla mě třeba nějaká ukázková aplikace včetně doprovodného článku… Model by v ní byl kompletně zapouzdřený pomocí servisní vrstvy s YetORM pod pokličkou – vůbec by to mohla být hezká ukázka určitého stylu, jakým lze v Nette vyvíjet. Co si o tom myslíš? Zadání bych si vymyslel takové, aby v něm existovala „netriviální doménová logika“ (taková, kterou nelze pokrýt klíči v databázi…).

A pak jsem se Tě chtěl zeptat, jakým směrem plánuješ vývoj směřovat a jestli bych taky třeba časem mohl poprosil o Composer balíček. :) Anebo to zkusit protlačit přímo do Nette (jmenný prostor Nette\Database\ORM by těm třem třídám určitě slušel).

Zkoumal jsem více modelů nad Nette\Database a nic mě neoslovilo, dokud jsi nenapsal tohle. Ještě jednou díky :).

uestla
Backer | 799
+
0
-

To mě těší. Komunitnímu vývoji se absolutně nebráním, čili pokud
chce kdokoli čímkoli přispět, jsem jedině pro.

Co se composeru týče, s tím pořád nejsem kamarád, používá-li
někdo YetORM a kamarádí se s composerem, budu rád za pullík.

A k budoucímu vývoji: právě se snažím rozumně rozšířit properties,
aby šlo deklarovat i typ coby třídu (čili rozšířit současný stav,
kdy jsou podporovány pouze primitivní datové typy).

Pokud mně, nebo někomu jinému, bude při používání chybět nějaká
funkcionalita, klidně to můžeme prodiskutovat a přidat, není problém.
Zatím jsem to použil asi 3krát, a celkem mi to vyhovuje.

Těší mě, že to někomu pomůže, nicméně zdržel bych se zatím
názorů o zařazení přímo do Nette, na to je to ještě v moc raném
stádiu a připouštím, že to má i nějaké mouchy (viz výše).

Díky!

thunderbuff
Člen | 164
+
0
-

Tak jsem YetORM zkusil. Musím říct, že je to opravdu krásná práce! Jak říká Šaman, je to tak minimalistické, že zdrojáky jdou lehce pochopit a přitom to krásně funguje. Palec nahoru :-)

Ivorius
Nette Blogger | 119
+
0
-

@Tharos To by bylo super, a kdybys do toho zakomponoval ještě multijazyčnost, klidně by to mohlo být pokračovatelem po quick startu.

uestla
Backer | 799
+
0
-

Na Šamanův popud jsem v properties branchi experimentálně přidal
podporu pro třídní property typy. Nicméně využití to najde nejspíš jen u datumu,
protože jiné instance (nepočítám-li ActiveRow) nativní přiřazování u ActiveRow
pokud vím nezvládá, ale budiž…

Upravil jsem podle toho testy.

Editoval uestla (31. 3. 2013 16:29)

mkoubik
Člen | 728
+
0
-

uestla napsal(a):

Co se composeru týče, s tím pořád nejsem kamarád, používá-li
někdo YetORM a kamarádí se s composerem, budu rád za pullík.

Pullík, přidej si tam akorát "license": "..." a zaregistruj si to na https://packagist.org/.

A ještě by to asi chtělo upravit testy, aby používaly Nette a PHPUnit z composeru.

Editoval mkoubik (31. 3. 2013 14:46)

Tharos
Člen | 1030
+
0
-

Tak jsem si taky dovolil poslat jeden pull request.

Provedl jsem drobnou optimalizaci: doteď se u entit při každém přístupu k položce přes magické metody __get nebo __set vytvářela nová reflexe, což je v PHP relativně drahé. Nově se jednou vytvořené reflexe uchovávají v paměti.

Editoval Tharos (31. 3. 2013 21:25)

Tharos
Člen | 1030
+
0
-

Ahoj,

narazil jsem na jednu věc, která mi aktuálně vcelku dost chybí. Než ale pošlu pull request, chtěl bych to probrat ještě tady…

Mějme propertu Entity zapsanou přes anotaci, která může nabývat hodnoty null. V anotaci by se to pak standardně zapsalo jako string|null, null|string, string|NULL, nebo NULL|string. S tím si teď ale ORM neporadí.

Nejjednodušším možným řešením je prostě kontrolovat, jak typ začíná nebo končí, a podle toho null hodnoty povolit. Problém je, že si to ale neporadí třeba se zápisy string|null|bool. Otázka je, zda takové zápisy mají vůbec smysl? Byť jsou syntakticky správné. Problém by s nimi byl samozřejmě i u automatického __set, kde by ORM nevědělo, na který typ vlastně přetypovávat.

To nejjednodušší řešení mám naimplementované a mohu poslat. Pokud bys ale chtěl něco sofistikovanějšího, dej vědět.

Editoval Tharos (2. 4. 2013 10:13)

uestla
Backer | 799
+
0
-

Dobrý postřeh, databáze NULL vracet může, to je pravda.

Smysl mi ale dává pouze <type>|NULL, všude jinde bych striktně trval na typu jediném – nejen z důvodu, že by knihovna nevěděla, na co přetypovávat, ale hlavně kvůli udržení aspoň nějaké konzistence. Nevidím důvod, proč by jedna properta měla nabývat dvou různých neNULLových typů.

Tharos
Člen | 1030
+
0
-

Vidím to úplně stejně. Mám teda připravit pull?

Spolu s tím bych rád vyzkoušel i dekomponování parsování těch property anotací do samostatné třídy (např. YetORM\EntityReflection extends Nette\Reflection\ClassType). Mělo by to několik výhod: parsování anotací by proběhlo zase jenom jednou a ne při každém přístupu k __get/__set, šlo by pak odstranit ty (IMHO ošklivé) výstupní parametry (&) a spolu s tím by šlo hezky vyřešit i problém s null.

No, já to u sebe takhle připravím a pak uvidíš, jestli to bude kompatibilní s Tvými představami. :)

Editoval Tharos (2. 4. 2013 13:29)

uestla
Backer | 799
+
0
-

Super :-)
  Díky.

Tharos
Člen | 1030
+
0
-

Tak jsem si s tím pohrál a výsledek můžeš vidět v téhle mojí brenši. Jelikož to byl poměrně hluboký zásah, pull request zatím neposílám. :)

Tak nějak by to mělo řešit vše, o čem jsem psal ve svém předchozím postu…

No ale mám v tom i jeden BC break. Vypustil jsem to automatické přejmenovávání názvu fieldNamefield_name, protože já v databázi sloupce pojmenovávám lower-CamelCasem (například bookTitle) a přes tu Tvou konverzi u mě nejel vlak. :) Podtržítko používám pouze u sloupců, které nesou cizí klíč (například author_id).

Nabízím, že tam ten převod doplním jako nějaký adaptér. Zkrátka si myslím, že podobná konverze by měla být spíš snadno doplnitelná a ne zadrátovaná v core kódu. Já jsem si kvůli tomu pro své bookTitle sloupce musel psát get/set metody…

Dej vědět, co si těch mých úpravách myslíš a pokud by Ti to vyhovovalo, pošlu pull request.

Editoval Tharos (2. 4. 2013 23:28)

uestla
Backer | 799
+
0
-

Ahoj, předně chci poděkovat – koukal jsem na brenš a vypadá moc pěkně. V souvislosti s vyčleněním reflexe bych možná zvážil ještě začlenit podporu pro třídní typy propert, co mám teď v branchi properties.

Ohledně adaptéru – přemýšlel jsem nad tím, ale nedošel jsem k použitelnému řešení. Takový adaptér by byl spjat s entitou, která se ovšem vytváří ve všech 3 vrstvách (entita, kolekce, repozitář). Otázka je, na které vrstvě tenhle adaptér nastavovat. Globálně nastavit pro danou entitu mi přijde nešťastné.

Tharos
Člen | 1030
+
0
-

Nemáš zač, pořád se cítím dlužníkem. :)

Ad adaptér) Napadlo mě ještě jedno řešení, úplně snadné:

/**
 * @property string|null $bookTitle %book_title
 */
class Book extends YetORM\Entity
{
}

Prostě by se vymyslela syntaxe pro zápis názvu sloupce tabulky (pokud se liší od názvu property) přímo v anotaci. Co si o tom myslíš? Implementačně triviální a snad vše řešící.

Ad třidní properties) To by mělo být to nejmenší.

Refaktoruji do YetORMu jednu menší aplikaci, která má ale poměrně košatý model. Beru to jako takový křest ohněm. :) Zatím to jde hladce a postupně mi krystalizují na povrch všemožné drobnosti, které za běhu v ORM ladím. Mimochodem povedlo se mi vcelku snadno namapovat jednu entitu rozlezlou do více tabulek – do jedné „hlavní“ a jedné „doplňkové“, a to ještě tak speciálně, že pro některé entity záznam v té doplňkové tabulce vůbec nemusí být. Tohle se mi zatím s žádným jiným ORMkem tak elegantně namapovat nepodařilo. :)

Až si dohraji, udělal bych z toho nějaký závěr a připravil bych pull request. Kód udržuji minimální, za sebe bych byl opravdu nerad, aby ORM nějak kynulo. Sepíšu, co a proč jsem upravil a pak nechám na Tobě, jestli to začleníš.

uestla
Backer | 799
+
0
-

To je suprovej nápad! Protože property zápis vlastně supluje vlastní [gs]ettery, ve kterých si to člověk napíše ručně, očekávalo by se, že se podobná věc bude řešit přímo v zápisu propert. A já vůl už bych to právě hnal kynoucí cestou.

Osobně bych taky byl raději za udržení minimaličnosti…

Pokud to github umí, forknu si tvůj fork a pošlu ti pull requesty s drobnostmi, na které jsem narazil, a pak bychom to mohli zařadit do masteru. Nezapomeň se ale prosím uvádět jako autor u toho, co napíšeš, ať je v tom pořádek.

Díky!

Tharos
Člen | 1030
+
0
-

Ahoj,

tak jsem k sobě pushnull ty mé nedávné úpravy. Teď jsou ve větvi dev, tu EntityReflection jsem už u sebe začlenil do masteru. Stručně k mým změnám a k tomu, co mě k nim vedlo:

Drobné rozšíření EntityReflection
Nově umí jednorázově zmapovat settery a gettery. Logicky to do této třídy patří a dá se toho využívat v Entity::toArray (viz dále) a také v Entity::checkValidity (viz dále).

Přidána metoda Entity::checkValidity
Metoda, která se z entity pokusí postupně načíst všechny položky a u těch, které nejsou read-only, je na ty hodnoty opět nastaví (stav se tedy nijak nezmění). Pěknej nesmysl, že jo. :) Ale jen na první pohled. Díky této akci protečou zapouzdřená data skrze všechny gettery a settery a vyhodí se výjimka, pokud je někde nějaká nekonzistence. Využívám toho hlavně při vytváření záznamů.

Upravena metoda toArray()
Nebavilo mě ji pro každou entitu s vazbami upravovat. Nyní si poradí i s navázanými entitami a kolekcemi entit – jednu úroveň vypíše kompletní, další úrovně jen informativně. Hezky je to vidět v testech. To, že se hlubší úrovně vypisují jen informativně, řeší problém s kruhovými závislostmi při konverzi.

Přejmenována metoda toActiveRow
Tohle je taková otázka. Úspěšně mapuji v jednom případě dvě tabulky (1:1) do jedné entity a když taková entita podědí metodu toActiveRow, je to divné, protože ona v sobě zapouzdřuje dvě instance ActiveRow. Přemýšlel jsem, na co by to šlo přejmenovat a vyzkoušel jsem decapsulate. :) Název vychází z toho, že entita zapouzdřuje (encapsulates) nějakou vnitří reprezentaci (typicky jeden ActiveRow) a pro pár speciálních účelů (persistence, výmaz) je výhodné tu vnitřní reprezentaci dostat ven. Decapsulate mi přišel vhodný termín :). Ale tohle je určitě k diskuzi, vím, že to není obvyklé sloveso.


Tak si to projdi… Snažil jsem se dodržet coding style a testy samozřejmě procházejí (samozřejmě jsem je vhodně upravil, hlavně aby reflektovali úpravy v toActiveRow). Kdyby se Ti to celé líbilo, klidně můžu poslat pull request. :)

Jinak ve volných chvílích bych tedy ještě doimplementoval tu možnost definice názvu sloupce u property a také podporu pro „hintování“ ostatních entit. Pak bych taky dopsal testy pro té mé nové funkce.

Pak by to bylo z mého pohledu úplně dokonalé :).

Editoval Tharos (5. 4. 2013 22:48)

enumag
Člen | 2118
+
0
-

@uestla, @Tharos: V posledních verzích 2.1-dev je ActiveRow::__set() deprecated, nebude to vyžadovat určité změny?

Tharos
Člen | 1030
+
0
-

Hmm, tak to je hezký… Pokud se přejde ze __set na update(), elegantní persistence půjde do kytek. Při zachování současného stylu by se pak musela ukládat každá změna samostatně… takže pytel dotazů kvůli jedné entitě.

Takže pevně doufám, že se ta změna nakonec z 2.1-dev vypustí…

Edit: Prošel jsem si bleskově debatu na GitHubu a asi nevidím úplně do detailů Nette\Database… Dá se v pár větách shrnout, co je na těch metodách špatného a proč by měly být deprecated?

Editoval Tharos (6. 4. 2013 0:00)

hrach
Člen | 1838
+
0
-

Dá se v pár větách shrnout, co je na těch metodách špatného a proč by měly být deprecated?

Vytvaří to dojem ORM, respektive Entity, ale bohužel to nikdy entita nebyla a nebude. Tuto funkcionalitu je lepsi implementovat bokem. Vyhnes se tim spouste problemu.

uestla
Backer | 799
+
0
-

Stavíme-li na něčem, co je v „divokém“ vývoji, asi bychom s tím měli počítat… Update se volá z Repository, čili bude to sice psaní navíc (možná nějaký podobný mechanismus jako býval v ActiveRow::__set() půjde převzít), ale fatální by to být nemělo… Ať už bude entita spojovat jednu nebo více tabulek…

Tharos
Člen | 1030
+
0
-

No, po pár minutách úvah mi také přijde, že tragické to nebude… Nejsnazší a vyhovující by mohlo být prosté zabalení ještě i samotného ActiveRowu do tenké obálky, která by pak na něj většinu volání přímo delegovala (všechny read operace) a sama by se zapojovala jenom do řešení těch write. Edit: Nejspíš to půjde i jednodušeji.

@hrach: Já chápu, co tím myslíš. Nicméně musíš uznat, že naše využití bylo praktické. :) Prostě u podobných věcí je třeba vědět, co se dělá, a pak se jich dá výhodně využít. Nový přístup bude určitě blbuvzdornější, ale prostě nějakou režii s sebou ponese.

V každém případě díky za odezvu.

Editoval Tharos (6. 4. 2013 7:25)

enumag
Člen | 2118
+
0
-

@Tharos

Přejmenována metoda toActiveRow
Tohle je taková otázka. Úspěšně mapuji v jednom případě dvě tabulky (1:1) do jedné entity a když taková entita podědí metodu toActiveRow, je to divné, protože ona v sobě zapouzdřuje dvě instance ActiveRow. Přemýšlel jsem, na co by to šlo přejmenovat a vyzkoušel jsem decapsulate. :) Název vychází z toho, že entita zapouzdřuje (encapsulates) nějakou vnitří reprezentaci (typicky jeden ActiveRow) a pro pár speciálních účelů (persistence, výmaz) je výhodné tu vnitřní reprezentaci dostat ven. Decapsulate mi přišel vhodný termín :). Ale tohle je určitě k diskuzi, vím, že to není obvyklé sloveso.

S tímhle mám trochu problém – jestli to dobře chápu tak v tvém případě metoda decapsulate u té entity co zapouzdřuje dva ActiveRow vrací něco jiného, což je špatně. Metoda by měla vždy vracet stejný typ. Samozřejmě pokud vrací tvou vlastní implementaci Nette\Database\Table\IRow, je vše v pořádku.

Tharos
Člen | 1030
+
0
-

To je prozíravá rada. Minimálně já tohle budu ve svém forku ještě upravovat…

Jak s tím pak naloží uestla a jaké řešení posvětí je na něm.

Editoval Tharos (7. 4. 2013 2:14)

uestla
Backer | 799
+
0
-

Zatím se spíše inspiruji. Snažím se nedělat zbytečně moc razantní změny…

Ohledně Reflection jsem šel podobnou cestou – vznikla branch reflection, ve které přibyl namespace Reflection sdružující logiku propert (na to, že jde o alternativní funkcionalitu týkající se zápisu anotací, to vyhřezlo celkem pěkně :-) ).

Přidal jsem tam rovnou i podporu zápisu jména sloupce, funguje takto:

/** @property string $bookTitle -> book_title */
class Book extends YetORM\Entity
{}

Díky tomuto zápisu pak mohu používat následovně:

$book->bookTitle = 'title'; // odpovídá $book->row->book_title = 'title'

Do téhle větve bych přidal podporu pro třídní typy z properties větve. No a až přijde správná chvíle, tak to hodím do masteru.

castamir
Člen | 629
+
0
-

Již delší dobu si dělám vlastní ORM nad dibi (nechtěl jsem znásilňovat nette/database + předělávat značnou část logiky díky query dotazům, které vrací Row a ne ActiveRow). Zpracování properties mám v konstruktoru resp metody volané v kostruktoru. Pro každou třídu entit se provede právě jedno namapování. Logika properties je oddělená ve vlastní třídě Property, zatímco v Entity odpadne spousta zbytečného kódu. Při nastavení hodnoty testuju existenci podle aliasu (bookTitle pro book_title) a pak přímým přístupem získám property, na kterým jen zavolám validate. Ještě mi tam toho spousta chybí (mapování všech databázových typů na hodnoty a třeba správná práce s výčtovým, setovým či datovým typem, ale princip je snad dobře viditelný.

hrach
Člen | 1838
+
0
-

Sorry za offtopic: Pro dibi orm pouzij http://orm.petrprochazka.com/ a neblazni.

castamir
Člen | 629
+
0
-

<offtopic>
@hrach: moc robustní, nepřehledné a bez dokumentace. Jo a ještě nedokončené :D. Ale díky za info.
</offtopic>

Editoval castamir (7. 4. 2013 11:34)

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

<offtopic>
@castamir Musím se zde PetrOrmu zastat, jde o výborně navržený nástroj, který je především flexibilní (!), a zároveň se s ním dá de facto rychle prototypovat.

Dokumentace bohužel neexistuje v ucelené formě, ale velké množství detailně popsaných informací přímo od autora lze najít na fóru.

Ad nedokončené: za to je profesionálně vyvíjené s extrémním důrazem na zpětnou kompatibilitu.
</offtopic>

Editoval vojtech.dobes (7. 4. 2013 12:05)

castamir
Člen | 629
+
0
-

pokračování na orm foru

uestla
Backer | 799
+
0
-

Právě jsem mergnul reflection do masteru – což způsobilo BC break u defaultní propertyNamecolumn_name konverze – tu je případně třeba dělat ručně (příklad výše).

Co se nekompatibility s Nette-2.1-dev ActiveRow::update() týče, zatím úpravy zařazovat neplánuji, mám rozpracovaný pokus, ale vzhledem k větším odlišnostem (SelectionFactory, apod.) a možnosti dalších změn ještě s jakoukoli finalizací počkám…

Souhrn změn oproti předchozí „verzi“ masteru:

Velké díky Tharosovi za přispění!

Editoval uestla (9. 4. 2013 14:10)

Tharos
Člen | 1030
+
0
-

Měl bych k tomu ještě drobnou prosbu. Nechtěl bys to prohlásit za nějakou verzi a otagovat, aby se v composeru nemuselo odkazovat na dev-master? Předem díky. A díky i za vkusnou integraci představených myšlenek. :)

uestla
Backer | 799
+
0
-

Otagováno coby verze 0.9.1.

Jack06
Člen | 168
+
0
-

Taková technická.
Zkusil jsem si to implementovat v podobě example a přidal jsem si namespace (modulová aplikace).

Ve chvíli, kdy jsem to vše dal pod společný namespace, tak mi to vyhazuje chybu u metoda toArray typ tabulky M:N. Nešlo by implementovt aby si to přebralo namespace? Nebo to jedině psát ručně do vztahu?

	/* struktura stejná jako tagy */
$return['sections'] = array();
foreach ($this->getSections() as $section) {
    $return['sections'][] = $section->getName();
}

Vyhozená chyba pak je: Class ‚Section‘ not found
Přičemž existuje, ale je v nějakém namespace, stejném jako ‚entita‘, ze které se to pokousím volat.

Section.php – http://pastebin.com/kXNdEpUk
Article.php – http://pastebin.com/MxKRYKUd
Fatal error File: …\libs\YetORM\EntityCollection.php Line: 71 – http://pastebin.com/LkfvjwKJ

Editoval Jack06 (14. 4. 2013 12:50)

Jan Tvrdík
Nette guru | 2595
+
0
-

@Jack06: Zkus upravit getSections():

/** @return \YetORM\EntityCollection */
public function getSections()
{
	return $this->getMany('ArticleModule\Section', 'article_has_section', 'section');
}

Brát to relativně by vedlo k hromadě problémů. Možná tě potěší, že v PHP 5.5 už půjde napsat

/** @return \YetORM\EntityCollection */
public function getSections()
{
	return $this->getMany(Section::class, 'article_has_section', 'section');
}
Jack06
Člen | 168
+
0
-

Jan Tvrdík napsal(a):

@Jack06: Zkus upravit getSections():

/** @return \YetORM\EntityCollection */
public function getSections()
{
	return $this->getMany('ArticleModule\Section', 'article_has_section', 'section');
}

Brát to relativně by vedlo k hromadě problémů. Možná tě potěší, že v PHP 5.5 už půjde napsat

/** @return \YetORM\EntityCollection */
public function getSections()
{
	return $this->getMany(Section::class, 'article_has_section', 'section');
}

Super, to bude přesně to co chci :-)

Jack06
Člen | 168
+
0
-

Ještě jednu otázku bych měl, jak řešit (nějaká reflection??), Když mám entitu Article, ale tabulka v databázi se bude jemenovat jinak, třeba bude mít prefix?