Closure table a výpis kategorií
- Draffix
- Člen | 146
Zdravím, už nějakou dobu se snažím to vyřešit, ale stále mě nenapadá řešení a tak se obracím zde. Chtěl bych pro hierarchii kategorií použít closure table, kde vycházím z článku Jana Voráčka. Bohužel nevím, jak můžu na hlavní stránce zobrazit všechny kategorie (tedy kořeny) a pak při jejich zobrazení ukázat jejich potomky (pouze o jednu úroveň méně), ale zároveň zobrazit i jejich předka. Mám toto:
Databázové dotazy:
public function getCategories() {
return $this->connection->query('
SELECT c.*, cc.depth FROM category c
JOIN category_closure cc
ON (c.category_id = cc.descendant)
WHERE (SELECT COUNT(*) FROM category_closure WHERE descendant = c.category_id)=1;
');
}
public function AllCategorySubtree($id) {
return $this->connection->query('
SELECT c.*, cc.depth FROM category c JOIN category_closure cc
ON (c.category_id= cc.descendant)
WHERE cc.ancestor = ?', $id);
}
BasePresenter:
public function beforeRender() {
parent::beforeRender();
$this->template->getCategories = $this->table->getCategories();
}
CategoryPresenter:
public function renderDefault($id, $titleCategory) {
$this->template->subtree = $this->table->AllCategorySubtree($id);
}
@layout
<ul>
{foreach $getCategories as $element}
<li><a n:href="Category: $element->category_id, $element->name">{$element->name}</a></li>
{block #cat}{/block}
{/foreach}
</ul>
Category:default
{block cat}
{foreach $subtree as $element}
<ul>
<li><a n:href="Category: $element->category_id, $element->name">{$element->name}</a></li>
</ul>
{/foreach}
{/block}
Problém je ten, že rekurzívou při proklikání dostávám výpis namísto PC příslušenství → Tiskárny → Laserové pouze PC příslušenství → Laserové. Věděl by někdo jak toto vyřešit? Nebo prostě jiný způsob, jak můžu pomocí closure vypsat kategorie, ale zároveň vidět jeho předka a jeho potomka? Snad mě chápete.
- BigCharlie
- Člen | 285
@castamir: jasné potvrzení toho, že „content is the king“ :-) Jinak jsem druhý, co na tvůj článek v diskusi odkazuje, odkaz jsem tu někde odchytil…
- Draffix
- Člen | 146
@BigCharlie No pokud myslíš, že první odkaz je tento, tak se mrkni na autora :-D Jinak jsem projel snad všechno co google vyplivl, tedy od těchto dvou blogů až po blog Billa Karwina, ale řešení jsem nenašel. Prostě bych potřeboval nějak pošťouchnout, jak mohu udělat klasické kategorické menu pomocí closure:
Hlavní kategorie č.1
Subkategorie č.1
Subkategorie č.1
Subkategorie č.1.1.
Hlavní kategorie č.2
atd...
- castamir
- Člen | 629
@Draffix Někde jsem měl ještě lepší kód, ale nemůžu ho najít. Ale než ho najdu, hodím sem jiný funkční kód.
public function getSubtree($node) {
$tree = $this->db->query("
SELECT c.*, cc2.ancestor, cc2.descendant, cc.depth
FROM
$this->category c
JOIN $this->closure cc
ON (c.id = cc.descendant)
JOIN $this->closure cc2
USING (descendant)
WHERE cc.ancestor = $node AND cc2.depth = 1
ORDER BY cc.depth, c.name
");
return $this->parseSubTree($node, $tree);
}
private function parseSubTree($rootID, $nodes) {
// to allow direct access by node ID
$byID = array();
// an array of parrents and their children
$byParent = array();
foreach ($nodes as $node) {
if ($node["id"] != $rootID) {
if (!isset($byParent[$node["ancestor"]])) {
$byParent[$node["ancestor"]] = array();
}
$byParent[$node["ancestor"]][] = $node["id"];
}
$byID[$node["id"]] = (array) $node;
}
// tree reconstruction
$tree = array();
foreach ($byParent[$rootID] as $nodeID) { // root direct children
$tree[] = $this->parseChildren($nodeID, $byID, $byParent);
}
return $tree;
}
private function parseChildren($id, $nodes, $parents) {
$tree = $nodes[$id];
$tree["children"] = array();
if (isset($parents[$id])) {
foreach ($parents[$id] as $nodeID) {
$tree["children"][] = $this->parseChildren($nodeID, $nodes, $parents);
}
}
return $tree;
}
dumpni si výsledek
Editoval castamir (11. 2. 2013 23:38)
- castamir
- Člen | 629
Výše zmíněné metody vrací pole, jehož prvky jsou přímí potomci
kořene stromu. Pro všechny potomky (bez ohledu na hloubku) platí, že mají
atribut children
, jež obsahuje jejich přímé potomky.
příklad použití: presenter
public function renderDefault() {
$this->template->categories = $this->dir->getSubtree(1);
}
šablona
<ul class="tree">
{block #categories}
{foreach $categories as $node}
<li>
<span">{$node["name"]}</span>
<ul n:if="count($node['children'])">
{include #categories, 'categories' => $node["children"]}
</ul>
</li>
{/foreach}
{/block}
</ul>
Editoval castamir (11. 2. 2013 23:44)
- Draffix
- Člen | 146
Funguje to super, ale lze to nějak udělat i tak, abych viděl vždy potomky jen dané kategorie včetně její nadřazenou kategorii? Teď mám kategorie takhle:
Výpis všech kořenových kategorií:
PC příslušenství
Reproduktory
A kliknu třeba na PC příslušenství:
Klávesnice
Bezdrátové
Drátové
Myši
Laserové
Optické
Tiskárny
Inkoustové
Laserové
A já bych potřeboval něco ve stylu když někdo klikne třeba na PC příslušenství a pak na Klávesnice:
PC příslušenství
Klávesnice
Bezdrátové
Drátové
Myši
Tiskárny
Reproduktory
- castamir
- Člen | 629
No, docela záleží na tom, kde to chceš použít. Pokud je dat málo, můžeš si vystačit s tím, že vypíšeš celý strom, který jen upravíš pomocí CSS stylů nebo javascriptu.
EDIT: něco na tento způsob
ul.tree ul { display: none }
ul.tree li:hover > ul { display: block }
edit: nebo můžeš použít něco předchystaného.
Editoval castamir (12. 2. 2013 1:14)
- Draffix
- Člen | 146
Jasně, to mě nenapadlo použít. Jedná se o relativně malý počet, takže by se to dalo použít. Jen si opět neumím (snad už naposled) poradit s tím výpisem. Jak jsem psal, vypisuje mi to teď takto:
Klávesnice
Bezdrátové
Drátové
Myši
Laserové
Optické
Tiskárny
Inkoustové
Laserové
A já bych tam potřeboval ještě „dodat“ ty kořenové kategorie (bude jich více) takto:
PC příslušenství (kořen)
Klávesnice
Bezdrátové
Drátové
Myši
Laserové
Optické
Tiskárny
Inkoustové
Laserové
Reproduktory (kořen)
Tašky (kořen)
atd..
Lze toto nějak vyřešit? Mimochodem děkuji za trpělivost se mnou.