Promeo – vas novy model do Nette

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

Promeo

O Promeu

Tazko sa mi hlada presne pomenovanie, pre to co Promeo v skutocnosti je. Nie je ORM vo
svojej podstate, nie je to DB layer, je to skor inteligentny model s plno praktickymi funkciami, pre
pristup k udajom v DB.

Vznikol ako myslienka prepojenia urcitych principov (nie kniznice samotnej!) NotORM od Jakuba Vrany, vyuziva silu dibi od Davida Grudla a toto
cele obohacuje o mnoho praktickych funkcii, ktore potrebujeme dennodenne.

Jeho vyuzitie sa najde v malych az strednych projektoch alebo tiez ako rychly prototyping v akomkolvek projektu, ked sa ako programator
nechcete starat o vytvaranie modelov a entit a potrebujete rychlo nieco efektivne a inteligentne.

Samozrejme nezabudlo sa ani na ludi, ktori su zvyknuty pisat triedy popisujuce jednotlive entity a iny sposob neuznavaju.
Prave pre nich je urceny rychly prototyping, kde v kode mozu zacat okamzite pouzivat nazvy danych tried a samotne triedy
definovat az budu mat chut alebo cas.

Sucastny stav

Promeo ako take som planoval vydat koncom tohto alebo zaciatkom buduceho roka. Medzitym ma ale pani David Grudl a Jakub Vrana (pricom minimalne jeden z nich vedel, ze sa chysta nieco na tento sposob :P)
predbehli a dohodli sa na spojeni sil z coho vzniklo Nette\Database. Z tohto dovodu som bol „donuteny“ vidat tento sneak preview toho co Promeo dokaze a zistit tak nazor komunity na to,
ci vydat nieco taketo samostatne alebo by bol zaujem o prepojenie a zakomponovanie do Nette – samotna kniznica bola odpociatku zamyslana najme ako modelovy doplnok do Nette a nie ako nieco plne samostatne ako je to u NotORM.

Samotna funkcionalita
Jednoduchy priklad na zaciatok

Ukazme si najskor uplne jednoduchy priklad vyuzitia Promea:

// v presenteri
public function renderUsers()
{
	$users = new Promeo(':tp:users');
	$this->template->users = $users->getAll();
}
//sablona
{if $users->isEmpty()}
<p>Neexistuju ziadny uzivatelia</p>
{else}
<p>pocet uzivatelov: {= count($users)}</p>
<table>
{foreach $users as $uid => $user}
	<tr>
		<td>
			{$user->name} // objektovy pristup
		</td>
		<td>
			{$user['email']} // array pristup
		</td>
	</tr>
</foreach>
</table>
{/if}

Pokial chceme udaje o konkretnom uzivatelovi staci zavolat get($id) namiesto getAll().

$this->template->user = $users->get(3); // vytiahne udaje o uzivatelovi s ID 3

V kode mozete vidiet zapis tabulky ako :tp:users, ty z vas ktori poznaju dibi tusia spravne :tp: je v tomto pripade nadefinovany prefix
v substitutions definiciach dibi ;).

PromeoEntity

Zakladna implementacia IPromeoEntity. Slovo zakladna mozno vyznieva lacine, u PromeoEntity je vsak pravy opak
pravdou a v skutocnosti v mnoho pripadoch nebudete potrebovat nic viac.

Umoznuje ArrayAccess ako aj ObjectAccess, pre pristup k datam vyuziva konvencie pre pomenovanie (vid nizsie sekciu Konvencie), automaticky
pretypuvava hodnoty v premennych podla ich typu v DB – pricom sa vyuziva vlaste (mudrejsie) a nie nativne pretypovavanie.

Implementuje Countable a IteratorAggregate pre zistenie poctu premennych a iteraciu nad samotnou entitou. Medzi dalsie vlastnosti patri export do
jednoducheho pola podla 3 typov (cisty export, export pre insert, export pre update).

Posledny spominany export (pre update) vyuziva vlastnost PromeoEntity, ktora si dokaze zapametat, ktore vlastnosti boli zmenene a pri update
sa menia iba tieto a nie cela entita.

Urcite osladenie programatorskeho zivota prinasa tiez moznost volat metody ako isPremenna alebo hasPremenna kde za Premenna doplnte nazov
stlpcu z databazy. Hodnoty z tychto funkcii sa automaticky (inteligentne) pretypovavaju na bool.

O vztahu PromeoEntity ku primarnym klucom sa viac dozviete v sekcii „Primarne kluce“.

Vlastne entity

Napriek tomu, ze zakladna entitna trieda v Promeu: PromeoEntity je dost silna a postacujuca na bezne pouzivanie
moze sa niekomu hodit pouzivat vlastnu entitnu triedu. Nie je nic lahsie, jedinou podmienkou je implementovanie jednoducheho rozhrania
IPromeoEntity, ktore vyzaduje dve metody getPK a getPKName.

Cely kod potom vyzera nasledovne:

$users = new Promeo(':tp:users', 'MyEntity');

Ako som vsak uz v uvode spominal, Promeo sluzi aj ako nastroj rychleho prototypingu, kde minimalne mna pri novych projektoch odradzalo
stale dokola definovat podobne entity a potrebne metody, o to radsej by som sa hned pustil do vyvoja s tym, ze specifika entit dorobim v momente
ked budu naozaj realne potrebne. Promeo pamatuje aj na tuto vec a preto je mozne pisat nasledovne:

$users = new Promeo(':tp:users', 'MyUser'); // trieda MyUser nie je nikde definovana

V kode tak mozete hned odzaciatku pouzivat nazvy vasich entit a ich definiciu nechat az na neskor.

Primarne kluce

Samotna trieda PromeoEntity predpoklada, ze zaznamy v databaze maju primarny kluc ako stlpec s menom id.
Tento predpoklad sice pokryva velku cast pripadov, nie vzdy mame ako primarny kluc stlpec s danym nazvom alebo dokonca iba jeden stlpec.

Promeo pameta aj na to a prinasa celkom luxusny zapis na zmenu nazvu PK:

// single column PK
$users = new Promeo(':tp:users', '#uid');

// multi column PK
$relations = new Promeo(':tp:user_relations', '#uid_1,uid_2');

V oboch pripadoch sa pouzije ako entitna trieda trieda PromeoEntity zakazdym vsak s inym PK. Symbol # bol
prevzaty z CSS syntaxe, kde sa pomocou neho referuje k id atributu a mal by prispievat k prehladnosti a samovykladu celeho kodu.

Samozrejme, pokial pouzivate vlastnu entitu, staci v metode getPKName vratit pole obsahujuce nazvy stlpcov, ktore tvoria PK a mate vystarane :).

westrem
Člen | 398
+
0
-

CRUD operacie

Praca s datami je najcastejsi sposob akym programator pracuje s databazou a Promeo vzniklo za ucelom ulahcit a osladit
programatorovi zivot. Kazdy typ operacie preto disponuje aj masovou variantou, ktora umoznuje vytvarat, menit a mazat viacere zaznamy naraz.

Podme sa blizsie podivat na jednotlive ukony:

Pridavanie

Jednoduche

$users = new Promeo(':tp:users');

// vytvorime si entitu
$user  = $users->entity(
			'name' => 'John Doe',
			'email'=> 'john.doe@gmail.com',
		);
// pridame ju do databazy
$users->add($user);

V priklade mozeme vidiet pouzitu metodu entity(), jej ucel je jednoduchy, odprostit programatora od pouzivania konkretneho
nazvu triedy v kode a dosiahnut tak vyssi stupen abstrakcie. Metoda sa sama postara o vratenie korektne vytvorenej entity so
vsetkymi nalezitostami, ktore k tomu patria.

Za zmienku stoji tiez to, ze pokial je primarny kluc typu auto_increment, objavi sa novo vygenerovana hodnota priamo v entite.
V tomto pripade by sme preto mali nove ID uzivatela dostune cez $user->id.

Hromadne

$vouchers = new Promeo(':tp:vouchers');

// vytvorme si entity
for ($i = 0; $i < 5; $i++) {
	$voucher = $vouchers->entity(
		'check_code' => sha1(uniqid(mt_rand(), true)),
		'discount'   => mt_rand(0, 100),
	);
	// pridame entitu na zasobnik
	$vouchers[] = $voucher;
}
// pridanie entit do databazy
$vouchers->flushAdd();

Pri hromadnych ukonoch vzdy existuje viac sposobov ako ich zapisat, v priklade mozeme vidiet asi ten najintuitivnejsi a to je
$vouchers[] = $voucher. Medzi dalsie patri napriklad: $vouchers->add($voucher, FALSE), $vouchers['a'] = $voucher pripadne $vouchers['+'] = $voucher.
Kazdemu moze vyhovovat iny zapis, dobre je vsak zvolit si jeden a potom sa drzat toho.

Metoda flushAdd() vracia pole entit, ktore maju doplnene automaticky vygenerovane primarne kluce.

Zmena

Update entit funguje obdobne ako pridavanie.

Jednoducha

$users = new Promeo(':tp:users');
$user  = $users->get(3);
	$user->email = 'my.new.email@gmail.com';
$users->update($user);

Hromadna

$vouchers = new Promeo(':tp:vouchers');

// vytiahnime si iba poukazky s vysokou zlavou
$bigDiscounts = $vouchers->where('[discount] > 60')->getAll();

foreach ($bigDiscounts as $id => $voucher) {
	$voucher->discount -= 20; // znizime zlavu o 20%
	$vouchers['u'] = $voucher;
}

// hromadny update
$vouchers->flushUpdate();

Ako uz bolo nacrtnute vyssie, pri zmenach sa v query posielanej do databazy vyskytuju len stlpce, ktore sa naozaj zmenili, cim sa zvysuje vykon a
znizuje chybovost kodu. Samozrejme aj tu mame viacero moznosti zapisu. Metoda flushUpdate vracia pocet uspesne vykonanych zmien.

Mazanie

A do tretice, v jednoduchosti je krasa :)

Jednoduche

$users = new Promeo(':tp:users');
$users->delete(3); // odstrani uzivatela s ID 3

Hromadne

// predpokladajme, ze v $logs mame zoznam logov a chceme vymazat tie, ktore su
// starsie ako 2 tyzdne
$checkdate = strtotime('-2 weeks');
$logger = new Promeo(':tp:logs');
foreach ($logs as $log) {
	if ($log->date < $checkdate) {
		$logger['d'] = $log->id;
	}
}
$logger->flushDelete();

Metoda flushDelete vracia pocet uspesne vymazanych zaznamov.

westrem
Člen | 398
+
0
-

Toggling, not trolling

