Autor Zpráva
josh
Profil *
Ahoj,

podle příkladu Jakuba Vrány jsem se nechal inspirovat a udělal jsem si traverzování. Podstatné položky v DB, které tahám, jsou LFT, RGT, LEVEL a NAME, řazeno dle LFT ASC.

Úrovně jsou dvě, ale nevím, jak to menu zapsat ve smarty:
{foreach from=$menu item='item'}
  {if $item.lft != ($item.rgt - 1)} <!-- pokud left neni o jednu mensi nez right -> je neco pod tim -->
    <li><a href="">{$item.name}</a>
      <ul> <!-- zacneme druhou uroven -->
  {else}
        <li><a href="">{$item.name}</a></li> <!-- druha uroven -->
  {/if}
  <!-- ted bych potreboval zakoncit seznam druhe urovne, ale nevim jak -->
  <!-- zkousel jsem opet podminku {if $item.lft != ($item.rgt - 1)}</ul></ul>{/if}, ale ta jej uzavre hned po te, co jej otevre -->
{/foreach}

Nakopne mě někdo správným směrem?

Děkuji.
AM_
Profil
Tohle je nějaké děsně pofidérní, průběžně tam občas začneš <ul>, ale nikde nevidím, že bys ho ukončoval.
Jinak jestli máš dvouúrovňové menu, vykašli se na stromy a prostě si udělej dvouúrovňový seznam, tohle je opravdu na menu zbtečně těžký kalibr.
josh
Profil *
AM:
děkuji za reakci.

Uznávám, že je to možná zbytečně těžký kalibr, měl bys návrh jiného řešení?
Potřebuji udělat v administraci webu možnost tvořit stránky tak, aby šlo vytvořit podmenu - dělám to tak, že při tvorbě stránky vyberu stránku nadřazenou (dejme tomu tvořím stránku "Sídlo firmy" a jako nadřazenou jí dám "Kontakty"). V menu budou Kontakty v první úrovni, Sídlo firmy a další kontaktní podstránky ve druhé.

V DB tedy ukládám nadřazenou stránku, úroveň zanoření, teď jsem přidal lft a rgt, myslel jsem, že pomůže. Mám tedy ještě přidat nějaký sloupec, nebo toto stačí? Jak bys tuto situaci řešil případně ty?

Děkuji.


ale nikde nevidím, že bys ho ukončoval.
to je v tom komentáři, že bych jej ukončit potřeboval, ale nevím jak :-)
AM_
Profil
josh:
V DB tedy ukládám nadřazenou stránku, úroveň zanoření, teď jsem přidal lft a rgt, myslel jsem, že pomůže. Mám tedy ještě přidat nějaký sloupec, nebo toto stačí? Jak bys tuto situaci řešil případně ty?
Menu není věc, která by se často měnila, a z praktických důvodů nebude obsahovat velká kvanta položek. Pokud si chceš stačí uložit nadřazenou položku, u první úrovně budeš vyplňovat třeba NULL. Pak můžeš renderer menu udělat sice časově neefektivním, nicméně velmi snadným způsobem:
- přečteš dotazem všechny položky, které mají rodiče NULL
- každou z těchto položek:
- vypíšeš
- rekurzivně přečteš a vypíšeš všechny položky, kterých je tato rodičem

Výsledek uložíš do cache souboru, který invaliduješ pokaždé, když někdo v admin rozhraní pozmění menu. Myslím, že toto vymyslíš mnohem rychleji, než traverzující algoritmus (včetně režie na udržování správných hodnot lft a rgt při změnách menu, která také není zrovna jednoduchá).
- pro běžná menu o několika desítkách položek generování menu potrvá stjně jen zlomek vteřiny
- bude se to dělat jen jednou po každé změně v menu
josh
Profil *
AM:
> - každou z těchto položek:
> - vypíšeš
tak budu mít seznam položek první úrovně, ale bez těch subpoložek, ne?

- rekurzivně přečteš
tohle jsem moc nepochopil, cos tím myslel :-)


Myslím, že toto vymyslíš mnohem rychleji, než traverzující algoritmus
to ano, vím, že by to nebylo lehké na údržbu, ale nějak bych se s tím (snad) popral. Tohle vypadá líp, jen pochopit tu správnou myšlenku (viz. výše) :-)
AM_
Profil
Zkusím vysvětlit na příkladu:
function vypismenu($rodic, $odsazeni){
  $menu = mysql_query("SELECT id, title FROM menu WHERE parent=$rodic");
  while ($item = mysql_fetch_assoc($menu)){
    for ($i = 0; $i < $odsazeni*2; $i++) echo '&nbsp;';
            //proste vypis 2*odsazeni mezer,
            //je na to nejaka funkce ktera to umi bez cyklu ale ted se mi ji nechce hledat,
            //treba budes odsazovat jinak, takze jen ilustracne
    echo '<a href="index.php?article='.$menu['id'].'">'.htmlspecialchars($menu['title']).'</a><br>';
    vypismenu($menu['id'], $odsazeni+1);
  }
}
vypismenu('NULL', 0);
Kcko
Profil
AM:
Zbytecne slozite na DB. Spravne jsi napsal, ze staci dotazem precist polozky co maji rodice NULL. Tady bych to opravil, a nacetl bych vsechny polozky.

