Databázová vrstva pro Nette: dibi, Doctrine, NotORM?

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

Doplním, že implementace v Nette nedovoluje rozlišit mezi tím, když chci získat hodnotu sloupce (číslo) a celý navázaný řádek v případě, že se vazební sloupec jmenuje stejně jako tabulka (tedy ne author_id, ale jen author). To jsme s Davidem probírali a vyhodnotili jako celkem nepodstatný problém z těchto důvodů:

  1. Nette může doporučit (kromě dokumentace také např. prostřednictvím skeletonu), aby takový název sloupce lidi nepoužívali.
  2. Získat samotné číslo je potřeba celkem zřídka.
  3. Ono to stejně bude fungovat, protože vrácený řádek prostřednictvím __toString() vrací právě svoje ID, jen se ten řádek získá zbytečně.

Převážila výhoda v tom, že se nemusí přemýšlet, kdy použít -> a kdy [""].

P.S. V NotORM to měnit nebudu.

bene
Člen | 82
+
0
-

Vypadá to hezky.

Pár otázek, poznatků:

  1. Lze někde nadefinovat, aby se pro nějakou tabulku použila má třída dědící z Row? Abych to ale nemusel vždy napsat v dotaze, ale dělo se to automaticky na základě konfigurace.
  2. table($table)->get($id) – předpokládám že pro prim. klíč složený ze dvou sloupců se použije zápis table($table)->get(array('article_id' => 1, 'tag_id' => 2)).
  3. Funguje to stejně jako v NotORM, že při získávání navázaných tabulek se provádějí dotazy s IN (id, id, id)?
  4. Plyne z otázky č. 3 – Pokud ano, lze docílit, aby když vytáhnu třeba 1000 záznamů, se to neprovedlo, ale normálně bych mohl iterovat, aniž bych načetl do paměti naráz 1000 řádků. Zajímalo by mě, jestli tohle nějak řeší i NotORM.
  5. Persistování, mazání – plánují se nějaké události (before/afterDelete/Update/Create)? Pokud ano, pak nebude potřeba už žádne „ORM“.
  6. Docela často potřebuji vytáhnout nějaký sloupec a uložit jej do pole, k tomu by mělo sloužit fetchPairs, ovšem existuje jeden velmi elgantní přístup where(xxx)->user_id vrátí pole ideček. – návrh, pokud to implementováno není.
  7. Plánuje se stejně jako v NotORM „předbíhání budoucnosti“?

Díky

Editoval bene (17. 12. 2010 11:39)

vrana
Člen | 131
+
0
-

bene napsal(a):

Pár otázek, poznatků:

  1. Na rozdíl od NotORM to nejde.
  2. Ne. V takovém případě je potřeba použít table($table)->where('article_id', 1)->where('tag_id', 2)->fetch().
  3. Ano.
  4. Ani v jedné knihovně to nejde. Doporučený způsob je omezit počet ručně pomocí limit(1000). To je ostatně vhodné i při běžném mysql_query().
  5. Metody insert, update a delete přítomné už jsou.
  6. To David nejspíš bude považovat za magii a ani já to do NotORM zařazovat nebudu.
  7. To už by mělo fungovat teď – Connection::$cache může obsahovat Nette\Caching\Cache.
dakota
Člen | 148
+
0
-

Po skopirovaní najnovšej prefixovanej verzie to hlási: Fatal Error – Call to undefined method NConnection::onQuery()

Všimol som si rozdiel pri offset

// $article = $db->table('article')->get(1);
photos: {foreach $article->related('article_photo')->limit(3, 2) as $photo}{$photo->src}, {/foreach}

vykona sa dvakrat offset – použije sa offset v sql LIMIT 3 OFFSET 2 – čo je spravne ale vykona sa offset aj v php, takže to zobrazi ako keby bol offset 4 a nie 2

// $articles = $db->table('article');
{foreach $articles as $article}
photos: {foreach $article->related('article_photo')->limit(3, 2) as $photo}{$photo->src}, {/foreach}
{/foreach}

tu je to dobre – tu sa vykona offset iba v php, pretože sa neda použiť v sql LIMIT 3 OFFSET 2

Tiež sa stracaju hodnoty pri použiti cache Connection::$cache v iteracii

{foreach $article->related('article_2_tag') as $article_tag}{$article_tag->tag->name}, {/foreach}

prvykrat to zobrazi spravne, ale ak sa použije cache tak už nie – hlasi to notice a warning

Editoval dakota (18. 12. 2010 12:35)

norbe
Backer | 405
+
0
-

vrana napsal(a):
5. Metody insert, update a delete přítomné už jsou.

Myslím že dotaz nebyl na existenci metod insert, update a delete ale na existenci událostí, které se spouštějí před a po těchto akcích.

vrana
Člen | 131
+
0
-

dakota napsal(a):

vykona sa dvakrat offset – použije sa offset v sql LIMIT 3 OFFSET 2 – čo je spravne ale vykona sa offset aj v php, takže to zobrazi ako keby bol offset 4 a nie 2

Už jsem to jednou psal, ale asi jsem to zapomněl odeslat – chybu jsem opravil v Nette i v NotORM.

Ostatní připomínky jsem neřešil.

vrana
Člen | 131
+
0
-

norbe napsal(a):

