Pokus o dibi ORM
- Ondřej Brejla
- Člen | 746
Rozhodně PHP 5.2. Je zatím, bohužel, v podstatě všude. Při omezení na 5.3 to budu pár let využívat tak maximálně na lokálu…
- Honza Marek
- Člen | 1664
romansklenar napsal(a):
Honza Marek napsal(a):
_Martin_ napsal(a):
- Fluent interface je fajn, ač u metody
save
mám pocit, že je zbytečný (save
je takový konec, co potom s tím objektem dělat dál?)Třeba můžeš po uložení získat id, když je auto increment.
IMHO není třeba, pokud to bude fungovat takhle:
$page = new Page; $page->name = '...'; $page->content = '...'; echo $page->id; // vede k výjimce $page->save(); echo $page->id; // zobrazí správně id => není důvod získávat ze save()
Mám další příklad, kdy by se to šiklo.
$page = Page::create(array(
"name" => "Název",
"text" => "Text článku"
))->save();
a teď chceš získat to id
$page->id;
- paranoiq
- Člen | 392
Honza Marek napsal(a):
Já z vás mám taky radost :-D
taky mám radost. pár věcí jsem si ujasnil a srovnal v hlavě :)
Nakonec to zřejmě udělám všechno podle sebe, tedy podle zásady, že nejjednodušší řešení je to nejlepší.
udělej. vážně
@juan: většinu novinek PHP 5.3 nelze v PHP 5.2 nijak emulovat. můžeš si třeba hrát na jmenné prostory s předponami tříd, ale třeba late static binding nebo __callStatic() prostě nenapodobíš :[
- Jan Jakeš
- Člen | 177
@juan: většinu novinek PHP 5.3 nelze v PHP 5.2 nijak emulovat. můžeš si třeba hrát na jmenné prostory s předponami tříd, ale třeba late static binding nebo __callStatic() prostě nenapodobíš :[
Právě proto jsem psal dvě různé verze – lišily by se bohužel i funkčností. To ORM vypadá parádně. Pokud se to dotáhne do konce, bude to super věc. Jenže na většině hostingů to ještě pár let nepůjde využít…
- stpnkcrk
- Generous Backer | 190
Jsem rozhodně pro PHP 5.3.
K těm hostingům – minimálně hosting ONEbit (http://www.onebit.cz/) už nabízí PHP 5.3 pro nové objednávky v ostrém provozu. ;)
Editoval skocourek (10. 11. 2009 16:24)
- Honza Marek
- Člen | 1664
Takže mám novinky. Na githubu je nová verze (https://github.com/…arek/dibiorm). Zatím funguje jen pro php 5.3 pro nette bez namespaců.
Zkusím sesumírovat způsob použití a vlastnosti.
- Typy a klíče se automaticky načítají z databáze. Jen jedno za život, kešuje se to.
- Nastavení typů můžu upravit v přepsané metodě createConfig (továrnička na konfiguraci).
- Můžu nastavit nějaké callbacky před/po vložení, úpravě a smazání ve stylu eventů Nette\Object.
- Při úpravě se ukládají jen změněné sloupce. Když nic nezměním, ani se dotaz neprovede.
- Primární klíč může být vícesloupcový. Hodnota se dá získat pomocí $object->getPrimary().
- Do databáze se ukládají jen hodnoty, který jsou sloupcema. Nezbuchne to tedy proto, že si do objekt uložím nějaké pomocné hodnoty.
- Save už nevrací boolean, ale vrací $this a hází výjimky.
- Leckterá metoda jde přepsat a tím je možné si model uzpůsobit.
- Stav objektu se dá zjistit přes $objekt->getState(). Hodnotou může být jedna z konstant BaseModel::STATE_NEW, BaseModel::STATE_LOADED, BaseModel::STATE_MODIFIED a BaseModel::STATE_DELETED. Stav jde i nastavit metodou setState.
// vyrobím si třídu
class Page extends BaseModel {
protected static $table = "pages";
// přenastavím nějaké typy
protected function createConfig() {
$cfg = parent::createConfig();
$cfg->setType("allowed", dibi::BOOL);
return $cfg;
// jde to nastavit ručně docela detailně, ale nechce se mi to rozepisovat
}
// inicializace objektu
protected function init() {
// nastavení eventu
$this->onAfterDelete[] = array($this, "deleteComments");
}
public function deleteComments() {
// jen příklad ;)
foreach ($this->getComments() as $comment) {
$comment->delete();
}
}
public function getComments() {
return Comment::findAllByPage($this->id);
}
}
// použití
// vytvoření článku
$page = Page::create(array(
"name" => "Název",
"text" => "Text"
))->save();
// najít článek podle id
$page2 = Page::find($page->id);
// jedině povolený
$page3 = Page::findByIdAndAllowed($page->id, true);
// nebo
$page4 = Page::find(array(
"id" => $page->id,
"allowed" => true,
));
// podobné je to i u hledání více záznamů
// všechny články
$pages = Page::findAll();
// povolené články
$pages = Page::findAllByAllowed(true);
// úprava
$page->name = "Nový název";
// nebo
$page->setData(array("text" => "Nový text"));
// a
$page->save();
// smazání
$page->delete();
// nebo
Page::create(array("id" => $page->id))->delete();
// snad je to všechno....
Verze pro php 5.2
By mohla být za cenu mnohem ošklivějšího api. A taky složitostí při údržbě samozřejmě. Názorně předvedu to api:
$pagesModel = new Page;
$page = $page->find($id);
$page->...
// nebo
$pagesModel = new Page;
$pages = $pagesModel->findAllByCategoryAndAllowed($cat, true);
Editoval Honza Marek (10. 11. 2009 18:45)
- lucass
- Člen | 89
Zdar,
vypadá to moc pěkně, akorát bych měl dotaz k problému, na který jsem narazil, když jsem se pokoušel o něco podobného:
- Jak řešit to, že data načítám z databázového view, který buď sdružuje více tabulek, formátuje sloupec, přidává sloupec (např. nějaký flag dle jiného sloupce použitím case when …) či sdružuje více sloupců (např. firstname a surname do fullname), ale data pak ukládám do tabulek (a klidně do více, když jsou např. vazební)?
- Když jsem zmínil vazební, jak se budou řešit M:N vazby? Bude mít model nějakou kolekci dalších modelů (á la Doctrine)?
Docela by mě toto zajímalo…
- Honza Marek
- Člen | 1664
lucass napsal(a):
Zdar,
vypadá to moc pěkně, akorát bych měl dotaz k problému, na který jsem narazil, když jsem se pokoušel o něco podobného:
- Jak řešit to, že data načítám z databázového view, který buď sdružuje více tabulek, formátuje sloupec, přidává sloupec (např. nějaký flag dle jiného sloupce použitím case when …) či sdružuje více sloupců (např. firstname a surname do fullname), ale data pak ukládám do tabulek (a klidně do více, když jsou např. vazební)?
Pokud by šlo jen o načtení dat, tak lze přepsat protected metodu
createFindFluent, která nyní generuje select * from tabulka
. Už
jsem psal, že data navíc nebudou ukládána (pokud nepřepíšeš ukládací
metody). Taky by šel přepsat setter a getter pro data, pro to možná
vymyslím i nějakou lepší podporu.
- Když jsem zmínil vazební, jak se budou řešit M:N vazby? Bude mít model nějakou kolekci dalších modelů (á la Doctrine)?
Zatím nevím. Ale například načtení tagů článku bych teď řešil zhruba takto:
class Page extends BaseModel {
protected static $table = "pages";
public function getTags() {
return $this->getDb()->select("*")->from("tags")
->where("id")->in(
$this->getDb()->select("tagId")->from("tags2pages")
->where("pageId = %i", $this->id)
)->execute()->setRowClass("Tag")->fetchAll();
}
}
class Tag extends BaseModel {
protected static $table = "tags";
}
Snad by to i fungovalo, psal jsem to z hlavy. Taky by na to šla napsat nějaká pomocná metoda nebo třída.
- _Martin_
- Generous Backer | 679
Pěkné. Jak psal Lucass, různé vazby tabulek bývají snad v každé trochu složitější webové aplikaci, s jejich podporou by bylo dobré počítat.
Podpora PHP 5.2 přes instance je v rámci možností dobré řešení.
Tímto způsobem by ovšem šlo oddělit i samotný model od ModelFactory či
jak to nazvat. Model je třída Page
a v modelu nemá metoda
find
co dělat. Opravdu.
Jinak to je pěkné. Líbí se mi, že se ukládají jen změněná data. Máš v plánu do budoucna zůstat u autodetekce DB struktury (případně za pomoci úprav nějakých metod), nebo taky půjdeš cestou nějakého definičního/konfiguračního souboru?
Možná by nebylo špatné (než se tu hádat o implementační detaily) zkusit sepsat nějaké featury a požadavky – každý tady se určitě setkal s nějakým specifickým případem – a dost možná spousta z nás řešila podobné problémy. Bylo by fajn, kdyby se toto ORM umělo s podobnými „používanými“ případy vypořádat, aby šlo cestou Nette.
- lucass
- Člen | 89
Honza Marek: Díky za reakci i nástin.
Co jsem ještě řešil, bylo, že např. při odeslání jednoho formuláře, který volá save nad více modely (tj. více tabulkami), jsem posloupnost volání save obaloval transakcí, aby se v případě chyby neuložila polovina dat a nezpůsobilo to nekonzistenci databáze. Tak by asi bylo dobré s tím nějak počítat.
- _Martin_
- Generous Backer | 679
lucass napsal(a):
Honza Marek: Díky za reakci i nástin.
Co jsem ještě řešil, bylo, že např. při odeslání jednoho formuláře, který volá save nad více modely (tj. více tabulkami), jsem posloupnost volání save obaloval transakcí, aby se v případě chyby neuložila polovina dat a nezpůsobilo to nekonzistenci databáze. Tak by asi bylo dobré s tím nějak počítat.
Řešili jsme ve firmě podobnou věc. Šlo o model, který měl vazby na jiné modely. Řešení bylo jednoduché – tento model měl metodu save, která spustila transakci, pak postupně volala save na všech závislých modelech a nakonec transakci ukončila. Snad by to šlo zevšeobecnit – pokud ukládáš data a cheš, aby byla uložena všechna, znamená to, že tam nějaká vazba těch modelů je. Říkám to správně?
- lucass
- Člen | 89
_Martin_ napsal(a):
Řešili jsme ve firmě podobnou věc. Šlo o model, který měl vazby na jiné modely. Řešení bylo jednoduché – tento model měl metodu save, která spustila transakci, pak postupně volala save na všech závislých modelech a nakonec transakci ukončila. Snad by to šlo zevšeobecnit – pokud ukládáš data a cheš, aby byla uložena všechna, znamená to, že tam nějaká vazba těch modelů je. Říkám to správně?
Tak, tak. Jediné, co se musí pořešit, je spuštění a ukončení transakce až po volání save všechn modelů. Já to řešil nějak takto (trošku pseudo):
<?php
abstract class Base Model {
...
private function save() {
start_transakce();
$this->doSave(); // vlastni volani posloupnosti save v potomkovi
konec_transakce();
}
...
abstract protected function doSave();
}
?>
Potomek pak:
<?php
class PotomekModel extends BaseModel {
...
protected function doSave() {
update_insert_a_tak; // upravy nad timto modelem
foreach ($this->dalsiModely as $dalsiModel) {
$dalsiModel->doSave(); // u vnorenych nevolam save, ale doSave, aby obesel globalni transakci
}
}
...
}
?>
Je to takové hodně jednoduché. Myslím, že se to dá vykoumat jinak a lépe.
Editoval lucass (11. 11. 2009 15:27)
- paranoiq
- Člen | 392
@lucass et @martin: řešení problémů s podmíněnými transakcemi jsem navrhoval tady: https://forum.dibiphp.com/…ne-transakce. postačí, aby si dibi pamatovala jméno otevřené transakce (tzn. všechny transakce pojmenovávat jako savepointy). až bude chvíle, zkusím to napsat
- Jod
- Člen | 701
Ja to mám riešené tak, že, keď zavolám nad modelom save, tak sa podľa
väzieb zavolá model ktorému patrí (ak taký je), vytvorí sa transakcia
v ktorej sa uloží danný model a následne ukladá všetky naviazané modely
ktoré sú zavesené na onSaved event a nakoniec transakciu uzavre.
Nejak takto:
<?php
$user = User::find(1);
$user->name = 'Jozo';
$user->Order();
$user->Order->productId = $productId;
$user->Order->amount = 1;
$user->Order->save();
// alebo
$order = $user->Order(array('productId' => $productId));
$order->amount = 1;
$order->save();
?>
- _Martin_
- Generous Backer | 679
Jod napsal(a):
…
Vycházel jsem z myšlenky, že vnořeným objektům může být jedno, že jsou vnořené. Ale napadá mě, že to není pravda. Nevím sice, jestli správné slovo popisující ten vztah je vnoření; rozhodně to je závislost. Zkusím to ukázat na tvém (upraveném) příkladu.
// získáme zákazníka a vytvoříme novou objednávku
$customer = Customers::find($customerId);
$order = Orders::create();
$order->productId = $productId;
$order->amount = 1;
Nejprve zkusíme uložit samotnou objednávku
$order->save();
Uvedený řádek by měl skončit výjimkou, protože objednávka neví, komu patří.
Tak přiřadíme objednávku zákazníkovi
$customer->addOrder($order);
// nebo
$customer->orders[] = $order;
A nyní použijeme buď
$order->save();
nebo
$customer->save();
Vidíme, že se sice objekt $customer
změnil, ale on sám nic
ukládat nepotřebuje – on potřebuje, aby se uložil model, který je jeho
součástí. A co s tou transakcí?
Kouzelná metoda save
zavolá postupně metodu
hasChanged
– nejprve na svém modelu a pak na všech
přiřazených modelech. Pokud je změněno více modelů, než jeden,
automaticky spouští transakci (pokud už není spuštěná). A takhle to
může být zanořené až do halelujah.
V uvedeném řešení jsou jisté otazníky nad implementací – třeba
jestli se má stav changed
zjišťovat při každém volání, nebo
se bude držet nějakým flagem. To je ovšem na podrobnější diskuzi.
Ještě mě napadá: měl by fungovat i následující kód?
// bez předchozího přiřazení k objektu Customer
$order->customerId = $customerId;
$order->save();
Neměl. Kdo přijde na důvod?=)
- Jod
- Člen | 701
_Martin_ napsal(a):
Nejprve zkusíme uložit samotnou objednávku$order->save();
Uvedený řádek by měl skončit výjimkou, protože objednávka neví, komu patří.
Toto výjimkov skonči tak či tak, pretože to nevie ani databáza :)
_Martin_ napsal(a):
Kouzelná metodasave
zavolá postupně metoduhasChanged
– nejprve na svém modelu a pak na všech přiřazených modelech. Pokud je změněno více modelů, než jeden, automaticky spouští transakci (pokud už není spuštěná). A takhle to může být zanořené až do halelujah.
Ano, otom som písal
V uvedeném řešení jsou jisté otazníky nad implementací – třeba jestli se má stav
changed
zjišťovat při každém volání, nebo se bude držet nějakým flagem. To je ovšem na podrobnější diskuzi.
No mne to funguje, ale je tu dosť iných vecí čo treba vyriešiť :)
_Martin_ napsal(a):
Ještě mě napadá: měl by fungovat i následující kód?// bez předchozího přiřazení k objektu Customer $order->customerId = $customerId; $order->save(); *Neměl. Kdo přijde na důvod?=)*
Hlavne preto, lebo nemá productId :D
- lucass
- Člen | 89
_Martin_ napsal(a):
…
Někdo tu výše psal, že by bylo dobré nad dibi mít nějaké hlídání těch transakcí. Co spíš udělat nějakou globální statickou třídu (továrničku), např. ModelFactory, která by měla přehled o všech aktuálně používaných modelech, transakcích, prostě o všem potřebném? A přes ní by se všechno řešilo, tj. vytváření instancí konkrétních modelů, záznamů atp.
Hlavně třeba pro případ, že v průběhu jednoho přenačtení stránky se může se záznamem s konkrétním ID pracovat v různých částech aplikace vícekrát, takže pokud už by byl jednou načten a v paměti, díky tomu singletonu ModelFactory by se získal a nemusel znova tahat z DB (pokud to tedy neřeší nettovská cache).
- lucass
- Člen | 89
paranoiq napsal(a):
@lucass: …
Jo, to máš recht. Nějak jsem se nechal unést, páč jsem měl v hlavě nejprve jen tu továrnu (protože něco podobného používám). Prostě jsem myslel hlavně nějakou tu statickou třídu, nadstavbu, která by toto řešila samostatně a jen by využívala potenciál Dibi.
- romansklenar
- Člen | 655
Honza Marek napsal(a):
- Nastavení typů můžu upravit v přepsané metodě createConfig (továrnička na konfiguraci).
To je velmi příjemné, protože díky tomu si můžu přesunout definici sloupců třeba do jednoho konfiguráku (ne nutně přímo config.ini) a nemusím tak při úpravách procházet jednotlivé modely.
- _Martin_
- Generous Backer | 679
lucass napsal(a):
…
Mě se ta centralizace ani krapítek nelíbí… Má to nějaké výhody oproti tomu, co jsem psal?
Ad. kešování: uvažoval jsem, jak ho implementovat a dostal jsem se
myšlenkou úplně jinam – bylo by vítané, kdyby ORM provádělo
automaticky „zamykání“ načtených objektů? Protože mě strašně děsí
představa, že v aplikaci získám model, něco na něm změním – a mezi
tím to samé udělá jiný model v jiném procesu (například jiný
uživatel) – a já pak jeho změny přeuložím. Jak tohle řešit?
A řešit to vůbec?
Pokud ne, tak by kešování asi mělo fungovat tak, že se objekt načte a
drží si v paměti originální data na jednom místě (=ona keš) – a
úpravy si drží lokálně. Pokud dojde k uložení, porovná lokální data
s těmi originálními (dostupnými všude) a v případě přeuložení
obnoví i keš originálních dat. Každopádně by ta keš měla držet
nejdéle po dobu požadavku, nikoliv mezi více požadavky. A mě to
začíná celé připomínat systém pro správu verzí=D
Myšlenka číslo 2: vlastně by se to kešování dalo využít
k ošetření případu, že záznam změní jiný proces. Vždy při uložení
by se provedl SELECT
s nastaveným FOR UPDATE
a
porovnala se data načtená na začátku – v případě shody by se provedlo
uložení, v případě neshody by byla výjimka. Takže kešování good
nápad, teď už jen jak jej implementovat.
- Jod
- Člen | 701
No volá nadradený model podľa väzby, takže aj tak aj tak. Sú na seba
povešané cez udalosti, takže, keď sa zavolá hlavný model tak sa začnú
postupne ukladať aj tie, ktoré majú naň väzby a transakcia sa spustí až
nad zmeneným modelom.
Neviem či to je najlepšie riešenie, uvidí sa :)
Tak potom foreign key by mal byť asi readonly :)
- Honza Kuchař
- Člen | 1662
Ahoj,
tak jsem se pokusil přepsat jeden rozsáhlejší model pod ormion. Jsem
samozřejmě nadšen! Díky moc! ;) Samozřejmě taky jsem na něco narazil, tak
to sem přihazuji:
- Možnost převést na DibiDataSource. (např. kvůli datagridu)
- Pořešit vazby na další modely. Docela dost se mi líbilo to jak je to vyřešeno zde: http://www.phpactiverecord.org/…associations (ne že by to v současné chvíli nešlo, ale když už člověk píše desátou tabulku, tak si uvědomí, že píše pořád to stejné dokola, akorát pořád mění názvy a taky samozřejmě mi to ještě nekontroluje vazby při ukládání, to mě konkrétně ale až zase tak nevadí, protože si je kontroluje samotné PgSQL)
- Když si zavolám něco::findAll(bla), tak neovlivním řazení a tyhle všechny věci. A potom už s tím taky nic neudělám, vrátí se mi pole. Tzn. neimplementovat přímo do Ormionu nějakou metodu, která by vracela DibiFluent či něco takového?
Jinak posílám můj BaseModel, který řeší:
- převod na DibiDataSource
- převod na DibiFluent
- doplní metodu fetchPairs($key, $value,$conditions = array())
- používá aktivované připojení na databázi z globálního registru dibi
abstract class BaseModel extends Ormion {
/**
* Getts dibi connection
* @return DibiConnection
*/
protected static function getDb() {
return dibi::getConnection();
}
/**
* Transforms to DibiFluent
* @param array $conditions
* @return DibiFluent
*/
public static function toFluent($conditions = array()) {
return static::getDb()
->select("*")
->from("%n", static::$table)
->where("%and", $conditions);
}
/**
* Transforms to DibiDataSource
* @param array $conditions
* @return DibiDataSource
*/
public static function toDataSource($conditions = array()) {
return static::toFluent($conditions)->toDataSource();
}
/**
* Fetches pairs
* @param string $key
* @param string $value
* @param array $conditions
* @return array
*/
public static function fetchPairs($key, $value,$conditions = array()){
return static::toFluent($conditions)
->clause("select",true) // Vymaže současnou klauzuli select
->select("%n",array($key,$value))
->fetchPairs($key, $value);
}
}
Editoval honzakuchar (28. 11. 2009 12:54)
- Honza Marek
- Člen | 1664
Díky :)
Taky jsem teď zkoušel Ormion prakticky nasadit a narazil jsem na tyhle věci:
- řazení… chci implementovat, aby magické metody uměly třeba Page::findAllBySectionAndAllowedOrderByDateDescAndName($section, true).
- možnost definovat složitější podmínky (např. where date < now())
- transparentní omezení výběru podle jazyka bez toho, abych něco nastavoval u každého dotazu (implementovat nějaké výchozí podmínky?)
Vazby na další modely mám v plánu. Nějaká metoda na vracení fluentu tam je, ale nevím jak je použitelná pro tenhle účel. Navíc je protected, což mi ale přijde správné… DibiDataSource.. to mě nenapadlo.
- stpnkcrk
- Generous Backer | 190
DibiDataSource je stále nepoužitelný u MySQL databází, upřímně mi nepřijde moc dobrý nápad používat ho hojně v Ormionu.
- Panda
- Člen | 569
Honza Marek napsal:
možnost definovat složitější podmínky (např. where date < now())
To umí samotné dibi, ne?
$connection->select('*')->from('table')->where('%and', array(
'col' => 'val',
array('%n < %sql', 'date', 'NOW()'),
'col2' => 'val2'
));
SELECT * FROM `table`
WHERE `col` = 'val' AND `date` < NOW() AND `col2` = 'val2'
Čehož jde hezky využívat pro skládání podmínek:
$connection->select('*')->from('table')->where('%or', array(
array('%and', array(
'col1' => 'val1',
'col2' => 'val2'
)),
array('%and', array(
'col1' => 'val2',
'col2' => 'val1'
))
));
SELECT * FROM TABLE
WHERE (`col1` = 'val1' AND `col2` = 'val2')
OR (`col1` = 'val2' AND `col2` = 'val1')
- Honza Kuchař
- Člen | 1662
DibiDataSource je stále nepoužitelný u MySQL databází, upřímně mi nepřijde moc dobrý nápad používat ho hojně v Ormionu.
V Ormionu jsem ho nechtěl používat (alespoň ne interně). Šlo mi o tu možnost DibiDataSource používat. (na věcech co už v současné chvíli na DibiDataSource jedou, např.: datagrid) Jinak já s MySQL už problém nemám, přešel jsem na PotgreSQL. (doufám, že nevyvolám flame :)
- romansklenar
- Člen | 655
Honza:
řazení… chci implementovat, aby magické metody uměly třeba Page::findAllBySectionAndAllowedOrderByDateDescAndName($section, true).
To nedělej, nevypadá to hezky ;) Hezky to má vyřešeno Django pomocí metody filter:
# vybere všechny členy Beatles narozené po 1.1.1961
Person.objects.filter(
group__name = 'The Beatles',
membership__date_joined__gt = date(1961,1,1)
)
[<Person: Ringo Starr]
- Ondřej Mirtes
- Člen | 1536
Ahoj, když mám vztah M:N, jak ho zpracovat v Ormionu?
Konkrétně mi jde o Planety a Budovy na nich – už jsem zvládnul
Planet::getBuildings(), vrátí mi to seznam všech budov a jejich počet na
planetě (včetně těch, které tam nejsou zatím postaveny – počet 0).
Úplně nejpohodlnější by bylo, kdybych mohl volat
$building->count++
a $building->count--
a ono se
to převádělo na insertování/mazání z té spojovací M:N tabulky :)
- Honza Marek
- Člen | 1664
- Nerozumím. Budova může být na více planetách?
- Speciální reakci na ++ a – snad ani nejde zařídit.
- Savannah
- Člen | 30
ad 1) Imho myslí že instance building ti dávají jednotlivé typy budov a instance planet je jednotlivá planeta. A pak máš n:m BuildingOnPlanet, kde ti instance řiká, že daná budova /daný typ budovy/ je na dané planetě (+ případně další atributy, jako kolikrát tam je)
ad 2) taky nevim, že by to v PHP šlo
- Ondřej Mirtes
- Člen | 1536
No bylo by hezké, kdyby to ORM umělo pracovat s takovými případy – kdy mi nejde o jednotlivé položky v tom M:N vztahu, ale jen o jejich počet. Ale možná jsem zvolil špatnou strukturu databáze.
Ale pak jsme rozhodli, že půjde o každou konkrétní budovu, takže
count++ už nepotřebuju :) Pral jsem se ovšem včera s cizími klíči, ale
dibi je ne a ne vidět. Jakým způsobem lze v Ormionu joinovat tabulky, pokud
to jde? Např. mám tabulku buildings_built
, kde je
building_id
a planet_id
a já si chci z
buildings
vytáhnout name
. Volal jsem nad
Building
(které mi slouží pro tabulku buildings_built)
-> name
a nic. Dumpoval jsem a config si žádné cizí klíče
nenačetl. Ale procházel jsem zdrojáky a nic o joinování tam není :(
A v dibi je u cizích klíčů taky někde
NotImplementedException
.
Editoval Ondřej Mirtes (16. 12. 2009 8:25)
- _Martin_
- Generous Backer | 679
Savannah napsal(a):
…že daná budova /daný typ budovy/ je na dané planetě (+ případně další atributy, jako kolikrát tam je)
Možná to je tím, že jsem teď popletený ze Smalltalku, ale: počet budov na planetě přeci není nějaká vlastnost, ale je to počet instancí dané budovy, ne? Ikdyž uznávám, že by s tím asi mělo PHP problémy, stovky malých instancí od každé budovy…
- Savannah
- Člen | 30
_Martin_ napsal(a):
Savannah napsal(a):
…že daná budova /daný typ budovy/ je na dané planetě (+ případně další atributy, jako kolikrát tam je)
Možná to je tím, že jsem teď popletený ze Smalltalku, ale: počet budov na planetě přeci není nějaká vlastnost, ale je to počet instancí dané budovy, ne? Ikdyž uznávám, že by s tím asi mělo PHP problémy, stovky malých instancí od každé budovy…
No to podle mě dost záleží, co chceš udělat. Co psal ráno Ondra jako první případ, tak jestli dělá něco jako onlinovku, kde ho zajímá pouze to, že na planetě A má 20 továren na tanky a 30 továren na jídlo, tak pak už je nesmyslné dávat co budova to instance (řádek v n:m), ale počet budov je spíše vlastností dané planety. A tím, že typy budov se můžou měnit, tak ho nejlépe implementuješ přes n:m, který má navíc atribut „count“.
> Ondřej Mirtes napsal(a):
>
No bylo by hezké, kdyby to ORM umělo pracovat s takovými případy – kdy mi nejde o jednotlivé položky v tom M:N vztahu, ale jen o jejich počet. Ale možná jsem zvolil špatnou strukturu databáze.
Ale pak jsme rozhodli, že půjde o každou konkrétní budovu, takže count++ už nepotřebuju :) Pral jsem se ovšem včera s cizími klíči, ale dibi je ne a ne vidět. Jakým způsobem lze v Ormionu joinovat tabulky, pokud to jde? Např. mám tabulku
buildings_built
, kde jebuilding_id
aplanet_id
a já si chci zbuildings
vytáhnoutname
. Volal jsem nadBuilding
(které mi slouží pro tabulku buildings_built)-> name
a nic. Dumpoval jsem a config si žádné cizí klíče nenačetl. Ale procházel jsem zdrojáky a nic o joinování tam není :( A v dibi je u cizích klíčů taky někdeNotImplementedException
.
Nad jakou databází to máš? Nevim, jak třeba non-InnoDB MySQL tabulky vůbec tyhle věci uznávají a jak to přímo z db jde pak vytáhnout.
- Ondřej Mirtes
- Člen | 1536
Je to v MySQL v InnoDB a ty cizí klíče prostě dibi nevidí. Ale co jsem koukal do Ormionu, tak tam nejspíš nějaké joinování není implementované (nebo jsem koukal hodně špatně a omlouvám se).
Uvítal bych, co mám napsat do createConfig, abych mohl přistupovat k datům i příbuzných tabulek, jestli to jde…
K počtům budov – jojo, jen pro počty budov je M:N asi moc, ale jiná struktura by byla zase náročnější na údržbu… Ale přehodnotili jsme to a budeme brát každou budovu zvlášť (každá bude mít svá nastavení apod.), takže M:N necháme.
Do toho dema (=školní projekt) si potřebuju vybrat budovy, mám
Building::findAllByPlanetId($planetId)
a rád bych pak odkázal na
prvky toho pole ->name
, což je ale položka z tabulky
buildings
a ne buildings_built
, takže bych
potřeboval to spojování.
Jinak se mi Ormion líbí, ale chtělo by to článek, co všechno umí, mám
spoustu otázek, jak tam co dělat (např. jak vybrat prvky
NOT NULL
? !NULL
asi v PHP fungovat nebude).
A myslím, že pomocí toho OrmionConfig by šly pohodlně generovat i formuláře, což je můj sen :)
- Honza Marek
- Člen | 1664
Ondřej Mirtes napsal(a):
Je to v MySQL v InnoDB a ty cizí klíče prostě dibi nevidí. Ale co jsem koukal do Ormionu, tak tam nejspíš nějaké joinování není implementované (nebo jsem koukal hodně špatně a omlouvám se).
Není, jedině si přepsat createFindFluent metodu a něco najoinovat v ní.
Jinak se mi Ormion líbí, ale chtělo by to článek, co všechno umí, mám spoustu otázek, jak tam co dělat (např. jak vybrat prvky
NOT NULL
?!NULL
asi v PHP fungovat nebude).
Mohlo by jít
Tabulka::findAll(array(
array("[sloupec] IS NOT NULL"),
));
A myslím, že pomocí toho OrmionConfig by šly pohodlně generovat i formuláře, což je můj sen :)
To je dobrý nápad.. Dovést to alespoň do stádia hračky by nemuselo být až tak těžké. Zkusím, až mi skončí zápočťák.
- Honza Marek
- Člen | 1664
Takhle: https://github.com/janmarek/Ormion, v addonech je i nějaká základní dokumentace.
- Honza Kuchař
- Člen | 1662
Já to používám, tedy i když v nějaké prehistorické verzi. :-) A je to moc fajn věc. Doporučuji.
- phx
- Člen | 651
Koukam na to a libi se mi to. Ale po prvni euforii me zase napada, ze ciste SQL (nebo DibiFluent) je lepsi na vytahovani dat z DB. Nemuzu si pomoc, ale prijde mi, ze vsechny ORM pohlizeji na data pouze uzce v ramci jedne Entity s nejakyma vazbama na okolni Entity. Bohuzel vetsinou v App kdyz uz neco vypisuji, napr seznam uzivatelu, tak me nezajima jenom jmeno a prijmeni, ale i dalsi veci. Napr pocet clanku, datum posledni aktivity atd. Proste data co se ziskaji z ostatnich tabulek. To je jeste OK, ale pak prijde pozadavek na moznost razeni dle techto informaci. A ORM je v konci. Protoze v tu chvili but ORM nacte vsechno z DB a seradi to v PHP ruco fuco (vykonost musi byt uzasna = hruza des:) a nebo to posle do DB jeden slozity dotaz. V tu chvili mi prijde ORM zbytecne. Alespon co se tyce dolovani dat z DB.
Opravte me pokud mam na to pokriveny pohled. Uz 2 dny premylsim kudy vede cesta a zatim mi nejlepe vychazi kombinace chytry objekt na insert/update/delete + DibiFluent na dolovani dat z DB.
- Honza Marek
- Člen | 1664
Co se týče Ormionu, řazení je v pohodě. To, co se tváří jako kolekce, tak ještě není načtené a dá se to ovlivnit pomocí různých where úplně stejně jako dibifluent. Co se týče efektivního vytahování počtů článků uživatele a tak, přiznávám, že to je už horší.
Jinak v modelu ti asi nic nebrání mít speciální fluenty, které mohou vracet data v těch entitních obálkách ($dibiResult->setRowClass($entityClassName))