Closure tree s Nette Database: Problém s JOIN

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

Snažím se o implementaci Closure Tree a narazil jsem hned na začátku na problém. Jde toto implementovat čistě pomocí ND popřípadě NotORM? Padá to na vytvoření JOINu přes referenci z category do category_closure, která samozřejmě neexistuje. Věděl by prosím někdo jak na to? Pokud to nejde, skončím u query() a hotovo :-D

SELECT c.*, cc.depth FROM category c
  JOIN category_closure cc
    ON (c.id = cc.descendant)
  WHERE cc.ancestor = ?', $ancestor

Editoval Climber007 (28. 1. 2013 1:24)

castamir
Člen | 629
+
0
-

Closure table je pěkný mechanismus, ALE ukázkové použití (popsané např. zde) je v drtivé většině případů nedostačující. Ty dotazy pak budou muset být komplikovanější a to už pomocí NDB nepůjde už vůbec. Tvůj dotaz vypisuje celý strom včetně kořene, ale nelze z něj rekonstruovat původní strom, jelikož znáš jen hloubku od kořene, ale neznáš rodiče všech prvků. Pro reálnou rekonstrukci musíš přidat ještě jeden join na category_closure cc2 USING (descendant) WHERE cc2.depth = 1. Úpravou sloupců na c.*, cc2.ancestor, cc.depth získáš požadovaná data. Tento dvojitý join navíc na stejnou tabulku zatím nelze v NDB provést (nejspíš kvůli aliasům). A to jsem ten příklad vůbec nekomplikoval přidáním řazení prvků :D.

Prostě v tomto případě se bez query asi neobejdeš.

PS: o prakticky použitelném použití closure tables teď píšu článek na svůj blog, až bude hotový, pošlu link…

Climber007
Člen | 105
+
0
-

Super, díky moc. Na článek se těším. Nicméně, lze pomocí ND vyřešit tenhle dotaz nebo ne?

castamir napsal(a):

PS: o prakticky použitelném použití closure tables teď píšu článek na svůj blog, až bude hotový, pošlu link…

hrach
Člen | 1844
+
0
-

Pokud jsou dobre nastavene klice (coz nevim), tak TEORETICKY by to melo byt

$db->table('category')->select('category.*, category_closure.depth')->where('category_closure.ancestor', $a)

Ale asi bych se o to radej ani nesnazil.

castamir
Člen | 629
+
0
-

Melo by to jiz zapsat, ale dotaz bude trosku obraceny.

$selection = $connection->table("category_closure")
    ->where("ancestor = ?", $ancestor);

a pristup ke k dane category muzes pres

$row = $selection->fetch(); // ziskani instance ActiveRow
$category = $row->ref("categoey", "ancestor");

Nebo by to melo jit pres teckovou notaci primo:

$selection = $connection->table("category_closure")
    ->select("category.*, category_closure.depth")
    ->where("category.id = category_closure.descendant")
    ->where("category_closure.ancestor = ?", $ancestor);

edit: ty prefixy jsem radeji doplnil

Editoval castamir (28. 1. 2013 15:10)

Climber007
Člen | 105
+
0
-

Díky, měl jsem to přesně tak, ale padá to pořád na referencích, category_closure.ancestor a category_closure.descendant míří na category.id. Měly by být v pořádku, ale vůbec mne nenapadá, kde může být chyba.

enumag
Člen | 2118
+
0
-

@Climber007: Co takhle uvést chybu kterou to hlásí? ;-)

castamir
Člen | 629
+
0
-

Chyba je nejspis na strane NDB. NDB nejspis nevi, ktery cizi klic ma pouzit (jsou tam 2 odkazujici na stejnou tabulku). Toto asi nepujde. Teda ne do doby, nez (jestli) se dodela rozsireni NDB o join.

Ten prvni zpusob pres ref ti fungoval?

enumag
Člen | 2118
+
0
-

@castamir: Troufnu si tvrdit že rozšíření NDB o join se dělat nebude. Jediné co NDB potřebuje je možnost v dotazu specifikovat sloupec přes který se to připojuje podobným způsobem jako při volání ref. Implementace je součástí hrachova refactoringu který už nějakou dobu je mezi pull requestama na githubu, zde na fóru jako RFC.

Climber007
Člen | 105
+
0
-

Promiň. Chyba je jasná: No reference found for $category_closure->category.

enumag napsal(a):

@Climber007: Co takhle uvést chybu kterou to hlásí? ;-)

castamir
Člen | 629
+
0
-

@enumag specifikace sloupce by mozna i stacila. Jeste ty aliasy a budu spokojenej :-). Ale na slozitejsi veci stejne radeji pouziju neco jineho. NDB mi nekdy pripada docela kostrbata a hlavne ma dvoji vystupy (Selection vs Statement). Ale na jednoduche dotazy je naopak idealni.

enumag
Člen | 2118
+
0
-

@Climber007: Používáš Conventional nebo Discovered reflexi? Na closure table potřebuješ jednoznačně discovered a možná ani ta nebude fungovat (z důvodů které uvedl castamir).

@castamir: Jo, aliasy bych taky ocenil. :-) Už jsem na to upozorňoval dříve, bohužel bez odezvy. Při používání NDB se snažím mít vše jako Selection, většinou to jde.

Climber007
Člen | 105
+
0
-

@castamir: Zkoušel, ale začínám se v tom zamotávat. Potřebuju jednoduše vytáhnout všechy podkategorie (včetně parent kategoie) a normálně je procházet v šabloně pomocí foreach. Nevím jak to procházet s tím ref, nicméně tuším a nelíbí se mi to. Chci prostě tvar $subcategory->name a nic víc. Přes query() to funguje, ale zase jsem nepřišel na to, jak z toho udělat objekt pochopitelný pro related na produkty takto získaných podkategorií.

$subcategory = $this->categoryRepository->subtreeOf($id); // Výsledek query(), není za ním fetch().
$this->template->products = $this->categoryRepository->findProducts($subcategory);

Tady to právě padne na: Call to undefined method Nette\Database\Statement::related(). Když si ale získám kategorie jinak (ne přes query) normálně se k tomu přes related dostanu.

castamir
Člen | 629
+
0
-

To je proto, ze query vraci Statement, ale table vraci Selection. Vse mas jeste v prehledu zde.

Popis tridy Statement

Popis tridy Selection

Edit: jeste doplnim, ze „radky“ v Selection jsou instance tridy ActiveRow, zatimco u Statementu jsou to instance tridy Row

Editoval castamir (28. 1. 2013 18:28)

Climber007
Člen | 105
+
0
-

Je to celé nějaké divné. V jiném případě, kde mám m:n vazbu vše funguje. LEFT JOIN se vytvoří v pořádku. Zkoušel jsem i odstranit jednu referenci, aby tam byla pouze jedna a taky nic.

Draffix
Člen | 146
+
0
-

Jen kdyby někdo na tento článek narazil (jako já), tak uvádím článek castamira o closure

Jan Suchánek
Člen | 404
+
0
-

Kdyby to někdo scháněl tak funguje:

		$ancestors = $this->dbContext->table("category_closure")->where("descendant  = ?", $id)->order("depth DESC");

		foreach($ancestors as $ancestor){
			$category = $row->ref("category","ancestor");
		}

		// Nebo

		$ancestors = $this->dbContext->table("category")
			->where(":category_closure(ancestor).descendant = ?", $id)
			->select("category.*")
			->select(":category_path.ancestor")
			->select(":category_path(ancestor).depth")
			->order(":category_path(ancestor).depth DESC");