vrana napsal(a):
5. Metody insert, update a delete přítomné už jsou.

Myslím že dotaz nebyl na existenci metod insert, update a delete ale na existenci událostí, které se spouštějí před a po těchto akcích.

Aha. A k čemu se to používá? Věci specifické pro entitu patří do modelu, logování zajišťuje onQuery (v NotORM debug). V NotORM lze navíc vytvořit potomka NotORM_Row, který může tyto metody předefinovat.

dakota
Člen | 148
+
0
-

David Grudl napsal(a):
Na to jsem nenarazil. Můžeš mi poslat nějaký příklad?

Fatal Error – Call to undefined method NConnection::onQuery() mi vypisuje v sandboxe najnovšej verzie Nette už pri $db = new NConnection(…) v renderDefault, nič som v sandboxe nemenil. Zatiaľ som to vyriešil zakomentovaním riadku $this->connection->onQuery($this, $params); v Nette\Database\Statement.

Nette\Database a NotORM su vynikajúce nástroje, páči sa mi automatický left join pri použití where, order zo súvisiacej tabuľky.

V rámci Nette\Database ma napadajú ešte:

  1. možnosť použiť vlastný DatabaseReflection – napr. ak sa mi nevyhovuje používať názvy tabuliek a cudzích kľúčov v tvare tabulka – tabulka_id, prip. množné číslo pri názve tabuľky a jednotné pri cudzom kľúči
  2. možnosť použiť viac cudzích kľúčov na tu istú súvisiacu tabuľku napr. from_country_id, to_country_id, bolo to už preberané na: http://php.vrana.cz/notorm.php#…, myslim ze v NotORM sa to da
  3. automaticky previesť $article = $db->table(‚article‘)->select(‚title, author.name, category.name‘) na $article->title, $article->author->name, $article->category->name pomocou left join (tz. aby sa nevykonali dalšie sql na súviciace tabuľky)

Editoval dakota (20. 12. 2010 21:53)

norbe
Backer | 405
+
0
-

vrana napsal(a): A k čemu se to používá?

Dají se tím vytvořit třídy obstarávající různé typy chování (např. traverzování kolem stromu, verzování db).

Osobně jsem celkem zvědavý jak se tahle část nette vyvine. Doctrine mi přijde zbytečně složitý, NotORM si zase neumim moc představit v situaci, kde mám na několika místech aplikace zobrazovat hodnoty závislé na více db tabulkách.

vrana
Člen | 131
+
0
-

dakota napsal(a):

Chybu onQuery jsem neřešil.

  1. možnosť použiť vlastný DatabaseReflection – napr. ak sa mi nevyhovuje používať názvy tabuliek a cudzích kľúčov v tvare tabulka – tabulka_id, prip. množné číslo pri názve tabuľky a jednotné pri cudzom kľúči
  2. možnosť použiť viac cudzích kľúčov na tu istú súvisiacu tabuľku napr. from_country_id, to_country_id, bolo to už preberané na: http://php.vrana.cz/notorm.php#…, myslim ze v NotORM sa to da

V kódu je to označené jako TODO, takže trpělivost.

3. automaticky previesť $article = $db->table('article')->select('title, author.name, category.name') na $article->title, $article->author->name, $article->category->name pomocou left join (tz. aby sa nevykonali dalšie sql na súviciace tabuľky)

Tohle do NotORM rozhodně dělat nebudu, jde to proti jeho duchu. NotORM s minimálním úsilím programátora vytváří limitně optimální dotazy (někdy dokonce ty úplně nejlepší možné). Takováto konstrukce jednak přidělává práci programátorovi a jednak může výkonnost zhoršit (přenáší se více dat, které zaberou více paměti, navíc to zhoršuje využitelnost query cache MySQL). Nicméně jde napsat select('title, author.name AS author_name') a tím to dostat na stejnou úroveň, většinou to ale ničemu neprospěje.

dakota
Člen | 148
+
0
-

vrana napsal(a):

Tohle do NotORM rozhodně dělat nebudu, jde to proti jeho duchu.

Bol to len nápad, myslel som to ako alternatívu pri načitaní jedneho riadku pomocou ->get(id) kde je niekedy vyhodnejšie použiť left join, na author.name as author_name a automaticky left join som prišiel až po odoslaní príspevku, čo je postačujúce.

dakota
Člen | 148
+
0
-

Ak mám napr. katalog firiem – štruktúra tabuliek

company: id, company_name, ...
category: id, name
company_2_category: company_id, category_id

tz. firmu možem zaradiť do viacerých kategorii

da sa nejako pomocou Nette\Database alebo NotORM efektívne získať z databázy firmy ktoré su zaradené v určitej kategorii, čiže niečo také:

SELECT DISTINCT company.id, company.company_name
FROM company, company_2_category
WHERE company.id = company_2_category.company_id AND company_2_category.category_id = ?
dakota
Člen | 148
+
0
-

dakota napsal(a):

Ak mám napr. katalog firiem – štruktúra tabuliek

company: id, company_name, ...
category: id, name
company_2_category: company_id, category_id

tz. firmu možem zaradiť do viacerých kategorii

da sa nejako pomocou Nette\Database alebo NotORM efektívne získať z databázy firmy ktoré su zaradené v určitej kategorii