Pocas programovania sa casto stretavame s ukonom, ked potrebujeme v databaze znegovat hodnotu v BOOL stlpci
pri urcitom zazname. V beznom ponimani by sme bud natvrdo napisali query (napr do dibi ;)) alebo pokial pracujeme
s nejakym sofistikovanejsim nastrojom tak vytvorili entitu, nastavili pozadovany udaj a vykonali zmenu.
Zdlhave, pracne a nachylne k chybam. Podme sa preto konecne skrabat tou pravou rukou :)

$vouchers = new Promeo(':tp:vouchers');
$vouchers->toggle(4, 'valid');

Ano, stalo sa presne to co si myslite. Dany prikaz znegoval hodnotu v stlpci valid pre voucher s ID 4.
Jednoduche vsak?

Samozrejme mozeme vykonat toggle aj na viacerych stlpcoch naraz:

$vouchers = new Promeo(':tp:vouchers');
$vouchers->toggle(4, array('valid', 'editable'));

A mate na sklade aj hromadnu variantu? Ano mame!

$vouchers = new Promeo(':tp:vouchers');
// predpokladajme, ze mame v premennej $oldVouchers entity starych
// voucherov, ktore chceme invalidovat
foreach ($oldVouchers as $id => $foo) {
	$vouchers['t'] = $id;
}
$vouchers->toggleColumns('valid', 'editable'); // nastavime, ktore stlpce sa maju znegovat
$vouchers->flushToggle();

Metoda flushToggle() uz tradicne vracia pocet uspesne vykonanych zmien.

Matematiku ja rad

Urcitou ceresnickou v praktickosti Promea je aj rychle vykonavanie jednoduchych matematickych udajov.

$vouchers = new Promeo(':tp:vouchers');
$vouchers->math(4, 'discount+30');

Uvedeny prikaz, zvysi discount u voucheru s ID 4 o 30. Podporovane operacie su
+, -, / a *. Samozrejme je mozne menit viac stlpcov naraz:

$users->math(6, 'salary*2; hours+20; holiday-10');

Choose wisely

Tak a dostavame sa k tomu zakladnemu, preco bol Promeo stvoreny – efektivne a rychle ziskavanie
dat z databazy.

V predoslych prikladoch sme videli zakladny postup ako ziskavat data:

// ziskanie vsetkych uzivatelov
$users = new Promeo(':tp:users');

foreach ($users as $id => $user) {
	// spracovanie ..
}

// ziskanie konkretneho uzivatela s ID 3
$users = new Promeo(':tp:users');
$user  = $users->get(3);

Ano, ano to sme uz videli, ukaz nam viac! Dobre, mate radi DibiFluent? Ja ano :), preco teda nevyuzit jeho
silu a pustit sa do zlozitejsich konstrukcii:

$vouchers = new Promeo(':tp:vouchers');
$vouchers->where('[discount] > %f', 40.33)
		 ->where('[valid] = 1')
		 ->orderBy('id')->desc()
		 ->limit(5)
		 ->offset(20)
		 ->getAll();

Promeo ako raw zdroj napojenia na databazu vyuziva prave DibiFluent, preto sa fantazii medze nekladu ;).

Sice mam DibiFluent hodne rad, predsa len som nasiel miesto, ktore v nom slo este trochu vylepsit – select klauzula:

$vouchers = new Promeo(':tp:vouchers');
$vouchers->select('id', 'valid%b', 'code%s')
		 ->getAll();

Ano hadate spravne, vyssie uvedeny kod sposobi to, ze sa z databazy vyberu len stlpce
id, valid a code. Zaroven mozte vidiet, ako je mozne vynutit si konkretny typ u
danych stlpcov – k tomuto ucelu sluzia modifikatory z dibi. Type juggling je vsak
plne automaticky a preto tuto moznost vyuzijete asi len ked potrebujete menit hodnotu na typ
bool, ktora sa kvoli limitacii MySQL neda zistit automaticky.

Podme vsak na dalsie moznosti pristupu k entitam. Promeo podporuje aj ArrayAcces
takze je mozne aj nieco ako:

$relations = new Promeo(':tp:user_relations', '#uid_1,uid_2');
$relation  = $relations[array('uid_1' => 1, 'uid_2' => 2)]; // vztah s klucom (1,2)

Padla vam sanka? Na priklade su vidiet hned dve veci naraz – spominany ArrayAccess ako aj fakt,
ze Promeo sa naozaj neobmedzuje na jednoduche kluce a umoznuje intuitivne pouzivat aj viacnasobne kluce.
Tu k dokonalosti uz hadam chyba len vylepsena syntax jazyka PHP o tuples a bolo by vystarano :).

Podme sa ale pozriet na multi column primary keys blizsie. Vsetci vieme, ze PHP je jazyk miestami obmedzeny a jedno taketo
obmedzenie prichadza aj s traverzovanim nad polom pomocou foreach-u, ked v tradicnej syntaxi
foreach {$array as $key => $val} moze byt $key iba regulerny kluc, ziadny objekt, pripadne pole.

Nemajte obavy, Promeo riesi aj tuto situaciu a ponuka hned dve moznosti ako traverzovat nad ziskanym result setom a mat pritom priamo na dosah
multi column PK.

Prvy a jednoduchsi sposob je pouzit traverse:

$relations = new Promeo(':tp:user_relations', '#uid_1,uid_2');
while ($relations->traverse($pk, $relation)) {
	// $pk je v skutocnosti pole a
	// $relation je nasa entita
}

Druhy sposob, ponuka urcity konfort v podobe iteratora na aky sme zvyknuty napr. v
Latte sablonach:

