Generovaná navigace z URL do základní šablony

Allconius
Člen | 313
+
0
-

Ahoj, jde nějak jednoduše vytvořit dynamickou navigaci pro stránky, např. z URL ? Aby třeba https://projekt.cz/…re/zivotopis vytvorilo v sablone zivotopis navigaci :
→ home (https://projekt.cz) → dokumenty (https://projekt.cz/dokumenty) → formulare (https://projekt.cz/…ty/formulare)

Kamil Valenta
Člen | 762
+
+1
-

explode(‚/‘, $url)

Ačkoliv si myslím, že v praxi bude vždy lépe zohledňovat strukturu např. z DB.

m.brecher
Generous Backer | 765
+
0
-

Generovat navigaci z url ne. Normálně z databáze. Web s několika stránkami může mít takhle jednoduchou navigaci:

Tabulka pro navigaci

SET NAMES utf8;
SET time_zone = '+00:00';
SET foreign_key_checks = 0;
SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';

SET NAMES utf8mb4;

DROP TABLE IF EXISTS `node`;
CREATE TABLE `node` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `slug` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_czech_ci DEFAULT NULL,
  `caption` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_czech_ci NOT NULL,
  `published` tinyint(4) NOT NULL DEFAULT '1',
  `sort` smallint(6) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `caption` (`caption`),
  UNIQUE KEY `sort` (`sort`),
  UNIQUE KEY `slug` (`slug`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_czech_ci;

INSERT INTO `node` (`id`, `slug`, `caption`, `published`, `sort`) VALUES
(1,	'@homepage',	'Homepage',	1,	1),
(2,	'sluzby',	'Služby',	1,	2),
(3,	'ukazky',	'Ukázky',	1,	3),
(4,	'reference',	'Reference',	1,	4);

Komponenta menu:

use App\Model\MenuModel;
use Nette\Application\UI\Control;

class MainMenu extends Control
{
    public function __construct(
        private MenuModel $menuModel,
        private string $currSlug,
    )
    {}

    public function render()
    {
        $this->template->currSlug = $this->currSlug;
        $this->template->items = $this->menuModel->getMenuItems();
        $this->template->render(__DIR__.'/templates/mainMenu.latte');
    }
}

Interface factory komponenty:

use App\Components\MainMenu;

interface MainMenuFactory
{
    public function create(?string $currSlug): MainMenu;
}

Šablona komponenty mainMenu.latte, položka menu aktuální stránky má css třídu selected

<div class="menu-container">
        <nav class="menu">
            {foreach $items as $item}
                {var $selected = $item->slug === $currSlug ? 'selected'}
                <a n:class="'button', $selected" href="{plink ':Article:default', slug: $item->slug}">{$item->caption}</a>
            {/foreach}
    </nav>
</div>

Vykreslení komponenty v layoutu:

{control 'mainMenu'}

Model:

use Nette\Database\Explorer;
use Nette\Database\Table\Selection;

class MenuModel
{
    public function __construct(private Explorer $database)
    {}

    public function getMenuItems(): Selection
    {
        return  $this->database->table('node')->where('published', true)->order('sort');
    }
}

Router – presenter Article pro stránky webu + možnost presenterů administrace, stránky jsou indexovány pomocí parametru slug, což odpovídá url za lomítkem, ale homepage nemá slug '' ale @homepage.

use Nette;
use Nette\Application\Routers\RouteList;
use Nette\Application\Routers\Route;

final class RouterFactory
{
    use Nette\StaticClass;

    public static function createRouter(): RouteList
    {
        $router = new RouteList;
        $router->addRoute('[<slug (?!admin).*>]', [
            'presenter' => [Route::Value => 'Article'],
            'action' => [Route::Value => 'default'],
            'slug' => [Route::Value => '@homepage'],
        ]);
        $router->addRoute('admin[/<presenter>/<action>[/<id>]]');
        return $router;
    }
}

Použití menu v presenteru:

use Nette\Application\UI\Presenter;

final class ArticlePresenter extends Presenter
{
	public function __construct(private MainMenuFactory $mainMenuFactory)

    public function renderDefault(string $slug)
    {
        # zde zpracujeme $slug pro vykreslení příslušné stránky
    }

    public function createComponentMainMenu(): MainMenu
    {
        return $this->mainMenuFactory->create(currSlug: $this->getParameter('slug'));
    }
}

Editoval m.brecher (29. 5. 2023 23:01)

Allconius
Člen | 313
+
0
-

Ahoj, díky za návod, nějak jsem se zase nějak zasekl na předávání proměnné z továrny komponenty, nechápu co tam mám špatně.
Továrna:

declare(strict_types=1);

namespace App\Components\News;

interface NewsControlFactory
{
    public function create(string $url): NewsControl;
}

Komponenta:

class NewsControl extends Control {



    function __construct(
        private DbManager $dbManager,
        private string $url,
    )
    {

    }

A vypíše se:

Nette\DI\ServiceCreationException

Service of type App\Components\News\NewsControl: Parameter $url in NewsControl::__construct() has no class type or default value, so its value must be specified.

nechápu co ještě potřebuje kromě „private string $url“ ?

Marek Bartoš
Nette Blogger | 1177
+
0
-

Jak tu službu registruješ?

m.brecher
Generous Backer | 765
+
-1
-

@Allconius

has no … default value, so its value must be specified.

Nemá předanou hodnotu $url z presenteru do metody create() viz:

public function createComponentMainMenu(): MainMenu
    {
        return $this->mainMenuFactory->create(currSlug: $this->getParameter('slug'));
    }

otestuj si, jestli se předává nenulová hodnota, to zajišťuje Router, který jsem Ti poslal, kde je <slug> v routě sice nepovinný, ale má defaultní nenulovou hodnotu, takže nikdy není null!

'slug' => [Route::Value => '@homepage'],

Doporučuji místo $url používat $slug, je to tak zvykem, jako url je zvykem označovat celé url, slug je jenom jeden segment url, který adresuje článek/produkt !

m.brecher
Generous Backer | 765
+
0
-

@Allconius

ještě opravuji kód factory, správně by parametr $currSlug neměl být nullable:

interface MainMenuFactory
{
    public function create(string $currSlug): MainMenu;
}
m.brecher
Generous Backer | 765
+
0
-

@Allconius

Chyba bude ale pravděpodobně v tom jak píše @MarekBartoš že nebude správně registrovaná služba. Takhle to vypadá, že je registrovaná jako služba komponenta NewsControl ta ale službou být nesmí, naopak je potřeba registrovat jako službu factory NewsControlFactory. Podívej se do Tracy panel DIC, kde jsou červeně aktivní vytvořené služby, kterou službu tam reálně máš ;)