$companies = $db->table('company')->where('country.name = ?', 'Slovenská republika')->limit(10);
// vytvori sql: SELECT company.* FROM company INNER JOIN country ON company.country_id = country.id WHERE (country.name = ?) LIMIT 10;

$companies = $db->table('company')->where('company_2_category.category_id = ?', 5)->limit(10);
// vyhodi chybu, čo je zatial logické
// mohlo by to však vytvoriť sql: SELECT company.* FROM company INNER JOIN company_2_category ON company.id = company_2_category.company_id WHERE company_2_category.category_id = ? LIMIT 10;

Editoval dakota (22. 12. 2010 12:09)

vrana
Člen | 131
+
0
-

dakota napsal(a):

da sa nejako pomocou Nette\Database alebo NotORM efektívne získať z databázy firmy ktoré su zaradené v určitej kategorii:

Nejsnáze takto (NotORM syntaxe):

<?php
foreach ($db->company_2_category("category_id", $category_id) as $company_2_category) {
	echo $company_2_category->company["company_name"];
}
?>

Editoval vrana (22. 12. 2010 15:29)

dakota
Člen | 148
+
0
-

takže da sa použiť

foreach ($db->table('company_2_category')->where("category_id = ?", ...)->where('company.country_id = ?', ...)->limit(10) as $company_2_category) {
	echo $company_2_category->company->company_name;
}

pričom to vytvorí

SELECT company_2_category.*
FROM company_2_category INNER JOIN company ON company_2_category.company_id = company.id
WHERE (category_id = ...) AND (company.country_id = ...)
LIMIT 10;

SELECT * FROM company WHERE (company.id IN (...));

Je to trochu iný sposob ako, na ktorý som zvyknutý, hlavne ak raz vypisujem všetky firmy, potom ich mam možnosť filtrovať podla kategorie, štátu … ,
pri všetkych firmách som použil SELECT * FROM company, pri vypise určitej kategorie som použil INNER JOIN company_2_category ON company.id = company_2_category.company_id a podmienku, pripadne ďalšie podmienky napr. štát, … .

Editoval dakota (22. 12. 2010 18:30)

dakota
Člen | 148
+
0
-

vrana napsal(a):

Ešte by som v rámci toho poprosil ako jednoducho potom k týmto firmám vypísať v akých kategóriach je firma zaradená (myslím tým ku každej firme vypisať kategorie)

Editoval dakota (22. 12. 2010 17:17)

bojovyletoun
Člen | 667
+
0
-

<mg src=http://www.notorm.com/static/classes.png

tip
akorád nevím zda jde o nette či notorm

foreach($db->firmy() as $firma){
foreach($firma)->firmy_2_kategorie() as $i){
echo $i->kategorie['jmeno']
}
}

ještě se chci zeptat, asi mi to uniklo, ale nette\Db bude mít stejnou syntaxi jako notorm (základ) nebo jinou?

Tharos
Člen | 1030
+
0
-

Jakube, nestálo by za to k NotORMu zprovoznit nějaké fórum podpory? Přeci jenom způsob přemýšlení při práci s ním se lehce liší od způsobu přemýšlení při práci s SQL. Takové fórum by mohlo být dobrým zdrojem inspirací, co a jak lze v NotORMu udělat.

dakota
Člen | 148
+
0
-

NotORM a Nette\Database maju odlišnú syntax v zápise, napr. NotORM $db->tablename(), Nette\Database $db->table(tablename).

Kategórie sa daju vypisať takto:

foreach ($company_2_category->company->related('company_2_category') as $category) {
	echo $category->category->name.', ';
}

Myslel som si že sa da použiť zápis, kde by som na zaklade formulára pridaval jednotlivé podmienky

$companies = $db->table('company');
// všetky firmy
$companies = $db->table('company')
	->where('company_2_category.category_id = ?', 5); // toto nie je možné
// firmy, ktoré su zaradené v kategorii 5
$companies = $db->table('company')
	->where('company_2_category.category_id = ?', 5) // toto nie je možné
	->where('company.country_id = ?', 22);
// firmy, ktoré su zaradené v kategorii 5 a sú z krajiny 22

// dalšie podmienky

v šablone potom:

{foreach $companies->order('company.company_name')->limit(50) as $company}
<div>
	<h1>{$company->company_name}</h1>
	<p>{$company->country->name}</p>
	<ul>
		{foreach $company->related('company_2_category') as $category}
		<li>{$category->category->name}</li>
		{/foreach}
	</ul>
</div>
{/foreach}

a že to vytvorí sql (firmy, ktoré su zaradené v kategorii 5 a sú z krajiny 22):

SELECT company.* FROM company
INNER JOIN company_2_category ON company.id = company_2_category.company_id
WHERE company_2_category.category_id = 5
AND company.country_id = 22
ORDER BY company.company_name
LIMIT 50;

Editoval dakota (23. 12. 2010 8:54)

bojovyletoun
Člen | 667
+
0
-

Nevíte jak v pomocí tohoto nástroje vypsat „kontigenční tabulku“? Příklad:
tabulka člověk: jméno, id.
tabulka země: jméno, id.
tabulka vazba: clovk_id, zeme_ id.
Zo brazí se tablulka asi taková

X Počet Josef Eva Pavla
Počet X 2 0 3
USA 2 Y N Y
China 2 Y N Y
Japan 1 N N Y