$relations = new Promeo(':tp:user_relations', '#uid_1,uid_2');
while ($relations->walk($pk, $relation, $iterator)) {
	if ($iterator->isFirst()) {
		// ..
	}
	if ($iterator->isOdd()) {
		// ..
	}
	// apod.
}
westrem
Člen | 398
+
0
-

Sikovne vsak? Teraz vsak prichadza ta spravna chvila na odkrytie hlavnej prednosti Promea,
ktoru ja interne nazyvam „poskladaj ma ako chces“. Na tradicnom pristupe k tvorbe modelov
mi hodne vadila jedna vec, pokial som na jednom mieste aplikacie potreboval ziskat vsetky zaznamy
a na druhom zaznamy vyhovujuce urcitej podmienke vecsinou to znamenalo nadefinovat dve takmer totozne
metody, ktore sa lisili len v pridani podmienky. Samozrejme, niekoho by napadlo spravit obecnu funkciu, ktora
berie rozne argumenty urcujuce klauzulu where, order by, limit apod. To vsak ale znamena, ze pokial chcem
pouzit len limit musim si pamatat kolky parameter v poradi to je a ostatne nastavit na NULL – fuj fuj, nepekne riesenie.

Ovela viac ma preto pritahovala myslienka moct si nadefinovat model ad-hoc priamo na mieste kde ho potrebujem a
moct ho kedykolvek zmenit. Toto je zakladna a nosna myslienka celeho Promea – postav si model aky chces a najme tam
kde to realne potrebujes! Pozrime sa preto ako sa Promeo stavia k relaciam v databazach a ako s nimi pracuje.

V ponimani vztahov v databaze existuju v podstate tri modelove situacie:

  1. Entita, ktora obsahuje ako svoju property kolekciu dalsich entit – napr. kurz (1) a terminy (n)
  2. Entita, ktora obsahuje ako svoju property dalsiu entitu – napr. kurz (1) a miesto konania (1)
  3. Entity, ktore obsahuju ako svoju property kolekciu inych entit – napr. novinky (n) a tagy (n) Rozdiel oproti prvemu pripadu bude v tom ako je relacia realizovana v databaze

Podme si teda rozobrat jednotlive pripady:

Moze byt toho viac

Majme tabulku courses:

  • id
  • name

a tabulku terms:

  • id
  • id_course
  • max_people
  • date

a chceme ziskat vsetky kurzy spolu s terminmi, pricom chceme len terminy kde sa moze prihlasit viac
ako 5 ludi a chceme ich mat zoradene podla datumu zostupne. Jednoduchy ukol, no predpokladam, ze vela z
vas by to riesila rozne a podla mna miestami aj zbytocne komplikovane. Ja rad jednoduchost:

$courses = new Promeo('courses');
$courses->many('terms')
			->where('[max_people] > 5')
			->orderBy('date')->desc();

Tada! Vo vysledku dostaneme entitu, ktora bude mat ako property terms kolekciu terminov, ktore su zoradene podla
datumu a splnuju podmienku v klauzuli where. Mozno sa pytate ako Promeo vie odkial ma co brat, co su PK apod. Ano priznavam
toto je trosku modelovy priklad, kde je vyuzita sila konvencii a preto nebolo treba udavat nic viac (v opacnom pripade by
sa upresnila len druha tabulka a referencny kluc). Ku konvenciam sa
vsak dostanem za chvilu.

Mam len jedneho kamarata

Poupravme teraz trochu nas priklad. Uvazme, ze kazdy termin moze ucit iny lektor a my chceme mat
pri kazdom termine aj konkretne informacie o lektorovi.

Tabulka terms bude teda vyzerat nasledovne:

  • id
  • id_course
  • max_people
  • date
  • id_teacher

a tabulka teachers:

  • id
  • name
  • qualification

a nove zadanie pomocou Promea:

$courses = new Promeo('courses');
$courses->many('terms')
			->where('[max_people] > 5')
			->orderBy('date')->desc()
				->one('teacher');

Vo vysledku ma teraz kazdy termin property teacher, ktory obsahuje dane informacie o lektorovi.

Kazdy s kazdym paktuje

Posledny pripad je podobny tomu prvemu, lisi sa vsak v tom, ze relaciu sprostredkuva tzn. relation table:

Tabulka news:

  • id
  • published
  • approved
  • text

Tabulka tags:

  • id
  • tag

Tabulka news_have_tags:

  • id_new
  • id_tag

Vo vysledku chceme vsetky odsuhlasene novinky zoradene podla datumu publikovania zostupne spolu s relevantnymi tagmi.

$news = new Promeo('news');
$news->where('[approved] = 1')
	 ->orderBy('published')->desc()
	 	->relation('tags');

To je vsetko, naozaj. Zase raz sme sice vyuzili silu konvencii avsak samozrejme je mozne upresnit nazvy klucov v tabulkach a zdrojove tabulky.
Kazda novinka bude teda obsahovat ako property tags zoznam pridelenych tagov.

westrem
Člen | 398
+
0
-

Efektivita

Niektori z vas sa mozno teraz pytaju a ako je to s efektivitou, kolko dotazov sa posiela do databazy, kolko udajov sa taha apod.
Strucne? Je to v pohode :P.

