Dynamické menu z databáze

Altimit
Člen | 82
+
0
-

Dobrý den, chci se zeptat jak by šla udělat určitá věc.
Mám udělanou databázi stylem:

id name hidden autor type category_id
1 polozka1 0 1 menu 0
2 polozka2 0 1 submenu 1
3 polozka3 0 1 submenu 1

Kdy name je název, hidden je nastavení, jest-li se má zobrazovat nebo skrývat, autor odkazuje na tabulku user_id, type určuje co to vlastně je a category_id má odkazovat na id.

Jedná se o to, že chci udělat menu, které má vypadat nějak takhle:

Jedna z podmínek zadavatelky byla, že chce mít možnost přidávat další položky, jinak by jsem tohle neřešil.
Vím jak udělat ty normální menu, kdy v MenuControl mi načítá data z databáze přes položku type = menu, ale nastává problém že nevím jak udělat to submenu, které se automaticky doplní do seznamu pod položku menu a položka menu se změní na collapsible položku..
Nějaké nápady? děkuji.

Kcko
Člen | 339
+
0
-

Říká se tomu rekurze, prostě zanoříš block menu do menu. Někde to tu bylo, ale je to lehké na napsání.
K té tvé struktuře, ukládej si ID rodičovské položky.
(Nejsnadněji pro tebe ⇒ https://latte.nette.org/cs/macros#… „V bloku lze vložit i sebe sama, což lze použít např. pro vykreslení stromového menu.“)
A to aby to bylo collapsible nemá s Nette nic společného, zařídíš snadno přes JavaScript.

Editoval Kcko (21. 3. 2018 14:51)

Altimit
Člen | 82
+
0
-

@Kcko Děkují za pořádné nakopnutí.
Ale vyskytl se obrovský problém, a to s tímto:

Duplikují se výsledky a když jsem to zkoušel profiltrovat tak kompletně zmizel i submenu

CZechBoY
Člen | 3608
+
0
-

bez kodu ani kure nehrabe…

Altimit
Člen | 82
+
0
-

@CZechBoY sorry, dodávám
MenuControl.php

public function render($id = NULL)
    {
        $this->template->setFile(__DIR__ . '/template.latte');
        $db = $this->database->table('category')->where(' category_id IS NULL');
        $this->template->menu = $db;
        $this->template->render();
    }

template.latte

<style>
    .collapsible-body{
        padding: 0!important;
    }

</style>
    <h5>Kategorie</h5>
{block #menu}
    <ul class="collection white collapsible">
    {foreach $menu as $item}
        {php $selection = $item->related('category','category_id')}
        {if $selection->count()>0}
            {if $item->category_id === NULL}
                <li>
                    <a class="center red-text collapsible-header">{$item->name} <i class="material-icons right" style="margin-right:0;">arrow_drop_down</i></a>
                    <div class="collapsible-body">
                        <ul>
                            {include #menu menu => $selection}
                        </ul>
                    </div>
                </li>
            {elseif $item->category_id !== NULL}
            {/if}
            {*{if $item->category_id === NULL}</ul>{elseif $item->category_id !== NULL}</li>{/if}
            *}{include #menu menu => $selection}
        {else}
                <a class="red-text collection-item center-align" href="#{$item->name|webalize}-{$item->id}"><li>{$item->name}</li></a>

        {/if}
    {/foreach}
    </ul>
{/block}
Piticu
Člen | 93
+
0
-

Co znamena ta podminka category_id IS NULL? Nevim, zda pomaha tvemu problemu, ale jsem zvedavy jaky ma vyznam :) Jinak mel by sis udelat nejaky model pro vyber a filtrovani dat z db a nechat si latte jen pro vykreslovani.
Muzes prosim ukazat si tabulku Category ?

Altimit napsal(a):

@CZechBoY sorry, dodávám
MenuControl.php

public function render($id = NULL)
    {
        $this->template->setFile(__DIR__ . '/template.latte');
        $db = $this->database->table('category')->where(' category_id IS NULL');
        $this->template->menu = $db;
        $this->template->render();
    }

template.latte

<style>
    .collapsible-body{
        padding: 0!important;
    }

</style>
    <h5>Kategorie</h5>
{block #menu}
    <ul class="collection white collapsible">
    {foreach $menu as $item}
        {php $selection = $item->related('category','category_id')}
        {if $selection->count()>0}
            {if $item->category_id === NULL}
                <li>
                    <a class="center red-text collapsible-header">{$item->name} <i class="material-icons right" style="margin-right:0;">arrow_drop_down</i></a>
                    <div class="collapsible-body">
                        <ul>
                            {include #menu menu => $selection}
                        </ul>
                    </div>
                </li>
            {elseif $item->category_id !== NULL}
            {/if}
            {*{if $item->category_id === NULL}</ul>{elseif $item->category_id !== NULL}</li>{/if}
            *}{include #menu menu => $selection}
        {else}
                <a class="red-text collection-item center-align" href="#{$item->name|webalize}-{$item->id}"><li>{$item->name}</li></a>

        {/if}
    {/foreach}
    </ul>
{/block}

Editoval Piticu (23. 3. 2018 8:05)

Altimit
Člen | 82
+
0
-

@Piticu Co přesněji jako myslíš? to co jsem psal hned v úvodu? nebo chceš i data?…
tady je obojí opět.

Omlouvám se, že píšu tak pozdě, ale je to způsobené mojí vytíženosti :)

Piticu napsal(a):

Co znamena ta podminka category_id IS NULL? Nevim, zda pomaha tvemu problemu, ale jsem zvedavy jaky ma vyznam :) Jinak mel by sis udelat nejaky model pro vyber a filtrovani dat z db a nechat si latte jen pro vykreslovani.
Muzes prosim ukazat si tabulku Category ?

suwer
Člen | 33
+
+1
-

To reseni na strane kodu je cele trochu nestastne :-). V presenteru si vytahujes pouze top kategorie, v sablone pak odpalujes jeden select za druhym pro jejich podkategorie. A pred samotnym vykreslenim jeste znova kontrolujes, ze je to fakt top kategorie.

Vytahni si cele menu z db naraz a transformuj ho dle svych pozadavku do vice rozmerneho pole (nebo objektu). To pak predej do sablony a vykresli.

Altimit
Člen | 82
+
0
-

ale já vůbec nevím jak, jinak by jsem to tak udělal :D
Tohle co tam je tak je řešení už od někoho udělané…
:)

suwer napsal(a):

To reseni na strane kodu je cele trochu nestastne :-). V presenteru si vytahujes pouze top kategorie, v sablone pak odpalujes jeden select za druhym pro jejich podkategorie. A pred samotnym vykreslenim jeste znova kontrolujes, ze je to fakt top kategorie.

Vytahni si cele menu z db naraz a transformuj ho dle svych pozadavku do vice rozmerneho pole (nebo objektu). To pak predej do sablony a vykresli.

Kcko
Člen | 339
+
0
-

Altimit napsal(a):

ale já vůbec nevím jak, jinak by jsem to tak udělal :D
Tohle co tam je tak je řešení už od někoho udělané…
:)

suwer napsal(a):

To reseni na strane kodu je cele trochu nestastne :-). V presenteru si vytahujes pouze top kategorie, v sablone pak odpalujes jeden select za druhym pro jejich podkategorie. A pred samotnym vykreslenim jeste znova kontrolujes, ze je to fakt top kategorie.

Vytahni si cele menu z db naraz a transformuj ho dle svych pozadavku do vice rozmerneho pole (nebo objektu). To pak predej do sablony a vykresli.

tak co třeba takhle (objektově)

<?php

$rootNode = new Node('ROOT');
$pcNode   = new Node('pc');
$ntbNode  = new Node('notebook');
$dellNode = new Node('dell');
$acerNode = new Node('acer');

$rootNode->add($ntbNode);
$rootNode->add($pcNode);
$ntbNode->add($dellNode);
$ntbNode->add($acerNode);




//echo showNode($rootNode);

echo "<pre>";
print_R($dellNode);



// echo "<pre>";
// print_R($rootNode);


echo showNodeCrumbs($dellNode);

function showNodeCrumbs($node)
{
    if ($node->parent)
        return  showNodeCrumbs($node->parent) . ' / ' . $node->name;

    else
        return $node->name;
}

function showNode($node, $level = 0)
{
    $out  =  '<ul>';
    $out .= '<li>' . $node->name . " L " . $level;
    if ($node->childrenTotal)
    {
        foreach ($node->children as $childNode)
        {
            $out .= showNode($childNode, $level + 1);
        }
    }

    $out .= '</ul>';

    return $out;
}


class Node
{
    public $name;

    public $children = [];
    public $parent = NULL;
    public $childrenTotal = 0;

    public function __construct($name)
    {
        $this->name = $name;
    }

    public function add($node)
    {
        $this->children[] = $node;
        $this->childrenTotal++;

        $node->setParent($this);

        return $this;
    }

    public function setParent($parent = NULL)
    {
        $this->parent  = $parent;
        return $this;
    }

}
?>

nebo takhle (akorát tam proměnné odkrývám přes global, což je ekl, ale jde o to abych pochopil jak si to máš uložit a jak to rekurzivně projít, ve finále z toho můžeš udělat jednoduchou třídu a globální proměnné udělat jako členské proměnné třídy a bude to fungovat)

<?php

$tree = [



	[1, 'Sport', 0],
	[2, 'Letní', 1],
	[3, 'Zimní', 1],
	[4, 'Fotbal', 2],
		[41, 'Amatéři', 4],
		[42, 'Profíci', 4],
			[420, 'A', 42],
			[5545, 'B', 42],
			[888, 'C', 42],
				[8423388, 'C1', 888],
	[5, 'Hokej', 3],


];

$parents = [];
$itemToParent = [];
foreach ($tree as $t)
{
	$parents[$t[2]][] = $t[0];
	$itemToParent[$t[0]] = $t[2];
}


$itemsHasParent = [];
if ($_GET['id'])
{

	$id = $_GET['id'];

	$itemsHasParent[] = $id;

	while ($itemToParent[$id])
	{
		$itemsHasParent[] = $itemToParent[$id];

		$id = $itemToParent[$id];

	}
}



tree(0, FALSE, 0, 2);
tree(0, TRUE, 0, 4);

function tree($parentId, $openAlways = FALSE, $depth = 0, $maxDepth = 3)
{
	global $tree;
	global $itemsHasParent;

	echo "<ul>";

	foreach ($tree as $t)
	{
		if ($t[2] == $parentId)
		{
			echo "<li><a href='?id=".$t[0]."'>".$t[1]." d:$depth</a>";

			if ( ((in_array($t[0], $itemsHasParent) && !$openAlways) || $openAlways) && $maxDepth > $depth)
				tree($t[0], $openAlways, $depth + 1, $maxDepth);
		}
	}


	echo "</ul>";


}

?>

A pak ještě tu http://lab.rjwebdesign.cz/tree (http://lab.rjwebdesign.cz/tree/index.phps), tam to mám obdobně přes nějakou trapnou třídu.

Editoval Kcko (29. 3. 2018 9:45)

Piticu
Člen | 93
+
0
-

Altimit napsal(a):

@Piticu Co přesněji jako myslíš? to co jsem psal hned v úvodu? nebo chceš i data?…
tady je obojí opět.

Omlouvám se, že píšu tak pozdě, ale je to způsobené mojí vytíženosti :)

Piticu napsal(a):

Co znamena ta podminka category_id IS NULL? Nevim, zda pomaha tvemu problemu, ale jsem zvedavy jaky ma vyznam :) Jinak mel by sis udelat nejaky model pro vyber a filtrovani dat z db a nechat si latte jen pro vykreslovani.
Muzes prosim ukazat si tabulku Category ?

Nebylo by lepsi udelat si 2 tabulky? menu a submenu? v submenu bys mel id_menu a pomoci INNER JOIN bys ses dostal k oboum tabulkam :)

GEpic
Člen | 560
+
+1
-

Stačí jedna tabulka která má závislost sama na sobě, kde prvek bude obsahovat parent_id (nadřazený prvek), to se potom dá pomocí related už zařídit celé. Začneš vypisovat prvky bez parenta a u toho si zjistíš, jestli náhodou tento prvek není parentem nějakému jinému. Tak můžeš udělat menu třeba desetistupňové.

Editoval GEpic (30. 3. 2018 9:58)