Promeo – vas novy model do Nette
- westrem
- Člen | 398
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
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
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
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:
- Entita, ktora obsahuje ako svoju property kolekciu dalsich entit – napr. kurz (1) a terminy (n)
- Entita, ktora obsahuje ako svoju property dalsiu entitu – napr. kurz (1) a miesto konania (1)
- 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
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:
- nazvy stlpcov, ktore obsahuju podciarkovnik
_
je mozne v kode ako properties pisat ako CamelCase tzn. v DB mameis_preview
v kode$entity->isPreview
- referencny kluc sa vyraba z nazvu tabulky odtrhnutim koncoveho
s
a pridanimid_
na ziaciatok tzn. tabulkacourses
, referal keyid_course
- Mediator table (relacna tabulka vztahu) sa tvori ako
pozadujuca_tabulka_have_zdrojova_tabulka
tzn. ako sme videli v priklade vyssienews_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
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.
- Filip Procházka
- Moderator | 4668
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
Promeo
4xUnitOfWork
. 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
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
westrem napsal(a):
V ponimani vztahov v databaze existuju v podstate tri modelove situacie:
- Entita, ktora obsahuje ako svoju property kolekciu dalsich entit – napr. kurz (1) a terminy (n)
- Entita, ktora obsahuje ako svoju property dalsiu entitu – napr. kurz (1) a miesto konania (1)
- 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
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
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
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
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
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 metodawalk
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
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
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
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
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í?