Menu a Submenu cez Databázu

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

Zdravím ľudia…
Prosím Vás máme záujem o menu ktoré by sa ťahalo aj so submenami z databáze.
Skúšal som poriešiť niečo takéto

menu.latte

<ul>
{foreach $menus as $mymenu}
{? $sub_relatedes = $mymenu->related('submenu', 'id')}
 {if $sub_relatedes->count('*')>0}
  <li><a href="#">{$mymenu->title}</a>
  <ul>
   <li>
   	<a href="{$mymenu->ref('submenu', 'id')->sublink}">
   		{$mymenu->ref('submenu', 'id')->subtitle}
   	</a>
   </li>
  </ul>
 </li>
 {? $my_relateds = $mymenu->related('menu1', 'id')}
{elseif $my_relateds->count('*')>0}
 <li><a n:href="$mymenu->link">{$mymenu->title}</a></li>
 {/if}
{/foreach}
</ul>

Presenter

public function renderDefault()
	{
		$this->template->menus = $this->menu1->findAll();
	}

MenuRepository

/** @var Nette\Database\Connection */
    private $database;

    public function __construct(Nette\Database\Connection $database)
    {
        $this->database = $database;
    }

    public function findAll()
    {
    	return $this->database->table('menu1');
    }

    public function findById($id)
    {
    	return $this->findAll()->get($id);
    }

    public function insert($values)
    {
    	return $this->findAll()->insert($values);
    }

A nakoniec DB

CREATE TABLE IF NOT EXISTS `menu1` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `submenu_id` int(11) unsigned NOT NULL,
  `title` varchar(64) NOT NULL,
  `link` varchar(60) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=9 ;

--
-- Sťahujem dáta pre tabuľku `menu1`
--

INSERT INTO `menu1` (`id`, `submenu_id`, `title`, `link`) VALUES
(1, 1, 'Menu1', 'Index:default'),
(2, 2, 'Menu2', 'Index:default'),
(3, 0, 'Menu3', 'Index:default'),
(4, 0, 'Menu4', 'Index:default'),
(5, 3, 'Menu5', 'Index:default');

-- --------------------------------------------------------

--
-- Štruktúra tabuľky pre tabuľku `submenu`
--

CREATE TABLE IF NOT EXISTS `submenu` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `subtitle` varchar(64) NOT NULL,
  `sublink` varchar(60) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;

--
-- Sťahujem dáta pre tabuľku `submenu`
--

INSERT INTO `submenu` (`id`, `subtitle`, `sublink`) VALUES
(1, 'Submenu1', 'Index:default'),
(2, 'Submenu5', 'Index:default'),
(3, 'Submenu3', 'Index:default');

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

Na moje počudovanie mi zobrazuje menu aj submenu lenže problém nastáva ak mám toto v tabuľke

Tabuľka Menu 							 Tabuľka Submenu
--------------------------------------------------------------------------------
id  submenu_id   title   link          | id 	subtitle 	sublink
--------------------------------------------------------------------------------
1 	1 			 Menu1 	 Index:default | 1 		Submenu1 	Index:default
2 	2 			 Menu2 	 Index:default | 2 		Submenu5 	Index:default
3 	0 			 Menu3 	 Index:default | 3 		Submenu3 	Index:default
4 	0 			 Menu4 	 Index:default |
5 	3 			 Menu5 	 Index:default |
--------------------------------------------------------------------------------

v poriadku zobrazí poradie Menu1+Submenu1 Menu2+Submenu5
Ale po pridaní viac menu a submenu a ich následne premiešanie už nezobrazí poradie Menu3, Menu4, Menu5+Submenu3 ale preskočí to všetko a zobrazuje ich
v tomto poradí Menu1+Submenu1,Menu2+Submenu5,Menu3+Submenu3, Menu4, Menu5 a pritom Menu5 má určené Submenu3

Hádam som to dostatočne vysvetlil :)

Viem, že toto nieje 100% ideálne riešenie no po prebádaní tu na fóre a v dokumentácii zomňa proste nevyšlo dosť !určite! jednoduchšie riešenie. Prosím ak sa Vám bude chcieť mohli by ste sem dať nejaké rozumnejšie príklady ktoré nielen pomôžu mňe ale mnohím začiatočnikom.
Ešte ma trápi jedna vec a to ako vytvoriť formulár ktorý bude vložievať menu do menu1 a submenu do submenu a určovať ktoré sub patri pod ktoré menu a naopak ktoré menu nebude obsahovať sub … Fúha sam som sa v tom teraz dosť zaplietol :) …
Ale to by sa asi riešilo cez select

ďakujem vopred všetkym.

Petr Hudík
Člen | 49
+
0
-