V dibi jsem to vyřešil pomocí tří dotazů (sice krkolomných

Tyto tři dotazy(jejich výsledky) v šabloně byli proměnné: pozn. první a druhý dotaz jsou totožné

dibi::select("jmeno,id, count(id)as ucast")->
			from("lidi")->leftJoin("vazba")->on("clovek_id=id")->groupBy("id")->fetchAll();

dibi::select("jmeno,id,count(id) as ucast")->
			from("zeme")->leftJoin('vazba')->on("zeme_id=id")->groupBy("zeme.id")->fetchAll();


template->exists=dibi::select("clovek_id,zeme_id, 1 as m")->from("vazba")->fetchAssoc("clovek_id|zeme_id=m");

v šabloně klasicky foreache a :

  • $zeme->jmeno, $clovek->jmeno, nadpisy
  • $akce->ucast , $clovek->ucast pro sumy
  • isset(exists[$clovek->id][$zeme->id]?„ano“:„ne“ pro jednotlivé bunky

otázka je, jak toto zapsat pro \Nette\Db? (aniž bych měl 47 dotazů) (mimochodem toto se mi stalo
pokud nepoužiji na konci fetchall)

Editoval bojovyletoun (24. 12. 2010 18:09)

Filip Procházka
Moderator | 4668
+
0
-

tohle je velice specifická potřeba a myslím si, že tvoje řešení je asi nejoptimálnější

Tharos
Člen | 1030
+
0
-

Bug (tentokrát již ne PHP): zde jsou prohozené parametry funkce strpos, takže momentálně příkazy, které mají jít přes preprocessor jenom kvůli substitucím, přes preprocessor vůbec nejdou.

Související issue: když se pořadí parametrů opraví, na preprocessor se posílají už i příkazy generované v konstruktoru driveru (například u MySQL driveru příkaz SET time_zone='+01:00', protože obsahuje dvojtečku). V době, kdy se ale instance driveru vytváří, ještě není u connection preprocessor k dispozici. Rychlý workaround: tohle by mělo být na řádku 52. :)

dakota
Člen | 148
+
0
-

Ja sa zase pridávam k väčšej podpore spájania tabuliek pre použitie podmienok vo where, napr.

$companies = $db->table('company')
        ->where('company_2_category.category_id = ?', 5) // toto nie je možné
        ->where('company.country_id = ?', 22);

Tiež k lepšej efektivite pri zistení počtu.

$company_2_category = $db->table('company_2_category')
		->where("category_id = ?", ..)
		->where('company.country_id = ?', ...);

$totalCount = $company_2_category->count();
// načíta z databázy údaje aby sa zistil počet
$totalCount = $company_2_category->count('company_2_category.company_id');
// sa nedá použiť, nezohľadňuje where z cudzej tabuľky

foreach ($company_2_category->limit(50, 100) as $c2c) {
       echo $c2c->company->company_name;
}

Editoval dakota (27. 12. 2010 10:25)

vrana
Člen | 131
+
0
-

dakota napsal(a):

Ja sa zase pridávam k väčšej podpore spájania tabuliek pre použitie podmienok vo where, napr.

Tečková notace se používá pro připojení tabulek ve vztahu 1:N. Nejde ji tedy použít pro připojení tabulek ve vztahu M:N. Je potřeba změnit uvažování, jak už jsem popisoval.

Tiež k lepšej efektivite pri zistení počtu.

Pro použití agregační funkce je potřeba předat metodě parametr, např. tedy count("*").

dakota
Člen | 148
+
0
-

vrana napsal(a):
Tečková notace se používá pro připojení tabulek ve vztahu 1:N. Nejde ji tedy použít pro připojení tabulek ve vztahu M:N. Je potřeba změnit uvažování, jak už jsem popisoval.

V príklade ktorý som uviedol – ak chcem získať firmy z určitej kategórie (firmy môžu byť zaradené vo viacerých kategóriach) a súčasne z určitého štátu môžem v Nette\Database použiť tento postup príp. nejaky iný?

$company_2_category = $db->table('company_2_category')
                ->where("category_id = ?", ...)
                ->where('company.country_id = ?', ...);

foreach ($company_2_category->limit(50, 100) as $c2c) {
       echo $c2c->company->company_name;
}

Pro použití agregační funkce je potřeba předat metodě parametr, např. tedy count("*").

Možno som sa zle vyjadril, chcel som tým poukazať na: ak pripojím tabuľku uvedením tabuľky vo where, tak pre zistenie počtu nemožem použiť count s parametrom pretože to vyhodi chybu (v dotaze pre zistenie počtu nedojde k pripojeniu tabuľky pričom vo where je uvedená), v tomto pripade funguje len count() bez parametru.

Editoval dakota (28. 12. 2010 13:16)

David Grudl
Nette Core | 8218
+
0
-

Trošku se nám to tu zvrhává v mišmaš.

Tady bych to asi trošku s dovolením promazal.

Ještě k problému s PHP 5.2 a onQuery: považoval jsem ho za vyřešený od 21. prosince, ale jak se zdá, ve verzí 5.2.6 a nižších to stále nefunguje, kvůli jinému bugu v PHP. Fixed.

David Grudl
Nette Core | 8218
+
0
-

Tharos napsal(a):

zde jsou prohozené parametry funkce strpos
Související issue…

fixed

dakota
Člen | 148
+
0
-

dakota napsal(a):

Pro použití agregační funkce je potřeba předat metodě parametr, např. tedy count("*").

Možno som sa zle vyjadril, chcel som tým poukazať na: ak pripojím tabuľku uvedením tabuľky vo where, tak pre zistenie počtu nemožem použiť count s parametrom pretože to vyhodi chybu (v dotaze pre zistenie počtu nedojde k pripojeniu tabuľky pričom vo where je uvedená), v tomto pripade funguje len count() bez parametru.

napišem to ešte tu v podstate to len zopakujem – možte to potom zmazať, vidím chybu v count ak je pripojená tabulka pomocou where, tak count('*') nefunguje – týka sa fungujúceho zápisu v Nette\Database.

napr.

$company_2_category = $db->table('company_2_category')
                ->where("category_id = ?", ...)
                ->where('company.country_id = ?', ...);

$company_2_category->count('*') // vyhodí chybu

Editoval dakota (30. 12. 2010 14:17)

Aurielle
Člen | 1281
+
0
-

David Grudl napsal(a):

  • příspěvky týkající se NotORM asi nejlépe na http://forum.php7.org (nebo existuje fórum pro NotORM?)

Whois píše Domain is for sale + je tam jen defaultní hostmonsterovská stránka…

vrana
Člen | 131
+
0
-

dakota napsal(a):

ak je pripojená tabulka pomocou where, tak count('*') nefunguje – týka sa fungujúceho zápisu v Nette\Database.

Konečně jsem to pochopil a opravil v NotORM (v Nette ne).

gawan
Člen | 110
+
0
-

vrana napsal(a):

Konečně jsem to pochopil a opravil v NotORM (v Nette ne).

chcem sa vás (Jakub, David) opýtať akým spôsobom budete udržiavať NotORM a Nette\Database? Opravy a nové vlastnosti budete commitovať do oboch alebo \Nette\Database bude len „chodobnejší príbuzný“ resp. oklieštená verzia NotORM?

vrana
Člen | 131
+
0
-

gawan napsal(a):

chcem sa vás (Jakub, David) opýtať akým spôsobom budete udržiavať NotORM a Nette\Database? Opravy a nové vlastnosti budete commitovať do oboch alebo \Nette\Database bude len „chodobnejší príbuzný“ resp. oklieštená verzia NotORM?

Mě se moc nechce udržovat něco, co sám nepoužívám. A Nette\Database se alespoň prozatím používat nechystám. Takže já se budu nadále soustředit na NotORM a je možné, že se oba projekty časem více rozejdou. Nevylučuji, že některé změny v NotORM budu dělat i do Nette\Database, ale jako svou „práci“ to nechápu.

krissott
Člen | 48
+
0
-

Jakube, kdy myslíš, že by mohl být poslední videotutoriál (Model definition – M from MVC) venku? Přemýšlím, že použiju NotORM, nebo spíše jeho Nette verzi ve větším projektu, rád bych shlédl tento tutoriál, než bych se do projektu pustil. Díky …

vrana
Člen | 131
+
0
-

krissott napsal(a):

Jakube, kdy myslíš, že by mohl být poslední videotutoriál (Model definition – M from MVC) venku?

Doufám, že si na to najdu čas během ledna.

David Grudl
Nette Core | 8218
+
0
-

gawan napsal(a):

chcem sa vás (Jakub, David) opýtať akým spôsobom budete udržiavať NotORM a Nette\Database? Opravy a nové vlastnosti budete commitovať do oboch alebo \Nette\Database bude len „chodobnejší príbuzný“ resp. oklieštená verzia NotORM?

Bugfixy v NotORM budu portovat do Nette\Database, snad jich moc nebude :-)