Ale teraz vazne, efektivita je to miesto, kde som sa nechal inspirovat Jakubovym NotORM a idem po jeho vzoru, tzn. do databazy sa posle len tolko
dotazov, kolko by ste ich tam poslali rucne pri vsetkej optimalizacii – na kazdy typ tabulky jeden. Naviac, tahaju sa udaje len o skutocne potrebnych
entitach, tzn. napriklad v pripade lektorov, pokial existuju ucitelia, ktori nie su pripradeni k ziadnemu terminu tak o nich nic nezitujeme a ziadne data
sa o nich neprenasaju zbytocne naviac. Rovnako to funguje pri tagoch. Pre uplnost vsak dodam, oproti NotORM sa do databazy posle skutocne o jeden dotaz naviac -
jedna sa o dotaz zistujuci PK pre hlavnu tabulku (v prikladoch su to courses a news).

Toto je z toho dovodu, ze vramci efektivity Promeo netaha data pokial nie su skutocne potrebne. Kedy je to vhodne?
Ked chceme napriklad zistit pocet noviniek, alebo ci existuje novinka s danym ID.

$news = new Promeo('news');

if (isset($news[4])) { // existuje novinka s ID 4?
	// .. spracovanie
}

// alebo
$news = new Promeo('news');
$this->template->numberOfNews = count($news);

V prikladoch vyssie sa v skutocnosti polozil len jeden dotaz SELECT [id] FROM [news]. Toto ma za nasledok
vyssiu efektivitu a nizsiu zataz na DB server, kedze sa netahaju data, ktore sa vobec nevyuziju.

Konvencie

To, ze zivot s Promeom vie byt vdaka konvenciam jednoduchsi ste uz zistili. No nielen s Promeo, vo vseobecnosti je lepsie
ked clovek dodrzuje urcite konvencie a drzi sa ich – zvysuje to prehladnost a citatelnost kodu, ci uz pre vas samotnych alebo pre
niekoho kto ten kod prevzal po vas.

V Promeu ma konvencie nastarosti interface IPromeoConvention, ktory definuje 5 metod:

  • formatKey
  • reverseFormatKey
  • referalKey
  • tableName
  • mediatorTable

Jeho zakladna implementacia PromeoConvention umoznuje nasledovne:

  1. nazvy stlpcov, ktore obsahuju podciarkovnik _ je mozne v kode ako properties pisat ako CamelCase tzn. v DB mame is_preview v kode $entity->isPreview
  2. referencny kluc sa vyraba z nazvu tabulky odtrhnutim koncoveho s a pridanim id_ na ziaciatok tzn. tabulka courses, referal key id_course
  3. Mediator table (relacna tabulka vztahu) sa tvori ako pozadujuca_tabulka_have_zdrojova_tabulka tzn. ako sme videli v priklade vyssie news_have_tags

Nastavenia

V samotnom zavere spomeniem uz len moznost nastavit si dve zakladne veci – default entity class a default convention class.
Je to z toho dovodu, ze nie kazdemu moze vyvovat zakladna implementacia PromeoEntity a PromeoConvention a bolo by zbytocne a
pracne aby vsade v kode musel nastavovat svoje implementacie. Preto sa daju default hodnoty zmenit a to nasledovne:

PromeoSettings::$convention = 'MyConventionClass';
PromeoSettings::$entityType = 'MyEntityClass';

Zaverom

Tak to by bolo prvotne predstavenie mojho Promea. V plane mam v najblizsej dobe ho dostat do podoby publikovatelnej samostatne (rozumej co najmenej zavislosti na mojej nadstavbe nad Nette) a
zjednodusit este par veci v zapise pri narabani s entitami.

Rad by som pocul vase nazory a dojmy, ci si myslite, ze by sa to mohlo zaimplementovat do Nette alebo nejak prepojit so sucastnou Nette\Database – priznam sa vsak, ze PDO nemam prilis rad a neviem
nakolko by to teda bolo mozne. Pripadne ci by ste to radsej uvitali ako samostatnu kniznicu.

Na zaver uz len fakt jedine – Stasne a vesele sviatky a doprajte si aj trochu oddychu ;).

JakubJarabica
Gold Partner | 184
+
0
-

Zatiaľ len letmo som to prešiel a nevidím jednu vec – fetchPairs. Ako plníš selectboxy? Keď to bude niekde(ideálne na GitHube live) veľmi rád to otestujem.

bojovyletoun
Člen | 667
+
0
-

A jsou tam Behaviors z Ormionu?

Filip Procházka
Moderator | 4668
+
0
-

A já jsem si o sobě myslel, že jsem grafoman :)

Co mi vadí, nebo mi to nepříjde úplně vhodné:

1) výpis dat entity

$user->name; // objektovy pristup
$user['email']; // array pristup

tohle je zbytečné, lepší je povolit jen jeden způsob (já raději objektový). Jde o to že nebudeš mást uživatele, zavedeš konvenci a oni se jí budou muset držet. Tohle jsem často taky řešil a zjistil jsem, že méně je někdy více. Když nebudeš mást uživatele 10 způsoby zápisu, věř mi že to ocení.

2) když css tak css

$relations = new Promeo(':tp:user_relations', '#uid_1,uid_2');

tohle by ti v CSS neprošlo

$relations = new Promeo(':tp:user_relations', '#uid_1,#uid_2');

tohle ano

$relations = new Promeo(':tp:user_relations', array('#uid_1','#uid_2')); // popř. array('uid_1','uid_2')

a takhle by se mi to líbilo nejvíc :)

3) CRUD – hromadné vytváření

$vouchers[] = $voucher;
$vouchers->add($voucher, FALSE);
$vouchers['a'] = $voucher;
$vouchers['+'] = $voucher;