Ahoj, pročítal jsem Tvůj příspěvek několikrát a mám trochu zmatek už v samotné struktuře dat. Proč jsi zvolil právě tento způsob? Dle mého názoru je zvolený způsob nevhodný, protože předpokládá jednu úroveň zanoření a zadání na strukturu menu se může velice snadno změnit, proto je často výhodnější vytvořit pro menu obecnější strukturu.

Domnívám se, že problém, který řešíš se jmenuje ukládání stromové struktury dat. Něco nalezneš v prastarém článku na Intervalu, problému se věnuje i Jakub Vrána v Traverzování kolem stromu a mnoho dalších příspěvků. Další zdroje jistě snadno nalezneš.

Editoval Petr Hudík (12. 5. 2013 11:45)

SontoEremo
Člen | 341
+
0
-

Noo to som aj ja sám s toho bol oblbnutý :)
Prešiel som si tvoje odkazy ale stále mi nieje jasne ako teda na tu db štruktúru a ako to celé vlastne pospájať nemohol by si v jednoduchosti hodiť nejaký kód robený v Nette a NDB? ďakujem.

Petr Hudík
Člen | 49
+
0
-

Prosím pokus se sám sobě odpovědět na následující otázku – „Co je tvým cílem – snažíš se vytvořit webovky, nebo se naučit Nette?“ Pokud pouze první bude pro Tebe daleko výhodnější (a domnívám se i levnější), pokud si seženeš někoho, kdo to naprogramuje. Pokud je Tvým cílem se něco naučit, není řešení, abychom sem házely zdrojáky a Ty jsi lepil kód pomocí metody CTRL+C/CTRL+V, prosím oprav mě, pokud se pletu.

Pro začátek navrhuji úplně tu nejjednodušší stromovou strukturu. U každého záznamu pouze přidat ID rodiče. Záznamy, které jsou na nejvyšší úrovni menu, mohou mít ID rodiče NULL.

SontoEremo
Člen | 341
+
0
-

Nazdar Petr Hudík
Bohužial sa naozaj pletieš :)
Nette sa chcem naučiť a nie som typ človeka ktorý spravý copy/paste a jede se dál :)
Mne hlavne išlo o to ako na to v nette aké príkazy použiť a ako na tabuľku …
čo som včera do pol 4 rána kukal všetko okolo ukladania stromovej štruktúry a traverzovanie okolo stromu
mi prišlo nejasné napr.
tabuľka všetko je v nej natlačené? id → nazov → link → sub
fakt mi ide iba o to ako to vyzerá v nette.

vvoody
Člen | 910
+
0
-

Nette nerieši menu, nette za teba nerieši ani ukladanie stromovej štruktúry, nette čiastočne rieši až väzby/FK medzi tabuľkami/entitami. Návrh databáze je preto tvoja starosť s ktorou ti nette nepomôže, treba si prejsť nejaké základy relačných databáz miesto hľadania „univerzálneho riešenia na menu“ v nette.
Keď budeš mať vhodne navrhnutú databázu pre stromové menu, nette ti pomôže z databáze to menu načítať, pomôže ti menu zobraziť, pomôže ti z toho všetkého spraviť jednoducho znovupoužiteľnú komponentu, prípadne ti umožní menu jednoducho cachovať atď. Ale až budeš sám mať jasno s akými dátami pracuješ.

Áno, všetko môže byt natlačené v jednej tabuľke.

  • id
  • title
  • link/destination
  • parent_id (FK na id tejto istej tabuľky, NULLable)

Editoval vvoody (13. 5. 2013 12:45)

SontoEremo
Člen | 341
+
0
-

Nazdar vvoody
Ak som správne pochopil tak strom tabuľky je nasledovný:

-----------------------------------
Tabuľka menu
-----------------------------------
id | title | link     | parent_id |
1  | menu1 | link:link| NULL      |
2  | menu2 | link:link| NULL      |
3  | sub1  | link:link| 1		  |
-----------------------------------
-----------------------------------

takže ak je menu1->id(1) a sub1->parent_id(1)
znamená, že menu1 bude obsahovať sub1 pretože parent_id ma id menu1 ?
a naopak ak menu1 je NULL znamená, že neobsahuje žiadne submenu.

Petr Hudík
Člen | 49
+
0
-

@SontoEremo: ano, je to přesně tak, jak píšeš. Dobré ještě je, když sloupec parent_id obsahuje cizí klíč k sloupci id.

Nyní, když budeš chtít vypsat menu nejvyšší úrovně (menu1, menu2), tak stačí vypsat všechny, kde parent_id je NULL, tím bych pravděpodobně začal…

SontoEremo
Člen | 341
+
0
-

