Šablony: Traverzování kolem stromu

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

Ahoj,

pokouším se v šabloně vypsat strom, který vznikl metodou Traverzování kolem stromu. Takže je seřazený a má proměnou hloubka, která určuje zanoření položky – více v části s „Výpis stromu do seznamu s odrážkami“ – chtěl bych to vypsat přesně do stromu s odrážkami

Chci zeptat, jestli je to dobrý nápad – jestli je dobrý nápad sestavovat takto strom v šabloně.

Pokoušel jsem se upravit příklad z Dědičnost bloků – konkrétně:

{block #menu}
<ul>
    {foreach $menu as $item}
    <li>{if is_array($item)} {include #menu, 'menu' => $item} {else}{$item}{/if}</li>
    {/foreach}
</ul>
{/block}

Ale tam mi přijde zbytečné transformovat to pole menu, aby jednotlivé položky byly zase pole menu a ty by byly taky pole atd… když už to mám takle pěkně seřazený…

Moc děkuju za každej nápad.

Ondřej Mirtes
Člen | 1536
+
0
-

Před nějakou dobou jsem v Nette taky traverzování kolem stromu dělal, taky na základě článku Jakuba Vrány. A pokud se nemýlím, tak tato metoda stromového výpisu je výjimečná právě tím, že se nevypisuje rekurzivně.

Myslím, že v této metodě by se vnořování v <ul> dalo zajistit detekcí změny hloubky oproti předchozí položce a na základě toho vypsat nějaké ukončování <ul> (vnořeného) a <li>… Bude to chtít trochu přemýšlení a experimentování, aby výsledek byl vždy validní, ale půjde to.

toka
Člen | 253
+
0
-

Experimentoval jsem takto dlouho, když jsem chtěl vypsat do šablony sitemap, a musím říct, že jsem byl tak netrpělivý a rozladěný výsledkem a nechtělo se mi nad tím myslet, půl páté ráno, že jsem se na to vykašlal :-) Ale zamínkou bylo vypsat nejen seznam, správně zanořený, což mi funguje, ale i mít možnost udělat odkazy – tedy každý prvek je pole. Šel jsem na to tak, že jsem přidal do pole klíč hasChild a podle něho chtěl ověřovat následnou potřebu volání rekurze, či nikoliv, ale ještě jsem to nedokončil. Proto bych se rád seznámil s Vaším řešením, pokud to není tajemství :-) Díky.

srigi
Nette Blogger | 558
+
0
-

Robim teraz na niecom, kde sa „Nested set“ pouziva a mam to celkom pekne vyriesene. Ako Model pouzivam Doctrine a ten Nested set podporuje nativne nad tabulkou. Ked si „fetchnem“ cely jeden strom, tak mi Model vrati cca toto:

Array
(
    [id] => 23
    [root_id] => 22
    [lft] => 2
    [rgt] => 9
    [level] => 1
)

Array
(
    [id] => 27
    [root_id] => 22
    [lft] => 3
    [rgt] => 4
    [level] => 2
)

Array
(
    [id] => 28
    [root_id] => 22
    [lft] => 5
    [rgt] => 6
    [level] => 2
)

Array
(
    [id] => 29
    [root_id] => 22
    [lft] => 7
    [rgt] => 8
    [level] => 2
)

Array
(
    [id] => 24
    [root_id] => 22
    [lft] => 10
    [rgt] => 15
    [level] => 1
)

Array
(
    [id] => 30
    [root_id] => 22
    [lft] => 11
    [rgt] => 12
    [level] => 2
)

Array
(
    [id] => 31
    [root_id] => 22
    [lft] => 13
    [rgt] => 14
    [level] => 2
)

Array
(
    [id] => 25
    [root_id] => 22
    [lft] => 16
    [rgt] => 17
    [level] => 1
  )

Array
(
    [id] => 26
    [root_id] => 22
    [lft] => 18
    [rgt] => 19
    [level] => 1
)

Doctrine umoznuje viac stromov v jednej tabulke, root nodu vynechavam, lebo by mi svietila ako rodic nultej urovne a ak by som kcel vytvorit jej ‚sibling‘, musel by som robit novy strom.

Nody su uz usporiadane a ako vidno nie su ulozene rekurzivne (vid. vyssie Ondrej). Na ich vykreslenie pouzivam takuto sablonu:

<ul>

  {? $temp = 1; }
  {foreach $tree as $node}
    {if $node->level > $temp}
      <ul>
      {? $temp = $node->level;}
    {elseif ($node->level == $temp) && (!$iterator->isFirst())}
      </li>
    {elseif $node->level < $temp}
      </li></ul></li>
      {? $temp = $node->level;}
    {/if}

    <li><a href="{plink Foo:bar 'id'=>$node->id}">{$node->name}</a>
  {/foreach}
  </li>

