Nette\Database a přístup k prvkům přes vazbu M:N

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

Lze nějak jednoduše v Nette\Database přistoupit k něčemu, co je navázané přes M:N?

Jediné funkční řešení, na které jsem přišel, je toto:

$userLanguages = array();
foreach ($connection->table('user')->where('id = ?', $id)->related('user_language') as $ul) {
	$userLanguages[] = $ul->ref('language');
}
return $userLanguages;

Nechce se mi ale psát foreach kvůli každému dotazu na prvky z M:N vazby.

Nebo umí to NotORM stručněji?

Tharos
Člen | 1030
+
0
-

Co přesně bys potřeboval? V NotORMu by to dost možná vyřešila tzv. tečková notace, která vynutí JOIN. Prostě chceš řádky z nějaké tabulky X filtrované podle hodnot z tabulky Y (mezi nimi je vazba M:N) bez toho, abys použil foreach, je to tak?

Tharos
Člen | 1030
+
0
-

Takže v NotORM by to řešil následující super stručný zápis:

$userLanguages = $NotORM->user_language('user_id', $id)->select('language.*');

Edit: V Nette\Database by to mělo jít ekvivalentně (podle dokumentace tu tečkovou notaci taky podporuje).

Editoval Tharos (20. 2. 2012 22:39)

Honza Marek
Člen | 1664
+
0
-

Jednoduše. Mám dejme tomu toho uživatele a chci znát jeho jazyky. Jsou spojený vazební tabulkou.

Budu něco podobného potřebovat často, tak se ptám, jestli na to existuje nějaká stručná syntaxe.

<small>Tip s tečkovou syntaxí mi generuje v SQL nějaké nesmysly. Myslím, že to není to, co potřebuju.</small>

Tharos
Člen | 1030
+
0
-

Možná je tečková notace v Nette\Database bugová, protože NotORM se zmíněným zápisem vygeneruje následující dotaz:

SELECT language.*
FROM user_language
LEFT JOIN language ON user_language.language_id = language.id
WHERE (user_id = ?)

Což by mělo být přesně to, co potřebuješ…

Editoval Tharos (20. 2. 2012 23:18)

Honza Marek
Člen | 1664
+
0
-
$connection->table('user_language')->where('user_id = ?', $id)->select('language.*');

v Nette dopadne jako

SELECT `language`.*
FROM `user_language`
WHERE (`user_id` = ?)
Tharos
Člen | 1030
+
0
-

Hmm, tak to je blbý… Tohle evidentně není z NotORMu naportované správně. No, ještě můžeš zkusit, jestli to třeba nějak nesouvisí s tímhle bugem. Je to fakt debilní rada, ale zkus přidat na konec všech řetězců mezeru…

Tharos
Člen | 1030
+
0
-

Hele, tak tohle by mělo fungovat (vyzkoušeno):

$connection->table('user_language')->where('user_id = ?', $id)->select('language.id, language.*');

Nette\Database je třeba trochu pomoci v té select části… Každopádně je to ale bug.

Editoval Tharos (20. 2. 2012 23:48)

hrach
Člen | 1834
+
0
-
  • díky za nalezení bugu
  • správná „use case“ pro M:N je tato – kod, ktery vy celou dobu resite je jenom 1:N, pac resite jen polovinu vztahu M:N. Tedy M:N se dela takto:
foreach ($db->table('users') as $user) {
	echo $user->name;
	foreach ($user->related('user_language') as $lang) {
		echo $lang->lang->name;
	}
}

Editoval hrach (21. 2. 2012 11:58)

nanuqcz
Člen | 822
+
0
-

Tohle je jedna z mála věcí, co se mi na Nette\Database / NotORM nelíbí. Mít tohle v šabloně není moc pěkné

{foreach $user->related('user_language') as $lang}
        {$lang->lang->name}
{/foreach}
Tharos
Člen | 1030
+
0
-

hrach napsal(a):

  • díky za nalezení bugu
  • správná „use case“ pro M:N je tato – kod, ktery vy celou dobu resite je jenom 1:N, pac resite jen polovinu vztahu M:N. Tedy M:N se dela takto:
foreach ($db->table('users') as $user) {
	echo $user->name;
	foreach ($user->related('user_language') as $lang) {
		echo $lang->lang->name;
	}
}

Tohle je ale docela jiný use case. Ty vypisuješ dostupné jazyky pro všechny uživatele, zatímco Honzu zajímají jazyky jenom jednoho uživatele. Pak je přece úplně zbytečné ten vnější foreach, který by se sestával z jedné iterace, psát…

Editoval Tharos (21. 2. 2012 12:47)

hrach
Člen | 1834
+
0
-

Tharos: ano, ale to je pak vazba 1:N, ne M:N. Proto sem to tam napsal, nicméně jeho přístup se může velmi rychle změnit a bude muset použít moji verzi, například:

foreach ($db->table('users')->where('id', array(1,2,10,18,23)) as $user) {
...
Tharos
Člen | 1030
+
0
-

nanuqcz napsal(a):

Tohle je jedna z mála věcí, co se mi na Nette\Database / NotORM nelíbí. Mít tohle v šabloně není moc pěkné

{foreach $user->related('user_language') as $lang}
        {$lang->lang->name}
{/foreach}

Presenter:

// ...
$this->template->userLanguages = $NotORM->user_language('user_id', $id)->select('language.*');
// ...

Šablona:

{foreach $userLanguages as $userLanguage}
{$userLanguage[id]}
{/foreach}

Nevidím v tom nic ošklivého. :) No a třeba v mé aplikaci s takovou velmi tenkou nadstavbou nad NotORMem by byl kód v presenteru následující:

// ...
$this->template->userLanguages = $NotORM->getFacade('user')->findAllUserLanguages($userId);
// ...

Ta tenká nadstavba spočívá v možnosti definic různých fasád, které zjednodušují a hlavně zčitelňují různé zápisy. Prostě preferuji například namísto tohoto kódu:

$pages = $NotORM->page('id', $NotORM->page()->select('MAX(id) id')->group('parent, url');

zápis:

$pages = $NotORM->getFacade('page')->findHeadRevisions();

Pak mi to přijde úplně super. Ale jsme off topic. :)

Editoval Tharos (21. 2. 2012 13:09)

Tharos
Člen | 1030
+
0
-

hrach napsal(a):

Tharos: ano, ale to je pak vazba 1:N, ne M:N. Proto sem to tam napsal, nicméně jeho přístup se může velmi rychle změnit a bude muset použít moji verzi, například:

foreach ($db->table('users')->where('id', array(1,2,10,18,23)) as $user) {
...

Pak samozřejmě ano.

Nicméně já ve svých aplikacích čtení pouze části M:N vazby využívám velmi často, protože to opravdu šetří řádky, zbytečný foreach a jsou případy, kdy je pravděpodobnost, že budu vůbec někdy na dané stránce potřebovat číst například více uživatelů zároveň, je mizivá (editace uživatele v backendu → chci mít přehled o jeho jazycích, ale vždy budu editovat pouze jednoho uživatele v daný moment atd.).

Honza Marek
Člen | 1664
+
0
-

Tharos, hrach: I když budu potřebovat jazyky jen jednoho uživatele, pořád to není klasické 1:N. Nejdřív totiž musim načíst hromadu záznamů ze spojovací tabulky a pak ke každému záznamu donačíst jeden jazyk. Což lze jedině tím hnusným foreachem nebo tím Tharosovým joinem.

hrach
Člen | 1834
+
0
-

Díky: poslal jsem opravu: https://github.com/…tte/pull/549

Tharos
Člen | 1030
+
0
-

Tomu říkám servis :). Je skvělé, že je Nette\Database v dobrých rukou, díky za Tvou práci na něm.

kainashi
Člen | 1
+
0
-

Ahoj, řeším problém s Nette\Database:

Mám tabulky articles, categories, tags + articles_categories a articles_tags.
Potřebuji vypsat seznam všech článků + k nim přiřazených tagů, případně kategorii a do ní přiřazené články.

Kód v šabloně mám následující ($articles je tabulka):

{foreach $articles as $article}
	{$article->title}
	{foreach $article->related('articles_tags') as $tags}
	    {$tags->tags->name}{sep}, {/sep}
	{/foreach}
{/foreach}

Tedy přibližně stejný, jaký zde již byl uváděn.
K prvnímu článku mám přiřazeny tři tagy, ke druhému jen jeden z těch tří.
Při prvním načtení – s čistou cache – stránky se vše vypíše v pořádku, tedy u prvního článku tři tagy, u druhého jeden.
Pokud stránku refreshnu, nebo se na ní vrátím, tak tag přiřazený ke druhému článku se sice vypíše u druhého, ale naprosto chybí u prvního.

Mám pocit, že pokud se mi podaří vyřešit tento první problém, vyřeším i druhý, proto jej zatím nebudu popisovat.

Předem děkuji za pomoc.

Editoval kainashi (23. 2. 2012 23:17)

potty
Člen | 13
+
0
-

Mám stejný problém jako kainashi.

{foreach $match->related('player_match')->order('number ASC') as $player}
	{$player->player->name} {$player->player->surname}
{/foreach}

Když vymažu cache, tak to funguje. Při dalším refreshi se už ale vypisuje pouze jeden řádek.

Editoval potty (11. 3. 2012 14:48)

duke
Člen | 650
+
0
-

Pokud máte vazební tabulku s dvousloupcovým primárním klíčem, tak je za to zodpovědný bug v DiscoveredReflection, který by měl být opraven tímto commitem. David to zatím nepushnul, takže buď si budete muset počkat, nebo si tu třídu patchněte podle toho commitu sami.

potty
Člen | 13
+
0
-

Díky, problém vyřešen :)

leninzprahy
Člen | 150
+
0
-

Ještě že to tu je, už jsem chtěl ublížit počítači :)
Je to přesně tak, vazební tabulka s primárním klíčem přes dva sloupce.
Taky moc dík :)