@Petr Hudík:
To som sa ešte chcel spýtať ako nastaviť parent_id cudzí kľúč ?
Všade kde som pozeral bolo v phpmyadmine v typ: FOREIGN KEY lenže to ani za boha neviem nájsť
mám tam všetko:
INT
TINYINT
MEDIUMINT
atď…
FOREIGN KEY nikde

Glottis
Člen | 129
+
0
-

honem si to doreste, jinak vam to nejakej nadsamec tady zavre pro nesouvislost s nette ;)

Petr Hudík
Člen | 49
+
0
-

Bohužel nedokážu poradit, používám Adminer, který velice doporučuji, v něm to nastavíš opravdu snadno.

Pouze doplním, že oba sloupce musí být stejného datového typu.

MartinitCZ
Člen | 580
+
0
-

@**Glottis**: Však to je dobře. Tohle je o neznalosti SQL a né o problémech s Nette. Toto má být na jpw.cz

SontoEremo
Člen | 341
+
0
-

OK OK ospravedlňujem sa …
posledný príspevok sa môže zmazať…
Nevedel som, že otázka ohľadom DB je mimo
Sorry :)

Do budúcna sa poučím!

SontoEremo
Člen | 341
+
0
-

Takže snažil som sa niečo poskladať no je to nejaké nestabilné a čudné :)

CREATE TABLE IF NOT EXISTS `menu` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL,
  `link` varchar(255) NOT NULL,
  `parent_id` int(11) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `parent_id` (`parent_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;

--
-- Sťahujem dáta pre tabuľku `menu`
--

INSERT INTO `menu` (`id`, `title`, `link`, `parent_id`) VALUES
(1, 'Dashboard', 'Index:default', NULL),
(2, 'Menu', 'Index:default', NULL),
(3, 'Submenu', 'Index:default', 1);

--
-- Obmedzenie pre exportované tabuľky
--

--
-- Obmedzenie pre tabuľku `menu`
--
ALTER TABLE `menu`
  ADD CONSTRAINT `menu_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `menu` (`id`);

Presenter

public function renderDefault()
	{
		$this->template->menus = $this->database->table('menu')
												->where('id')
												->order('parent_id');
	}

Šablóna

<ul>
{foreach $menus as $menu}
<li>{$menu->title}
 {if $menu->parent_id > 0}
  <ul>
   <li>{$menu->title}</li>
  </ul>
 {/if}
 </li>
{/foreach}
</ul>

Menu mi ukáže ale submenu ktoré má byť pod Dashboard nie je ale výsledok je nasledovný:

Dashboard
Menu
Submenu
    Submenu

Som už z toho na totálku úplne prevrátený mohli by ste ma ešte trocha popostrčiť?
ďakujem vopred všetkym.

Tabetha
Člen | 140
+
0
-

Takto by to malo fungovať…

	$this->template->menus = $this->database->table('menu2')
			                         ->where("parent_id IS NULL" )
			                         ->order('parent_id');
<ul>
	{foreach $menus as $menu}
	{var $childs = $menu->related('menu2','parent_id')}
	{dump $childs->count()}
	<li>{$menu->title}
	    {if $childs->count() > 0}
		<ul>
			{foreach $childs as $child}
				<li>{$child->title}</li>
			{/foreach}
		</ul>
	    {/if}
	</li>
	{/foreach}
</ul>

a výsledok (doplnil som tam riadky kvôli overeniu)

<ul>
	<li>Dashboard
		<ul>
				<li>Submenu</li>
				<li>Test5</li>
		</ul>
	</li>
	<li>Menu
		<ul>
				<li>Test4</li>
		</ul>
	</li>
</ul>
SontoEremo
Člen | 341
+
0
-

Tabetha
Tak teraz som sa zasmial k smrti aký som hlúpi Veľmi pekne ti ďakujem zas mi toto fórum a ochotný členovia pomohli :)

Takže prikladám sem Menu a Submenu ťahané z DB

Presenter:

/** @var Nette\Database\Connection */
    private $database;

    public function __construct(Nette\Database\Connection $database)
    {
        $this->database = $database;
    }

	public function renderDefault()
	{
		$this->template->menus = $this->database->table('menu')
												->where("parent_id IS NULL" )
                                 				->order('parent_id');
	}

Šablóna:

<ul>
    {foreach $menus as $menu}
    {var $childs = $menu->related('menu','parent_id')}
    {dump $childs->count()}
    <li>
    	{if $childs->count() > 0}
    	<a href="javascript:;">{$menu->title}</a>
    	{else}
    	<a n:href="$menu->link">{$menu->title}</a>
    	{/if}
        {if $childs->count() > 0}
        <ul>
            {foreach $childs as $child}
                <li><a n:href="$child->link">{$child->title}</a></li>
            {/foreach}
        </ul>
        {/if}
    </li>
    {/foreach}