</ul>

Generuje to validny HTML kod cca:

<ul>
  <li>
    <a href="">Node name</a>
    <ul>
      <li><a href="">Subnode name</a></li>
      ...
    </ul>
  </li>
  ...

cize vnorene UL je „zakomponovane“ este v nadradenom LI, tak ako som to potreboval (a ako je to spravne podla W3C). Sablona si do $temp uklada aktualny level a v jednotlivych iteraciach sa if podmienka pozera, ci level klesa, stupa alebo je bez zmeny a podla toho renderuje. Pouzivam tam este premennu $iter aby sa mi hned po v prvej iteracii nevyrenderovalo uzatvaracie </li>, toto treba trocha zrefaktorovat a vyuzit ten iterator, co je ako pomocna premenna v loopoch v sablonach.

Inak ked som hladal tuto vec po nete, nasiel som, ze ludia zo Symfony a Propel renderuju Nested set uplne rovnako (odkladaju si level do docasnej premennej), takze je toto riesenie IMO spravne.
enjoy.

Edit: este pripomeniem, ze tato sablona umoznuje renderovat strom neobmedzenej hlbky, nie len do druhej urovne ako je v ukazke vyssie (je to len priklad). Okrem tohom nody v mojom priklade su objekty, nie len polia, preto na nich mozem volat rozne pomocne metody potrebne k renderu. Priklad je ale IMHO dostatocne zrozumitelny.

Editoval srigi (9. 7. 2010 6:45)

Vyki
Člen | 388
+
0
-

Já osobně používám základní myšlenku tohoto: https://forum.dibiphp.com/…34-fetchtree. Je to rekurzivní tvoření stromu (pole) dle záznamů z DB na základě vztahu $id a $parent_id. Jsou tam velmi pěkně použity reference polí. Celý ten strom si ukládám do cache aby to vždy při tvoření menu nemuselo sahat do DB. Pro vypisování toho menu potom také používám ten příklad z dědičnosti bloků.

Ani
Člen | 226
+
0
-

Trochu to tu zneužiju.
Můžu se jen zeptat, proč všichni vypisujete tyhle menu do <ul> <li>, snad jedině, že to jsou značky na seznam? Já to vždycky řešim jen jako normální odkazy, který si upravím pomocí css a .depth1, .depth2… Dělám něco špatně?

Jinak já si celý menu z db tahám jako ->fetchAssoc(‚parent_id,id‘) a to pak projedu poměrně jednoduchou i docela rychlou rekurzivní funkcí. A vykreslení už je jednoduchý, přes foreach.

Editoval Ani (10. 2. 2010 1:15)

srigi
Nette Blogger | 558
+
0
-

Ani napsal(a):

Trochu to tu zneužiju.
Můžu se jen zeptat, proč všichni vypisujete tyhle menu do <ul> <li>, snad jedině, že to jsou značky na seznam?

Ide o to, že strom vyrenderovany do UL-LI zoznamu sa ti vykresli ako strom aj pri vypnutom stylovani. Tvoje riešenie IMO zlyha (z vizualnej stránky veci) pri vypnutom CSS. A navyše je renderovanie do UL-LI nezávisle na hĺbke stromu. Ty ak vo svojej metode nepripravys štýly pre hĺbku 5 a viac, tak už paturovnove menu sa nebude vykreslovat správne. A okrem toho je to semantickejsie ako nejaké ahref-y v dive.

iguana007
Člen | 970
+
0
-

Ahoj srigi,
prosimte, mel bych na tebe dotaz, pokousel jsem se implementovat tvuj priklad (http://bit.ly/bAVvlc) tykajici se nested set. Jde mi o to, ze mam v databazi nested strom odkazu, z ktereho bych rad vykreslil UL>LI dropdown menu.
Myslim, ze jsem vse pochopil spravne, ale narazil jsem na error, ktery na me pri renderu menu vyskoci:
Call to undefined method DibiRow::getActive()
Predpokladam, ze to po me bude chtit i ty dalsi metody z sablony, mohl by si mi prosim popsat, co tyto metody maji presne delat?
Diky moc za kazdou radu.

srigi
Nette Blogger | 558
+
0
-

iguana007 napsal(a):

Ahoj srigi,
prosimte, mel bych na tebe dotaz, pokousel jsem se implementovat tvuj priklad (http://bit.ly/bAVvlc) tykajici se nested set. Jde mi o to, ze mam v databazi nested strom odkazu, z ktereho bych rad vykreslil UL>LI dropdown menu.
Myslim, ze jsem vse pochopil spravne, ale narazil jsem na error, ktery na me pri renderu menu vyskoci:
Call to undefined method DibiRow::getActive()
Predpokladam, ze to po me bude chtit i ty dalsi metody z sablony, mohl by si mi prosim popsat, co tyto metody maji presne delat?
Diky moc za kazdou radu.

Hello, ide o to, ze som ten svoj priklad neocistil – pri vykreslovani LIcka si vsimni, ze je zistovanie „aktivnosti“. Len to vyhod z kodu a pojde ti to.

ide o to, ze ja som do View sablony predaval tzv. Doctrine_List co je hentamto pole – v skutocnosti su to objeky Doctrine_Record, ktore je mozne previest na pole. No a ja som ich rozsiril o metodu getActive()

Sypem si popol na hlavu

edit: upravil som povodny prispevok. Kod je tam upraveny (ocisteny), je tam pouzity aj ten $iterator, takze si to updatnite.

Editoval srigi (9. 7. 2010 6:47)

iguana007
Člen | 970
+
0
-

Super! :o) uz to bezi … diky moc … ;o)

