Pouze tento týden sleva až 80 % na e-learning týkající se C# .NET. Zároveň využij akci až 30 % zdarma při nákupu e-learningu - Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 3 - Směrovač (router)

V minulé lekci, .htaccess, autoloader a obecný kontroler, jsme si nastavili .htaccess, vytvořili index.php a obecný kontroler.

Dnes se budeme věnovat směrovači, tedy komponentě, která podle URL adresy nasměruje požadavek na správný kontroler.

Směrovač

Směrovač (anglicky router) dostane URL adresu, kterou si zpracuje a na jejím základě zavolá příslušný kontroler. Opět je několik způsobů, kterými lze směrovač implementovat a opět jsem vybral ten nejjednodušší. Princip směrovače nápadně připomíná princip kontroleru, toho také využijeme, směrovač bude kontroler.

Směrovač tedy přidáme jako kontroler. Ve složce kontrolery/ vytvoříme soubor SmerovacKontroler.php a do něj stejnojmennou třídu, která bude dědit z obecné třídy Kontroler, kterou jsme si udělali minule. Nezapomeneme implementovat abstraktní metodu zpracuj().

Směrovač dle adresy zjistí, který kontroler voláme, vytvoří nám ho a zároveň i uloží do atributu $kontroler, který třídě rovněž přidáme.

<?php

class SmerovacKontroler extends Kontroler
{

    protected Kontroler $kontroler;

    public function zpracuj(array $parametry) : void
    {

    }

}

Možná je trochu matoucí, že v sobě máme vlastně vložené 2 kontrolery, ale je to tak. Kontroler SmerovacKontroler bere URL od uživatele, tu zpracuje a podle ní zavolá příslušný vložený kontroler (např. ClanekKontroler). Oba kontrolery budou mít pohled, SmerovacKontroler bude mít šablonu s rozložením stránky (hlavička, menu atd.) a vložený kontroler bude mít šablonu s obsahem dané stránky (článek, kontaktní formulář, přihlašovací obrazovka atd.).

Parsování URL adresy

Jako první si naparsujeme URL adresu a to přímo v PHP. Adresa nám přijde jako textový řetězec, bude vypadat stejně, jako ta v adresním řádku. Pojďme se dohodnout nejprve na jejím formátu, který bude následující a který jsme již minule naťukli:

http://www.domena.cz/kontroler/parametr2/parametr3

V praxi by vypadala třeba takto:

http://www.domena.cz/clanek/nazev-clanku

Říkáme, že voláme kontroler ClanekKontroler a předáváme mu parametr "nazev-clanku". Kontroler si už článek vezme od modelu a předá ho pohledu, který ho vypíše uživateli.

Např. z této URL:

http://www.domena.cz/kontroler/parametr2/parametr3
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

bychom potřebovali získat takového pole:

Array
(
    [0] => kontroler
    [1] => parametr2
    [2] => parametr3
)

Přijdeme třídě tedy metodu parsujURL(), která bude jako argument přijímat URL adresu a vrátí nám výše zmíněné pole:

private function parsujURL(string $url) : array
{

}

K naparsování url adresy nám PHP poskytuje funkci parse_url(), která za nás sice neudělá všechnu práci, ale pomůže nám oddělit část s protokolem a doménou od části s parametry. Funkce vrátí asociativní pole, část /kontroler/parametr2/parametr3 dostaneme pod indexem path. Pozor si dáme jen na "/", kterým může tato část začínat, pokud tam je, jednoduše ho odstraníme pomocí funkce ltrim(). Odstraníme také bílé znaky kolem URL (kdyby za ni třeba někdo napsal mezeru), k tomu slouží PHP funkce trim().

$naparsovanaURL = parse_url($url);
$naparsovanaURL["path"] = ltrim($naparsovanaURL["path"], "/");
$naparsovanaURL["path"] = trim($naparsovanaURL["path"]);

Nyní není nic jednoduššího, než si výsledný řetězec rozbít podle lomítek na pole jednotlivých parametrů:

$rozdelenaCesta = explode("/", $naparsovanaURL["path"]);