A rekurzivne je pote vypsal pomoci PHP.

Tj. vysledkem bude pouze 1x dotaz. (Mam to ozkousene na navigaci o zhruba 700 polozkach a je to rychle jak peklo).
AM_
Profil
Kcko:
vlastně jo, když bys to z databáze dostal takhle:
"SELECT id, title FROM menu ORDER BY parent"

tak by se s tím i docela dalo pracovat. Ale stejně, přibude ti tam pak v PHP režie na třídění těch položek, to už asi bez traverzování tak jednoduše přímo při dotazu do databáze nesetřídíš. Takže si stejně myslím, že na jednoduché menu, které může uživatel upravovat v administraci, stačí tohle, navíc když zapojíš cache, je opravdu jedno, jestli se to poprvé generuje 0.5s nebo 0.005s :)
josh
Profil *
AM:
Použil jsem tvůj script, v DB mám místo NULL 0 (nulu).

Když vypíšu
vypismenu(0)
, vypíše mi to všechny položky. Jelikož je jen pár nadřazených, udělal jsem to takhle, i když to není to, co jsem přesně chtěl (je tam ta první úroveň předvolená, nejde s ní manipulovat bez zásahu do HTML šablony).

function vypismenu($rodic) {
	$menu = mysql_query("SELECT id, name FROM menu WHERE parent=$rodic");
	while ($item = mysql_fetch_assoc($menu)) {
		echo '<li><a href="index.php?article='.$item['id'].'">' . htmlspecialchars($item['name']) . '</a></li>';
		vypismenu($item['id']);
	}
}




<ul id="menu">
  <li><a href="{$ROOT}polozka1/">Polozka1</a>
    <ul>
{php}vypismenu(3){/php}
    </ul>
  </li>
  <li><a href="{$ROOT}polozka2/">Polozka2</a>
    <ul>
{php}vypismenu(15){/php}
    </ul>
  </li>
  <li><a href="{$ROOT}polozka3/">Polozka3</a>
    <ul>
{php}vypismenu(5){/php}
    </ul>
  </li>
</ul>
larryx
Profil
AM:
//proste vypis 2*odsazeni mezer,
//je na to nejaka funkce ktera to umi bez cyklu ale ted se mi ji nechce hledat,


myslím že to je str_repeat();
Kcko
Profil
AM:
Staci trivialni funkce ... viz



  protected function loadCoreData()
  {
    global $page;
    $sql = mysql_query("SELECT * FROM navigace ORDER BY poradi");
    while ($r = mysql_fetch_object($sql))
    {
      $this->subcats[ $r->rodic ][] = $r->id;
      $this->catNames[ $r->id ] = $r->nazev;
      $this->catParents[ $r->id ] = $r->rodic;
      $this->level[ $r->id ] = $r->poradi;  
      $this->seo[ $r->id ] = $r->url;
      
      //print_r($this->subcats);
    }  
  }


  public function printCategories($catIDs, $level = 1, $maxLevel = 999) 
  {
   global $page;
    
    echo "<ul>";
    
    foreach($catIDs as $catID) 
    {
      //$odkaz = "/" . implode("/",$this->buildHref($catID));
      $name = $this->catNames[$catID];
      $seo = $this->seo[$catID];
      
      // selected? 
      $totalUrl = $catID."-".$seo;
      if ($seo == "")
      $totalUrl = $catID;
      $selected = ( (int) $page[1] == $catID) ? "class='selected'" : "";
      //echo "<div><a href='/$page[0]/$catID-$seo.phtml'$selected >".$name."</a>";
      echo "<li><a href='/$page[0]/$totalUrl.phtml'$selected >".$name."</a>";

      
      if( array_key_exists($catID, $this->subcats) && ($catID == $_GET["catID"]) or in_array($catID, $this->rodice))
        {
           //echo $catID;	
           $this->printCategories($this->subcats[$catID], ($level + 1) );
        }
      
      echo "</li>";  

       
    }
    echo "</ul>";
  }  
josh
Profil *
Kcko:
to tvoje taky vypadá zajímavě, bohužel OOP není můj šálek kávy.. Můžu se zeptat, zda bys byl tak laskav a přepsal to do procedurálního stavu?

Můžeme se pak dohodnout na nějaké odměně, uvědomuji si, že dnes je programátorský čas drahý a zadarmo ani kuře nehrabe :-)
Kcko
Profil
napis mi na ICQ ... mam v profilu, udelam si na tebe vecer cas ...
josh
Profil *
Děkuji Kckovi za konzultaci a vyřešení problému.

AM:
tobě taky dík za snahu a rady.

josh.

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: