Pokus o dibi ORM

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

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…

Jan Jakeš
Člen | 177
+
0
-

U nás je zatím snad jen jeden hosting s testovacím provozem php5.3, to se těžko oželí… Jedině mě napadá vyvíjet dvě různé verze – pro 5.2 a 5.3.

Honza Marek
Člen | 1664
+
0
-

romansklenar napsal(a):

Honza Marek napsal(a):

_Martin_ napsal(a):

  1. 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
+
0
-

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
+
0
-

@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
+
0
-

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
+
0
-

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
+
0
-

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:

  1. 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í)?
  2. 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
+
0
-

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:

  1. 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.

  1. 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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

_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)

_Martin_
Generous Backer | 679
+
0
-

lucass napsal(a):

Jj, princip je stejný a implementace se k dokonalosti dopiluje. A musím říct, že tvé řešení se mi začíná líbit víc, než to naše=)

paranoiq
Člen | 392
+
0
-

@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

_Martin_
Generous Backer | 679
+
0
-

paranoiq napsal(a):

Zkus. Já si to pročetl a mám pocit, že taky řešíme jednu velkou transakci – škoda, že tam to téma zůstalo takové nedořešené.

Jod
Člen | 701
+
0
-

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
+
0
-

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
+
0
-

_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á 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.

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
+
0
-

_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).

paranoiq
Člen | 392
+
0
-

@lucass: globální továrna na modely není špatný nápad, ale nemyslím si, že by měla mít pod palcem kontrolu transakcí. továrna funguje při vytváření a načítání modelů. transakce jsou naproti tomu potřeba především při jejich ukládání

lucass
Člen | 89
+
0
-

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
+
0
-

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.

Jod
Člen | 701
+
0
-

Také veci by šlo vtesnať do ConnectionManageru napríklad, alebo ak ide oto mať čím viac tried tak TransactionManageru :)

_Martin_
Generous Backer | 679
+
0
-

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.

_Martin_
Generous Backer | 679
+
0
-

Jod napsal(a):

Ano, otom som písal

Já to pochopil tak, že tvůj model volá při uložení model nadřazený…?

Hlavne preto, lebo nemá productId :D

Hezká odpověď – měl jsem doplnit, že to je další alternativa uložení, čili to naplnění daty již proběhlo a productId má.

Jod
Člen | 701
+
0
-

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
+
0
-

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:

  1. Možnost převést na DibiDataSource. (např. kvůli datagridu)
  2. 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)
  3. 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
+
0
-

Díky :)

Taky jsem teď zkoušel Ormion prakticky nasadit a narazil jsem na tyhle věci:

  1. řazení… chci implementovat, aby magické metody uměly třeba Page::findAllBySectionAndAllowedOrderByDateDescAndName($section, true).
  2. možnost definovat složitější podmínky (např. where date < now())
  3. 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
+
0
-

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.

Viz https://forum.dibiphp.com/…urce-a-mysql

Panda
Člen | 569
+
0
-

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
+
0
-

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 :)

Honza Marek
Člen | 1664
+
0
-

Panda
To umí samotné dibi, ne?

Super, to jsem nevěděl. Díky!

romansklenar
Člen | 655
+
0
-

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]
lucass
Člen | 89
+
0
-

Zdary,

asi mi něco uniklo, ale co je to ten Ormion? To je název vznikajícího ORM pro Nette? Hledal jsem ve fóru a nikde o tom není zmínka, zato tady se o tom mluví, jako by se nechumelilo:)

Díky za info.

Honza Marek
Člen | 1664
+
0
-

Ano. Je k prohlédnutí a vyzkoušení na githubu.

lucass
Člen | 89
+
0
-

Díkys, mrknu na to.

Ondřej Mirtes
Člen | 1536
+
0
-

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
+
0
-
  1. Nerozumím. Budova může být na více planetách?
  2. Speciální reakci na ++ a – snad ani nejde zařídit.
Savannah
Člen | 30
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

_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 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.

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
+
0
-

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
+
0
-

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.

phx
Člen | 651
+
0
-

Jak to dopadlo s timto projektem?

Honza Marek
Člen | 1664
+
0
-

Takhle: https://github.com/janmarek/Ormion, v addonech je i nějaká základní dokumentace.

Honza Kuchař
Člen | 1662
+
0
-

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
+
0
-

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
+
0
-

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))