Výsledek vrátíme a funkce k parsování URL je hotová:

return $rozdelenaCesta;

Volání vloženého kontroleru

Přesuňme se do metody zpracuj(), která se nám na začátku požadavku zavolá. V argumentu $parametry budeme očekávat pole, kde na 1. indexu bude URL adresa. Tu si naparsujeme pomocí naší metody:

$naparsovanaURL = $this->parsujURL($parametry[0]);

Zatím nebudeme ošetřovat případy, kdy je URL zadána nekompletní nebo neexistující.

Získání názvu třídy

První úkol bude zjistit jméno třídy kontroleru. Již víme, že název kontroleru je 1. parametr v naparsované URL adrese. Pro URL adresy však platí jiné konvence, než pro názvy tříd. Proto nám název kontroleru přijde jako "nazev-kontroleru" a my z něj potřebujeme vytvořit "NazevKontrole­ruKontroler", tedy např. řetězec "clanek" převedeme na "ClanekKontroler" a "vypis-uzivatelu" na "VypisUzivate­luKontroler". Na tento převod si opět vytvoříme ve třídě metodu a pojmenujeme ji pomlckyDoVelbloudiNotace(). Metoda bude přijímat i vracet řetězec:

private function pomlckyDoVelbloudiNotace(string $text) : string
{

}

Použijeme PHP funkci ucwords(), která zvětší počáteční písmeno všech slov ve větě. Funkce sice není UTF-safe, ale to nám zde nevadí. Z parametru si tedy nejprve uděláme větu a to jednoduše tak, že pomlčky nahradíme mezerami:

$veta = str_replace('-', ' ', $text);

Následně zvětšíme první písmena slov a mezery odstraníme:

$veta = ucwords($veta);
$veta = str_replace(' ', '', $veta);

To je vše. Kód by šel napsat i do jednoho řádku, ale pro potřeby tutoriálu jsem ho rozepsal. Metoda převede např. řetězec "vypis-uzivatelu" na VypisUzivatelu. Výsledek navrátíme:

return $veta;

Vraťme se do metody zpracuj() a zjistěme si název třídy kontroleru pomocí naší nové metody:

$tridaKontroleru = $this->pomlckyDoVelbloudiNotace(array_shift($naparsovanaURL)) . 'Kontroler';

Pomocí array_shift() jsme získali 1. parametr z naparsované URL a zároveň ho z tohoto pole i odstranili, již ho tam totiž nebudeme potřebovat.

Název třídy kontroleru si pro testovací účely pouze vypišme:

echo($tridaKontroleru);

Stejně tak si vypišme i zbytek pole z URL adresy:

echo('<br />');
print_r($naparsovanaURL);

Volání směrovače

Přesuňme se do index.php a vytvořme si zde směrovač. Necháme ho zpracovat URL adresu, kterou v PHP nalezneme v superglobálním poli $_SERVER pod klíčem REQUEST_URI. Hodnotu ještě musíme vložit do pole, jelikož to metoda zpracuj() očekává:

$smerovac = new SmerovacKontroler();
$smerovac->zpracuj(array($_SERVER['REQUEST_URI']));

Díky autoloaderu z minula se nám třída SmerovacKontroler sama načte. Celou magii si vyzkoušíme, zadáme následující URL:

localhost/clanek/nazev-clanku/dalsi-parametr

A měli bychom dosáhnout následujícího výsledku:

Your page
localhost/cla­nek/nazev-clanku/dalsi-parametr

Směrovač nám tedy funguje, z URL adresy nám zjistil název kontroleru a pole parametrů. Pro dnešek toho již bylo dost.

Příště, v lekci Propojení kontroleru a pohledu, si propojíme pohled s kontrolerem.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 3292x (4.44 kB)
Aplikace je včetně zdrojových kódů v jazyce PHP

 