tohle je vskutku odstrašující, nechal bych maximálně první 2

4) flush

$vouchers->flushAdd();

tohle je myslím zbytečně matoucí, to máš jako metodu na každou ze 4 operací?

$vouchers->flush();

tohle mi příjde jednodužší

pokud však trváš na tvém crud přístupu, tohle bude pořád lepší (máš hromadné splachovadlo)

$vouchers->flush('u');
$vouchers->flush('update');

5) CRUD všeobecně

Když na to tak koukám, ono to skoro vypadá že máš vždycky v objektu PromeoUnitOfWork. Což není moc košér
Víc by se mi líbila fronta jako má Doctrine2. Něco si o tom přečti, třeba tě něco napadne ;)

6) CRUD – hromadné operace

co mi vyloženě vadí, tak jak to pracuje s těmi ID. Ty si dáš do fronty, ty jednotlivé idčka entity a pak to pravděpodobně mažeš pomocí nějakého DELETE FROM .. WHERE id IN(...). Nebylo by lepší něco takového?

foreach ()
$checkdate = strtotime('-2 weeks');
$logger = new Promeo(':tp:logs');
foreach ($logs as $log) {
        if ($log->date < $checkdate) {
                $logger['d'] = $log;
        }
}
$logger->flushDelete();

s tím, že by jsi si v Promeo sám podle primárních klíčů vytáhl property, podle které by se mazalo? usnadnilo by to zápis pokud máš více primárních klíčů

7) math

$users->math(6, 'salary*2; hours+20; holiday-10');

v toggle to máš ale hezčí!

$users->math(6, array('salary*2', 'hours+20', 'holiday-10'));

8) hromadné cokoli

$vouchers = new Promeo(':tp:vouchers');
// predpokladajme, ze mame v premennej $oldVouchers entity starych
// voucherov, ktore chceme invalidovat
foreach ($oldVouchers as $id => $foo) {
        $vouchers['t'] = $id;
}
$vouchers->toggleColumns('valid', 'editable'); // nastavime, ktore stlpce sa maju znegovat
$vouchers->flushToggle();

nápad: co takhle použít výsledky, místo zbírání IDček?

$vouchers = new Promeo(':tp:vouchers');
$result = $vouchers->select('id')->getAll();
$vouchers->toggle($result, array('valid', 'editable'));

to vypadá lépe, né? :)

9) získávání záznamů

$relations = new Promeo(':tp:user_relations', '#uid_1,uid_2');
$relation  = $relations[array('uid_1' => 1, 'uid_2' => 2)]; // vztah s klucom (1,2)

když pominu fakt, že tohle jsem ještě nikde neviděl a moc se mi to nelíbí (ale asi bych si zvykl) tak bych ti poradil, co takhle to ještě víc ulehčit?

$relations = new Promeo(':tp:user_relations', '#uid_1,uid_2');
$relation  = $relations[array(1, 2)]; // vztah s klucom (1,2)

stejně klíče máš pojmenované, a pokud nepotřebuješ měnit jejich pořadí, tak takhle je to více přehledné.

10) WTF nedefinované proměnné?

while ($relations->walk($pk, $relation, $iterator)) { }

ale fuj

while (list($pk, $relation, $iterator) = $relations->walk()) { }

http://cz.php.net/list http://cz.php.net/each ;)

11) Statická třída?

PromeoSettings::$convention = 'MyConventionClass';
PromeoSettings::$entityType = 'MyEntityClass';

tohle je fuj fuj

12) Nějaký ten hromadný obal?

Líbilo by se mi něco takového

class Database
{

	private $configurator;

	private $tables = array();


	public function table()
	{
		$args = func_get_args();
		$key = serialize($args);

		if (!isset($this->tables[$key])) {
			$ref = new ReflectionClass('Promeo');
			$table = $ref->newInstanceArgs($args);
			$table->setConfigurator($this->configurator);
			$this->tables[$key] = $table;
		}

		return $this->tables[$key];
	}

	public static function createDatabase($options)
	{
		$configurator = $options['configurator'] ?: 'PromeoConfigurator';
		$db = new static(new $configurator());
		return $db;
	}
}
service.database.factory = Promeo\Database::createDatabase()
$db = $application->getService('database');
$vouchers = $db->table(':tp:vouchers');
....

s tím, že tohle by se zase dalo hezky zabalit do pár metod třeba v presenteru, nebo v nějakých službách atd, ale tak to už je kosmetika :)

Závěrem

Syntaxe na relace se mi velice líbí, něco podobného jsem se snažil vymyslet a tvoje synataxe je dovedená k dokonalosti :)

Jinak přeju zdařilý vývoj a spoustu spokojených uživatelů. Doufám, že moje reakce přinesla nové nápady :)

Editoval HosipLan (24. 12. 2010 20:33)

bojovyletoun
Člen | 667
+
0
-

na co já bych reagoval?

  • 2 způsoby, které dělají totéž, jsou matoucí ($x->y, $x[‚y‘]). Prostě je to pro mě více přemýšlení.
  • 2) líbí se mi rozložení do array, je to prostě paramatrizovatelnější
  • hromadné delete …[‚d‘] se mi zdá big wtf

PS: jak bys řešil „kontigenční tabulku“ link popřípadě ukázkový příklad z notorm

Editoval bojovyletoun (24. 12. 2010 19:59)

dakota
Člen | 148
+
0
-

westrem napsal(a):