Každopádně Nette\Database\Selector nepovažuju za chudobného príbuzného NotORM, ba právě naopak, mělo by jít o jakési „bestof“ z NotORM a dibi. Takže Selector umí navíc automaticky ošetřovat identifikátory, má (z mého pohledu) srozumitelnější API, integrovaný Debug panel atd.

dakota
Člen | 148
+
0
-

V prípade štruktúry tabuliek:

offer: id, company_id, contact_person_id, title, ...
contact_person: id, company_id, first_name, surname, ...
company: id, company_name, ...

Uživateľ može ale nemusí uviesť pri ponuke kontaktnú osobu.
V prípade uvedenia kontaktnej osoby sa v tabuľke offer v contact_person_id uloží id kontaktnej osoby (dátový typ int).
V prípade neuvedenia kontaktnej osoby bude v contact_person_id 0 (alebo NULL).

$offer = $this->db->table('offer')->get(199);
echo 'Offer: ' . $offer->title.'<br>';
echo 'Company: ' . $offer->company->company_name.'<br>';
if ($offer->contact_person) {
	echo 'Contact person: ' . $offer->contact_person->first_name . ' ' . $offer->contact_person->surname.'<br>';
}

1. možnosť – neuvedená kontaktná osoba – v contact_person_id je NULL (nastavené default NULL pri contact_person_id), vykoná sa dotaz:

SELECT `id`, `surname`, `first_name` FROM `contact_person` WHERE (`contact_person`.`id` IN (''))
// v IN je ''

2. možnosť – neuvedená kontaktná osoba – v contact_person_id je uvedené 0, vykoná sa dotaz:

SELECT `id`, `surname`, `first_name` FROM `contact_person` WHERE (`contact_person`.`id` IN (0))
// v IN je 0

Chcem sa spýtať či sa v tomto príklade v prípade neuvedenia kontaktnej osoby musí v Nette\Database vykonať dotaz na zistenie údajov o kontaktnej osobe pre danú položku (ponuka), keď už z contact_person_id je patrné že daná položka (ponuka) kontaktnú osobu nemá.

Tiež sa chcem spýtať, síce to nesúvisí s Nette\Database, čo je lepšie ukladať do contact_person_id (dátový typ int), ak neuvediem kontaktnú osobu pri danej položke: 0 alebo NULL (neznáma hodnota). V súčasnosti používam 0, pretože sa mi to zdá správnejšie.

Uvedenú štruktúru tabuliek je síce možné zmeniť na (z pohľadu normalizácie je správnejšia, ale zložitejšia):

offer: id, company_id, title, ...
contact_person: id, company_id, first_name, surname, ...
offer_contact_person: offer_id, contact_person_id
company: id, company_name, ...

Editoval dakota (4. 1. 2011 15:18)

Tharos
Člen | 1030
+
0
-

dakota napsal(a):

Tiež sa chcem spýtať, síce to nesúvisí s Nette\Database, čo je lepšie ukladať do contact_person_id (dátový typ int), ak neuvediem kontaktnú osobu pri danej položke: 0 alebo NULL (neznáma hodnota). V súčasnosti používam 0, pretože sa mi to zdá správnejšie.

Správnější je samozřejmě null. Kontaktní osoba prostě není nadefinována. 0 znamená, že se záznamem souvisí kontaktní osoba s ID 0.

Edit: Zvláště pak při použití cizích klíčů bys s 0 použitou tímto způsobem už vůbec nepochodil.

Editoval Tharos (4. 1. 2011 13:11)

bojovyletoun
Člen | 667
+
0
-
  • ukládal bych** null** namísto 0, protože je to IMHO správnější, znamenáto, „hodnota nebyla nastavena“
  • neřešíš toto , 2 ?
  • jestli to správně chápu, tak pro referenced row se používá $offer->ref(‚company‘) A nebo $offer->company se stejným významem? (z metody __&get v selectoru)
dakota
Člen | 148
+
0
-

Tharos napsal(a):
Správnější je samozřejmě null. Kontaktní osoba prostě není nadefinována. 0 znamená, že se záznamem souvisí kontaktní osoba s ID 0.

Vďaka za radu asi to upravím na NULL. ID 0 sa mi zdá vzláštne ale možné to je.

Pri NULL to pre získanie údajov o kontaktnej osobe v Nette\Database položí dotaz:

SELECT `id`, `surname`, `first_name` FROM `contact_person` WHERE (`contact_person`.`id` IN (''))

pripadne ak viac kontaktných osôb

SELECT `id`, `surname`, `first_name` FROM `contact_person` WHERE (`contact_person`.`id` IN ('', 5, 8 ))

neviem či je to v poriadku alebo nie

Editoval dakota (4. 1. 2011 15:15)

dakota
Člen | 148
+
0
-

bojovyletoun napsal(a):

  • jestli to správně chápu, tak pro referenced row se používá $offer->ref(‚company‘) A nebo $offer->company se stejným významem? (z metody __&get v selectoru)

Myslím si že $offer->ref(‚company‘) a $offer->company majú rovnaký význam

Edit: Prečo ma metóda ref() tak krátky názov? Nebolo by vystižnejšie referenced() alebo referencedRow()?

Editoval dakota (4. 1. 2011 14:59)

vrana
Člen | 131
+
0
-

dakota napsal(a):

Chcem sa spýtať či sa v tomto príklade v prípade neuvedenia kontaktnej osoby musí v Nette\Database vykonať dotaz na zistenie údajov o kontaktnej osobe pre danú položku (ponuka), keď už z contact_person_id je patrné že daná položka (ponuka) kontaktnú osobu nemá.

Dobrý postřeh. Upravil jsem NotORM i Nette\Database tak, aby se na odkaz na hodnotu NULL neptaly.

dakota
Člen | 148
+
0
-

vrana napsal(a):
Dobrý postřeh. Upravil jsem NotORM i Nette\Database tak, aby se na odkaz na hodnotu NULL neptaly.

Vďaka za rýchlu reakciu. Chcel by som ešte upozorniť na:

Chyba v count() pri neexistujúcom zázname – https://forum.nette.org/…ase-selector

Nefungujúci JOIN na základe určitého zápisu where – https://forum.nette.org/…ase-selector

Neviem či je chyba aj v NotORM alebo len v Nette\Database.

vrana
Člen | 131
+
0
-

dakota napsal(a):

Chyba v count() pri neexistujúcom zázname – https://forum.nette.org/…ase-selector

Díky za report. Opravil jsem to v NotORM, v Nette ne.

Nefungujúci JOIN na základe určitého zápisu where – https://forum.nette.org/…ase-selector

V NotORM se to neprojevuje, v Nette jsem to nezkoušel.

dakota
Člen | 148
+
0
-

Ako mám správne postupovať v prípade použitia GROUP pri zistení celkového počtu pre stránkovanie?