iguana007
Člen | 970
+
0
-

Tak odvolávám – v tom příkladu, jak to vykreslit v té šabloně je někde chyba, protože se mi ten strom od určité části začne renderovat špatně – tj. přestane to uzavírat prvky, dám sem příklad:

Item
	Item
		Item
		Item
	Item
		Item
		Item
Item
	Item
		Item
		Item
		Item
	Item
		Item
Item
	Item
		Item
	Item
		Item - zde mi to zacne renderovat spatne
Item
	Item
		Item
		Item
		Item
	Item
		Item
	Item
		Item
		Item

Přijde mi právě divné, že to zkolabuje někde uprostřed a ne třeba u prvního výskytu levelu 3 apod. Nějaké nápady co s tím prosím? Řeším to už celý den a už vážně nevím co by mohlo být špatně.
Díky za každou radu.

norbe
Backer | 405
+
0
-

Nemůže být problém ve špatně uloženém stromu?

iguana007
Člen | 970
+
0
-

Kontroloval jsem, strom vypada ok.

westrem
Člen | 398
+
0
-

Necital som uplne dopodrobna vsetky prispevky, takze mozno poviem nieco co uz bolo povedane, alebo nieco co sa plne nehodi ale nepostaci obycajne DFS?

Peetee
Člen | 75
+
0
-

Ahoj,

děkuji všem přispívajícím, sice již je to poměrně dlouho, ale tento problém řeším pravidelně, proto si dovolím připojit upravené řešení od srigi, ještě jednou děkuji!

<ul>

  {? $temp = 1; }
  {foreach $tree as $node}
    {if $node->level > $temp}
      <ul>
      {? $temp = $node->level;}
    {elseif ($node->level == $temp) && (!$iterator->isFirst())}
      </li>
    {elseif $node->level < $temp}
      {? $a=str_repeat("</li></ul></li>",$temp-$node->level)}
      {!$a}
      {? $temp = $node->level;}
    {/if}

    <li><a href="{plink Foo:bar 'id'=>$node->id}">{$node->name}</a>
  {/foreach}
  {? $a=str_repeat("</li></ul></li>",$temp)}
  {!$a}
</ul>

Budu rád za komentáře.

Editoval Peetee (27. 9. 2010 22:55)

iguana007
Člen | 970
+
0
-

Peetee napsal(a):
Budu rád za komentáře.

Chtěl bych se zeptat, jakou fci plní proměnná $material?

Peetee
Člen | 75
+
0
-

iguana007 napsal(a):
Chtěl bych se zeptat, jakou fci plní proměnná $material?

To byl „překlep“ (ten kód jsem vykopíroval z aktuálního projektu a pak jsem to upravoval až tady), opravil jsem to v původní verzi.

macesko
Člen | 12
+
+1
-

Ahoj,
nevim jestli jsi to testoval, ale nasel jsem tam chyby. Trochu jsem to upravil.

<ul>
        {? $temp = 1; }
        {foreach $pages as $node}
        {if $node['depth'] > $temp}
        <ul>
            {? $temp = $node['depth'];}
            {elseif ($node['depth'] == $temp) && (!$iterator->isFirst())}
            </li>
            {elseif $node['depth'] < $temp}
            </li>
            {? $a=str_repeat("</ul></li>",$temp-$node['depth'])}
        {!$a}
        {? $temp = $node['depth'];}
        {/if}
        <li><a href="{plink Foo:bar 'id'=>$node->id}">{$node->name}</a>
            {/foreach}
        </li>
        {? $a=str_repeat("</ul></li>",$temp-1)}
{!$a}
</ul>

Editoval macesko (5. 11. 2010 3:23)