V ponimani vztahov v databaze existuju v podstate tri modelove situacie:

  1. Entita, ktora obsahuje ako svoju property kolekciu dalsich entit – napr. kurz (1) a terminy (n)
  2. Entita, ktora obsahuje ako svoju property dalsiu entitu – napr. kurz (1) a miesto konania (1)
  3. Entity, ktore obsahuju ako svoju property kolekciu inych entit – napr. novinky (n) a tagy (n) Rozdiel oproti prvemu pripadu bude v tom ako je relacia realizovana v databaze

Môžeš uviesť na ukážku aj dotazy, ktoré by sa pri jednotlivých vzťahoch vykonali?

Editoval dakota (25. 12. 2010 9:17)

Ani
Člen | 226
+
0
-

Vypadá to docela pěkně hlavně to spojování.

Jen by mě zajímalo jak máš řešené řazení podle nějakéjo atributu, který vlastní property. Klasicky když mám výpis článků s autory a chci řadit podle jména autora?

A pak jestli je nějaká možnost jak získat výsledek jako jednu entitu (připojené tabulky nevytváří další entity, ale přidávají se s nějakým prefixem, ke stávající), tak aby se dala pohodlně vkládat do gridu?

Honza Kuchař
Člen | 1662
+
0
-

Zdá se mi to jako produkt životaschopný. Určitě se na to podívám podrobněji. A ovládání je mi velice sympatické. Ale těch více metod na přidávání mi přijde zvláštní. Array i objekt přístup je zbytečný, ale z JavaScriptu jsem zvyklý, že to je to samé, takže za mě ok. ;)

Fakt se mi to moc líbí. (po přečtení úvodu zde na fóru)

westrem
Člen | 398
+
0
-

Tak az po 3 tyzdnoch sa mi podarilo dostat naspet na Forum, idem teda postupne odpovedat.

westrem
Člen | 398
+
0
-

JAM3SoN

Kedze cely Promeo je stavany na principe prace s low level entitami (s moznostou definovat si vlastne), nema separatne metody typu fetchSingle, fetchPairs apod. ako ich pozname napr z dibi. Fetch pairs a jej funkcionalita je vsak hojne pouzivana, preto uvazujem o metode, ktora by vedela z daneho result setu vratit prave pole obsahujuce pozadovany kluc a value.

bojovyletoun

Ormion som nepouzival a ani nevidel zdrojaky. Vies mi priblizit co behaviours v jeho ponimani znamenaju?

HosipLan

V prvom rade velke dakujem, ze si sa tak rozpisal ;).

Rozne pristupy

Zatial tam ponecham oba s tym, ze preferovany bude objektovy. Nie vsetci vsak vedia ako dynamicky pristupovat k objektovym properties a pre tychto ludi bude lepsie pouzivat array access syntax. Beriem vsak poznamku o tom, ze menej je niekedy viac, s tym sa neda nez suhlasit.

Vlastne ID u entity a CSS

Ako pisem, je to len ispiracia v pouzity znaku, nie v uplnej syntaxi. Ja viem, ze v CSS by moj zapis nepresiel, resp. neznamenal to co znamena u mna. To vsak nebol ani zamer.

CRUD a hromadne metody

S tymi roznymi sposobmi ako pridavat entity na zasobnik mas pravdu. Je to viac ci menej moja lenivost a preto si umoznujem viac sposobov, ale skor sa asi uchylim k standardizacii jedneho a od zvysnych upustim.

Samostatne flush metody vsak ponecham, z toho dovodu aby bolo mozne flushovat jednotlive zasobniky separatne. Pridam vsak asi jednoduchu flush metodu, ktora bude flushovat ten zasobnik s ktorym sa naposledy robilo.

UnitOfWork – priznam sa, ze ten pojem pocujem prvykrat. Je to nejaky design pattern? (Tie mam cca prestudovane ale tento nazov som nikde nevidel). Vies mi to popisat blizsie, pripadne ma odkazat na nejaky dobry zdroj? Vdaka

Hromadny DELETE – ano tu mas pravdu, sam teraz premyslam, preco som to implementoval tak, ze musi user rucne udavat IDcka ked to moze Mapper spravit zanho, vdaka za vypichnutie ;)

Ziskavanie zaznamov

Co sa viac stlpcovych ID tyka, chcem ten sposob zapisu sprehladnit a zjednodusit. Tak ako som to napisal to tam je preto aby bolo jasne, ze je to mozne, chcem to vsak dotiahnut na vyssi level intuitivnosti.

Nedefinovane premenne

Vidis v tom pristupe vyslovene nieco spatne? Nie je to tradicny pristup ale nemyslim si, ze je zly. list samozrejme poznam, ale osobne sa mi jeho pouzivanie nepaci a tiez sa mi nepozdava myslienka, ze by mala metoda walk vracat pole, odhliadnuc od toho, ze moj zapis je kratsi a podla mna aj zrozumitelnejsi.

Staticka trieda

Zase raz, nevidim v tom nejak extra problem. Ano, nie je to koser z OOP hladiska, cisto staticke triedy vyuzivam minimalne, ale nastavenie zatial spociva iba z tychto dvoch veci, preto nevidim momentalne dovod to menit. Naviac dalsi argument, preco je to zatial takto je ten, ze tak torchu cakam ako sa vyvinie DI v Nette a aka bude jeho finalna podoba a podla toho by som to v buducnosti upravil.

Hromadny obal

Vyzera pekna, ale ako pisem vyssie, service like to spravim vo chvili ked bude jasne DI v Nette.