Napríklad:

$cs = $this->db->table('company_2_category')
		->group('company_id')
		->select('company_id, COUNT(category_id) AS category_count');

$totalCount = $cs->count('*');

foreach($cs->limit(30, 60) as $c) {
	echo $c->company->company_name.' - '.$c->category_count.'<br>';
};

Použitie count() bez parametru – je neefektívne pri veľkom počte záznamov.

Pri count('*') sa vykoná dotaz:

SELECT COUNT(*) FROM `company_2_category`

Možno by sa dala metoda count upraviť aby v prípade GROUP použila tento dotaz aj ked nie je až taký efektívny ale iný ma nenapadá:

SELECT COUNT(*) FROM (SELECT company_id, COUNT(category_id) FROM company_2_category GROUP BY company_id) AS tmp

Ešte je tu možnosť v tomto príklade ukladať do tabuľky company počet kategorii – využiteľné pri zotriedení podľa počtu kategorii.

dakota
Člen | 148
+
0
-

David Grudl napsal(a):

Každopádně Nette\Database\Selector nepovažuju za chudobného príbuzného NotORM, ba právě naopak, mělo by jít o jakési „bestof“ z NotORM a dibi. Takže Selector umí navíc automaticky ošetřovat identifikátory, má (z mého pohledu) srozumitelnější API, integrovaný Debug panel atd.

Mám zopár otázok ohľadom budúceho vývoja Nette\Database.

Bude sa Nette\Database príp. Nette\Database\Selector v blízkej budúcnosti rozširovať o nejaké dalšie metódy, príp. možnosti, nástroje – napr. o niektoré veci z dibi?

Uvažuje sa napr. o doplnenie manuálneho JOIN v rámci Nette\Database\Selector (popri automatickom JOIN) alebo o rozšírení automatického JOIN o tabulky vo vzťahu M:N?. V rámci testovania by som v určitých prípadoch použil iné spojenie tabuliek aké mi to Nette\Database\Selector automaticky vytvorí, napr. v rámci pridavania podmienok na základe určitého filtra – uvádzal som to už v predošlých príspevkoch.

Uvažuje sa o prepacovaní, zefektívnení a zaradení DibiFluent (príp. inej podoby skladania dotazov) do Nette\Database? Aby sa v určitých prípadoch nemuselo popri Nette\Database používať aj dibi. Zaujímavá by bola napr. kombinácia DibiFluent (resp. kombinácia DibiFluent a Nette\Database\Selector) pri skladaní dotazu a Nette\Database\Selector pri získavaní súvisiacich záznamov.

Je možné konkrétnejšie uviesť aká ďalšia podpora modelu sa popri Nette\Database v Nette pripravuje? V úvodnom príspevku je slabo načrtnuté čo sa v rámci podpory modelu pripravuje.

Editoval dakota (4. 1. 2011 22:22)

dakota
Člen | 148
+
0
-

Chcem sa tiež spýtať na efektivitu dotazov pri Nette\Database\Selector.

Vo väščine prípadov sa mi zdá Nette\Database\Selector (NotORM) efektívne a pri skúšaní som nenarazil na žiadny väčší problém týkajúci sa efektívnosti a možné problémy plynuli skôr z neznalosti použitia napr. pri count. Tiež sa mi parkrát stalo že mi pri výpise väčšieho počtu záznamov zabralo dosť z pamäti príp. mi pamäť nestačila.

Sú nejaké limity, pri ktorých môže mať Nette\Database\Selector (NotORM) problem s efektívnosťou? Alebo sú nejaké odporúčania kedy Nette\Database\Selector (NotORM) použiť s opatrnosťou?

Napríklad

  • vzťah 1:1 alebo vzťah ktorý sa blíži k 1:1, niekedy sa mi zdá že by bolo v tomto prípade lepšie načitať data už pomocou JOIN
  • poddotazy v MySQL – tie sa pri MySQL prevádzajú na IN ( id1, id2, … ). Ak by poddotaz vrátil niekoľko stoviek id možem to v MySQL použiť?
  • výpis veľkého počtu záznamov – odporúča sa limit(1000)
  • opakované použitie IN ( … ) s niekoľko desiatkami id pre získanie rôznych údajov
  • možná zvýšena réžia na strane php pri získavaní mnohých súvisiacich záznamov
  • väčšie množstvo previazaných tabuliek, niekoľko stotisíc záznamov v tabuľkách

Neistota sa však týka hlavne klauzuly IN a vzťahov blížiacich sa k 1:1. Je nejaký limit počtu id v IN pokiaľ to je ešte efektívne? Žiadne rapidne zhoršenie efektívnosti som zatiaľ pri skúšaní nespozoroval.

Výhody Nette\Database\Selector (NotORM) týkajúce sa efektívnosti sa uvádzajú: Nette\Database\Selector (NotORM) kladie konštantný počet dotazov (záznamy získa pomocou IN), kladie jednoduché dotazy, využíva sa databazová cache, neprenášajú sa zbytočne duplicitné záznamy atď. Výhoda je aj to že sa nemusia ručne písať dotazy na získanie dát zo spojených tabuliek.

Nette\Database\Selector (NotORM) by mohol tiež poskytnúť možnosť nejakym spôsobom upraviť správanie v prípadoch i mierne zhoršenej efektívnosti v určitých prípadoch a tým zvýšiť efektívnosť.