Předchozí článek
.htaccess, autoloader a obecný kontroler
Všechny články v sekci
MVC - Jednoduchý redakční systém v PHP objektově
Přeskočit článek
(nedoporučujeme)
Propojení kontroleru a pohledu
Článek pro vás napsal David Čápka
Avatar
Uživatelské hodnocení:
63 hlasů
David je zakladatelem ITnetwork a programování se profesionálně věnuje 13 let. Má rád Nirvanu, sushi a svobodu podnikání.
Unicorn university David se informační technologie naučil na Unicorn University - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity

 

 

Komentáře
Zobrazit starší komentáře (97)

Avatar
Jan Lupčík
Supertvůrce
Avatar
Jan Lupčík:9.6.2019 16:14

Ahoj, máš vytvořený soubor .htaccess s obsahem z minulé lekce?

Odpovědět
9.6.2019 16:14
TruckersMP vývojář
Avatar
Petr
Člen
Avatar
Petr:11.6.2019 10:42

Nefunguje mi "RewriteEngine", ani na primitivní akci. Samozřejmě na vzorové také ne.

_htaccess
Options -Indexes
RewriteEngine On
RewriteRule index\.html indexy.html

Mám Linux Mint 19 Cinnamon, apache2 php 7.2. Používám Localhost.

Virtual host mi v home funguje normálně, včetně databáze. Modul rewrite.so je v /usr/lib/apache2/. Asi mod Rewrite na Localhostu nesmí fungovat. Nevím.

 
Odpovědět
11.6.2019 10:42
Avatar
Petr
Člen
Avatar
Petr:12.6.2019 19:33

sudo a2enmod rewrite, to byl ten správný příkaz roota, aby apache povolilo mod RewriteEngine. Potom v htaccess příkaz "RewriteEngine On" dostal smysl.
Dále v sites-enable ve virtual.conf bylo třeba změnit příkazy na:
Options Indexes FollowSymLinks MultiViews
AllowOverride All ... nikoliv none

Restart apache2 a bylo vše funkční.
Takže problém byl jenom v konfiguraci apache, který jsem netušil, jak ho vyřešit.
Google pomohl.

 
Odpovědět
12.6.2019 19:33
Avatar
Matěj Přerovský:17.2.2020 12:41

Ahoj,
už druhý den řeším jeden problém.

<?php
// Nastavení interního kódování pro funkce pro práci s řetězci
mb_internal_encoding("UTF-8");
// Callback pro automatické načítání tříd controllerů a modelů
function autoloadFunkce($trida)
{
        // Končí název třídy řetězcem "Kontroler"
    if (preg_match('/Kontroler$/', $trida))
        require("kontrolery/" . $trida . ".php");
    else
        require("modely/" . $trida . ".php");
}

spl_autoload_register("autoloadFunkce");

// Vytvoření routeru a zpracování parametrů od uživatele z URL
$smerovac = new SmerovacKontroler();
$smerovac->zpracuj(array($_SERVER['REQUEST_URI']));

echo("Pokus");

Když zakomentuju

$smerovac = new SmerovacKontroler();
$smerovac->zpracuj(array($_SERVER['REQUEST_URI']));

, tak to správně vypíše Pokus.

Jinak tam je jen bílá plocha.

Mám PHP 7.3.12 a je to na Endoře.

Veškerý kód jsem si nakonec zkopíroval, ale stejně to nefunguje.

Pomůžete mi prosím?

Editováno 17.2.2020 12:42
Odpovědět
17.2.2020 12:41
42
Avatar
Milan Turyna
Tvůrce
Avatar
Odpovídá na Matěj Přerovský
Milan Turyna:17.2.2020 14:01

Nevím jak je to s tímhle návodem, nečetl jsem ho, ale možná když půjdeš na další lekci tam se podíváš jak to propojit a napíšeš pokus do pohledu tak by to mohlo jít.

 
Odpovědět
17.2.2020 14:01
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Stepan
Člen
Avatar
Stepan:23.7.2020 14:04