westrem
Člen | 398
+
0
-

Ani

Radenie sposobom akym popisujes ty by som imho riesil uz na SQL urovni, radit to v PHPku po ziskani vysledku by bol podla mna dost velky overhead.

Co sa tyka vysledku ako jednej entity tak ako to popisujes ty, tak to momentalne este v Promeu nie je, ale priznam sa, ze je to asi posledna taka vecsia featuresa, ktora mi tam chyba, treba ju vsak poriadne premysliet a zaimplementovat. Cely vyvoj hlavnych casti Promea bol totiz poznaceny takym tym osvietenim ked ma napadli hlavne stavebne kamene kodu a zvysok funkcionality sa uz len doplnal :)

Honza Kuchar

Dakujem za kladne hodnotenie, od cloveka s takym skillom to pride povzbudive :)

PS:

Co sa tyka ukazkovych dotazov, pokusim sa ich sem pridat v dohladnej dobe, len je teraz cez skuskove malo casu na hocico :(

Filip Procházka
Moderator | 4668
+
0
-

westrem napsal(a):

Rozne pristupy

Nie vsetci vsak vedia ako dynamicky pristupovat k objektovym properties…

Však to můžeš pak vysvětlit v dokumentaci a nechta jen jeden přístup :)

Vlastne ID u entity a CSS

Ako pisem, je to len ispiracia v pouzity znaku, nie v uplnej syntaxi.

nejde mi o to aby to v CSS prošlo, ale aby to bylo intuitivnější :)

CRUD a hromadne metody

UnitOfWork

je to Design Patter, v podstatě obálka, do které se ukládají změny a pak se hromadně spustí jejich persistence. V režii UoW je ale pouze to ukládání změn, ta persitence je pak v režii některé jiné třídy.

Nedefinovane premenne

Vidis v tom pristupe vyslovene nieco spatne? Nie je to tradicny pristup ale nemyslim si, ze je zly. list samozrejme poznam, ale osobne sa mi jeho pouzivanie nepaci a tiez sa mi nepozdava myslienka, ze by mala metoda walk vracat pole, odhliadnuc od toho, ze moj zapis je kratsi a podla mna aj zrozumitelnejsi.

je to ošklivá syntax

foreach ($relations->walk() as $pk => $entity){
	// ...
}

foreach ($iterator = new PromeoWalkIterator($relations) as $pk => $entity){
	// ...
}

Nehledě na to že ten iterátor je jenom třešnička a využiješ ho tak ve 20% případů (z toho ještě většinou jen v šabloně) tak je zbytečné ho tam cpát.

Staticka trieda

Zase raz, nevidim v tom nejak extra problem.

Statické třídy jsou zlo. Protože pak se rozhodnu, že potřebuju pracovat s více databázemi a ta jedna bude mít jinou konfiguraci? Jsem kde? :)

Nevidíš důvod to měnit protože se ti nechce, ale sám moc dobře víš, že by to tak bylo lepší :)

Hromadny obal

Vyzera pekna, ale ako pisem vyssie, service like to spravim vo chvili ked bude jasne DI v Nette.

Na Nette DI se vykašli, nepotřebuješ nástroje Nette, aby jsi mohl používat DI. Je to návrhová vzor né knihovna

Editoval HosipLan (14. 1. 2011 8:57)

Ani
Člen | 226
+
0
-

westrem napsal(a):

Radenie sposobom akym popisujes ty by som imho riesil uz na SQL urovni, radit to v PHPku po ziskani vysledku by bol podla mna dost velky overhead.

No jasně že na úrovni db, jenže tam se narazí s tím notorm přístupem, takže se musí nějak joinovat viz http://php.vrana.cz/…v-notorm.php

vrana
Člen | 131
+
0
-

HosipLan napsal(a):

Syntaxe na relace se mi velice líbí, něco podobného jsem se snažil vymyslet a tvoje synataxe je dovedená k dokonalosti :)

No a není lepší syntaxe NotORM, kdy připojované tabulky v dotazu vůbec nemusím psát a prostě je jen používám ve výsledku? Nejde o žádný sarkasmus – ptám se vážně, jestli jsem něco nepřehlédl.

Mě zaujala práce s vícesloupcovým primárním klíčem – to převezmu do NotORM.

Filip Procházka
Moderator | 4668
+
0
-

vrana napsal(a):

No a není lepší syntaxe NotORM, kdy připojované tabulky v dotazu vůbec nemusím psát a prostě je jen používám ve výsledku? Nejde o žádný sarkasmus – ptám se vážně, jestli jsem něco nepřehlédl.

V mém případě jsem se snažil vymyslet obecnou syntaxi zápisu dotazu pro datovou vrstvu, protože nad ní se válelo ORM a ty mappery mohly být i v MySQL i v CouchDB. Moje pokusy ztroskotaly tady: https://gist.github.com/…7a3a2dad4e58 :)

Necítím se kompetentní soudit takhle brzo po ránu, ale myslím, že NotORM má syntax lepší.

Editoval HosipLan (18. 1. 2011 8:05)

LuKo
Člen | 116
+
0
-

Vypadá to luxusně, kdypak se lze těšit na zveřejnění?

Jeden tip k referal key id_course: Jakub v NotORM zvolil opačný (course_id) a tuším, že to má podpořené anketou mezi vývojáři. I když to bude v Promeu nastavitelné, většina vývojářů bude nastavovat právě tento parametr. Nestálo by za to udělat Jakubovo konvenci jako výchozí?