Editoval dakota (5. 1. 2011 10:54)

vrana
Člen | 131
+
0
-

dakota napsal(a):

Tiež sa mi parkrát stalo že mi pri výpise väčšieho počtu záznamov zabralo dosť z pamäti príp. mi pamäť nestačila.

Obrovské výsledky je vhodné stahovat postupně, např. pomocí limit(1000). To je ostatně vhodné i při použití běžných dotazů, protože i u nich se nejprve celý výsledek přenese a uloží do paměti (kterou PHP nezapočítává do limitu, takže to není tak dobře vidět, ale zabraná paměť to je). Viz What about memory consumption?.

vzťah 1:1 alebo vzťah ktorý sa blíži k 1:1, niekedy sa mi zdá že by bolo v tomto prípade lepšie načitať data už pomocou JOIN

Samozřejmě, k tomu se dá použít select("tab.col"). Jen by někdy mohl být problém s názvy sloupců, to se dá v NotORM vyřešit vlastní Structure.

poddotazy v MySQL – tie sa v MySQL prevádzajú na IN ( id1, id2, … ). Ak by poddotaz vrátil niekoľko stoviek id možem to v MySQL použiť?

Ano, bohužel to je pořád lepší, než přímé vložení poddotazu.

možná zvýšena réžia na strane php pri získavaní mnohých súvisiacich záznamov

V tomhle by k problémům dojít nemělo. Související záznamy se ukládají do PHP polí, která mají konstantní čas přístupu při libovolném počtu záznamů.

Nette\Database\Selector (NotORM) by mohol tiež poskytnúť možnosť nejakym spôsobom upraviť správanie v prípadoch i mierne zhoršenej efektívnosti v určitých prípadoch a tým zvýšiť efektívnosť.

Máš na mysli něco konkrétního? I tak se do toho asi pouštět nebudu, protože chci, aby chování zůstalo předvídatelné, NotORM nemá být blackbox.

dakota
Člen | 148
+
0
-

vrana napsal(a):

Nette\Database\Selector (NotORM) by mohol tiež poskytnúť možnosť nejakym spôsobom upraviť správanie v prípadoch i mierne zhoršenej efektívnosti v určitých prípadoch a tým zvýšiť efektívnosť.

Máš na mysli něco konkrétního? I tak se do toho asi pouštět nebudu, protože chci, aby chování zůstalo předvídatelné, NotORM nemá být blackbox.

V tomto prípade som mal na mysli napr. toto:

$company_2_category = $db->table('company_2_category')
                ->where("category_id", ...)
                ->where('company.country_id', ...);

foreach ($company_2_category->limit(500) as $c2c) {
       echo $c2c->company->company_name;
	echo $c2c->company->country->name;
}

vidím to ako vzťah 1:1 – čiže z prveho dotazu sa 500 id premietne do IN ( 500 id ), zámerne som uviedol väčší počet ale rovnako je to aj pri limit(50), tak to pre lepšiu efektivitu zapišem takto:

$company_2_category = $db->table('company_2_category')
                ->where("category_id", ...)
                ->where('company.country_id', ...);

$company_2_category->select('company_2_category.company_id, company.company_name, company.country_id');

foreach ($company_2_category->limit(500) as $c2c) {
	echo $c2c->company_name;
	// chcem zapisať aj názov krajiny ale nemam ako jedine znovu použiť
	echo $c2c->company->country->name;
}

V tomto prípade mi fakt chýba pripojenie tabuľky typu SELECT ... FROM company INNER JOIN company_2_category ON ... alebo nejaký iný spôsob ako zvýšiť efektivitu. Ak je to len môj iný pohľad alebo iný spôsob myslenia tak to považuj za bezpredmetné. Sú to možno špecifické potreby ale môžu sa vyskytnúť.

Mam tiež chápať aj to že: poddotaz v MySQL s niekoľko tisíc vrátenými id asi radšej nepoužívať.

Editoval dakota (5. 1. 2011 11:15)

vrana
Člen | 131
+
0
-

dakota napsal(a):

Ako mám správne postupovať v prípade použitia GROUP pri zistení celkového počtu pre stránkovanie?

Nejefektivnější je v takovém případě položit extra dotaz, v tvém případě table("company_2_category")->count("DISTINCT company_id").

Zkratku pro to asi dělat nebudu, protože by se to poněkud zkomplikovalo třeba v případě count("DISTINCT x") nebo dokonce max("tab2.y") a ještě víc u MultiResult.

dakota
Člen | 148
+
0
-

vrana napsal(a):

Nejefektivnější je v takovém případě položit extra dotaz, v tvém případě table("company_2_category")->count("DISTINCT company_id").

len malá poznámka: distinct som vyskúšal funguje v tomto prípade rovnako, ale týmto sa v podstate ignoruje GROUP BY … HAVING …, možno tie konštrukcie nie sú časté ale môžu sa vyskytnúť.

Edit:

Ono vo všeobecnosti je niekedy problém s efektivitou pri GROUP BY a tiež DISTINCT napr. pri zistení celkového počtu. Možno by som to nechal tak ako je to teraz implementované, neviem či však s tým nebudú niekedy problémy alebo nejasnosti.

Editoval dakota (5. 1. 2011 12:01)