</ul>

Ešta aby som nezabudol mám tam tento kód

{if $childs->count() > 0}
    	<a href="javascript:;">{$menu->title}</a>
    	{else}
    	<a n:href="$menu->link">{$menu->title}</a>
    	{/if}

A to len preto lebo moja šablóna vyžaduje v menu ktoré ma submenu aby bol href=„javascript:;“
Normálne to má byť

<ul>
    {foreach $menus as $menu}
    {var $childs = $menu->related('menu','parent_id')}
    {dump $childs->count()}
    <li>
    	<a n:href="$menu->link">{$menu->title}</a>
        {if $childs->count() > 0}
        <ul>
            {foreach $childs as $child}
                <li><a n:href="$child->link">{$child->title}</a></li>
            {/foreach}
        </ul>
        {/if}
    </li>
    {/foreach}
</ul>

a Tabuľka DB:

CREATE TABLE IF NOT EXISTS `menu` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL,
  `link` varchar(255) NOT NULL,
  `parent_id` int(11) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `parent_id` (`parent_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=8 ;

--
-- Sťahujem dáta pre tabuľku `menu`
--

INSERT INTO `menu` (`id`, `title`, `link`, `parent_id`) VALUES
(1, 'Menu1', 'Index:default', NULL),
(2, 'Menu2', 'Index:default', NULL),
(3, 'Submenu1', 'Index:default', 1),
(4, 'Submenu2', 'Index:default', 1),
(5, 'Menu3', 'Index:default', NULL),
(6, 'Menu5', 'Index:default', NULL),
(7, 'Sumnenu3', 'Index:default', 6);

--
-- Obmedzenie pre exportované tabuľky
--

--
-- Obmedzenie pre tabuľku `menu`
--
ALTER TABLE `menu`
  ADD CONSTRAINT `menu_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `menu` (`id`);

Ešte raz všetkým veľmi pekne ďakujem..........

Tabetha
Člen | 140
+
0
-

Ešte to môžeš upraviť takto ak sa nemýlim… a dump môžeš dať preč :-) ak ho nepotrebuješ

<ul>
    {foreach $menus as $menu}
    {var $childs = $menu->related('menu','parent_id')}
    <li>
	{if $childs->count() > 0}
		<a href="javascript:;">{$menu->title}</a>
		<ul>
        		{foreach $childs as $child}
	             	   <li><a n:href="$child->link">{$child->title}</a></li>
	        	{/foreach}
		</ul>
	{else}
		<a n:href="$menu->link">{$menu->title}</a>
	{/if}
    </li>
    {/foreach}
</ul>
SontoEremo
Člen | 341
+
0
-

@Tabetha:
Mám to takto

<ul>
{foreach $menus as $menu}
     {var $childs = $menu->related('webadmin_menu','parent_id')}
    {if $childs->count('*') > 0}
     <li class="has-sub {foreach $childs as $child}{ifCurrent $child->menu_link}active{/ifCurrent}{/foreach}">
      <a href="javascript:;">
       <i class="{$menu->menu_icon}"></i>
       <span class="title">{$menu->menu_title}</span>
       <span class="arrow "></span>
      </a>
      <ul class="sub">
    {foreach $childs as $child}
       <li class="{ifCurrent $child->menu_link}active{/ifCurrent}">
        <a n:href="$child->menu_link">{$child->menu_title}</a>
       </li>
    {/foreach}
      </ul>
     </li>
    {else}
     <li class="{ifCurrent $menu->menu_link}active{/ifCurrent}">
      <a n:href="$menu->menu_link">
       <i class="{$menu->menu_icon}"></i>
       <span class="title">{$menu->menu_title}</span>
       <span class="selected"></span>
      </a>
     </li>
    {/if}
    {/foreach}
  </ul>

Teraz sa práve snažím rozbehnuť breadcrumb

<ul class="breadcrumb">
    {if $user->isLoggedIn()}
     {foreach $menus as $breadcrumbmenu}
      <li>
        <i class="{$breadcrumbmenu->menu_icon}"></i>
        <a href="{$breadcrumbmenu->menu_link}">{$breadcrumbmenu->menu_title}</a>
        <i class="icon-angle-right"></i>
      </li>
     {/foreach}
    {/if}
   </ul>

Myslíš, že môže použiť rovnakú metodu ak s menu?

{var $childs = $menu->related('webadmin_menu','parent_id')}
{if $childs->count('*') > 0}
{foreach $childs as $child}
{/foreach} atď...
Tabetha
Člen | 140
+
0
-

principiálne môžeš, ak to máš rovnako viazané ako v tabulke menu