Ahoj, řeším jeden problém, kde a jak vyřeším, mám-li v xamppu projekt v podsložce např: localhost/mvc/­......
Pokaždé mě to přesměruje na chyba stylu localhost/chyba - nenajde mi třídu .
Nechal jsem si vypsat co jde do $tridaKontroleru a tam mám právě podložku MVC a není v ní žádný kontroler.
Dám-li projekt do hlavní složky, vše funguje.
Nertuší někdo jak tuto věc vyřešit?

 
Odpovědět
23.7.2020 14:04
Avatar
jozef.stropko:24.7.2020 11:47

Stepan, neviem, či Ti poradím. Ja nemám projekt v koreňovej zložke htdocs, ale v podzložke cms01. Pri zadaní echo($triedaKon­troleru); vo funkcíí spracuj(), ktorá je v SmerovacKontroler-i na stránke sa vypíše Cms01Kontroler. Akurát som zmenil v premennej $triedaKontroleru vo funkcii spracuj() array_shift($na­parsovanaURL) na $naparsovanaURL[1], keďže predpokladám, že array_shift vyberie z poľa prvý index (t.j. nultý), čo je podzložka cms01 a index [1] je už názov požadovanej zložky čo je clanok ([0] - cms01 [1] - clanok)

Netvrdím, že moje riešenie je správne, snažím sa pochopiť MVC a osobne som si projekt založil v podzložke a nie v koreňovej zložke htdocs, keďže tam mám mnoho či už projektov, alebo učebných súborov a kvôli mvc, ktoré by bolo v htdocs to nechcem dávať všetko preč.

 
Odpovědět
24.7.2020 11:47
Avatar
jozef.stropko:24.7.2020 11:49

Aby som upresnil adresa u mňa je v podzložke localhost/cms01

 
Odpovědět
24.7.2020 11:49
Avatar
Marcel Sup
Člen
Avatar
Odpovídá na jozef.stropko
Marcel Sup:16.8.2021 11:39

Já také jsem si vložil projekt do podsložky (RS) a řešil jsem funkční zprovoznění. Myslím, že se mi to podařilo vytvořením konstanty, která se skládá z názvu podsložky opatřenou po obou stranách lomítky v abstraktní třídě Kontroler. Je potřeba předsadit tuto konstantu ve funkci presmeruj (v abstr.třídě Kontroler) před url adresu při přesměrování.Dále je potřeba místo pouhého lomítka vytrimovat z výrazu ve třídě SmerovacKontroler právě tuto konstatntu.

abstract class Kontroler {

    protected $data = array();
    protected $pohled = "";
    protected $hlavicka = array('titulek' => '', 'klicova_slova' => '', 'popis' => '');

    const ABSOLUTE_PATH = "/RS/";

    abstract function zpracuj($parametry);

    public function vypisPohled() {
        if ($this->pohled) {
            extract($this->data);
            require("pohledy/" . $this->pohled . ".phtml");
        }
    }

    public function presmeruj($url) {
        header("Location: " . self::ABSOLUTE_PATH . $url );
        header("Connection: close");
        exit;
    }

}
 private function parsujURL($url) {
        $naparsovanaURL = parse_url($url);
        $naparsovanaURL["path"] = ltrim($naparsovanaURL["path"], Kontroler::ABSOLUTE_PATH );
        $naparsovanaURL["path"] = trim($naparsovanaURL["path"]);
        $rozdelenaCesta = explode("/", $naparsovanaURL["path"]);
        return $rozdelenaCesta;
 
Odpovědět
16.8.2021 11:39
Avatar
Jirka
Člen
Avatar
Jirka:21. února 18:57

Nepoužívám subdomény pro localhost a mám v jedné složce více projektů atd.
Upravil jsem tedy parametry pro směrovač takto:

$url_web = "/test/web";
$url_web = str_replace("$url_web", "", $_SERVER['REQUEST_URI']);
$smerovac = new SmerovacKontroler();
$smerovac->zpracuj(array($url_web));

Funguje mi to stejně, pokud mám například localhost/tes­t/web/clanek/na­zev-clanku/dalsi-parametr.
Může to mít nějaký negativní vliv na další lekce, nebo fungování projektu?

 
Odpovědět
21. února 18:57
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 107. Zobrazit vše