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í.
Pouze tento týden sleva až 80 % na e-learning týkající se Pythonu. Zároveň využij slevovou akci až 30 % zdarma při nákupu e-learningu - Více informací.
python week + discount 30

Lekce 2 - .htaccess, autoloader a obecný kontroler

V minulé lekci, Popis MVC architektury, jsme si vysvětlili MVC architekturu.

Již víme, že stránka se bude skládat z komponent 3 typů: kontrolerů, modelů a pohledů. Dnes si připravíme prostředí, začneme souborem .htaccess, dále si v index.php nastavíme PHP dle našich potřeb a vytvoříme předka pro naše kontrolery.

Co budete potřebovat je zprovozněný lokální server (viz. XAMPP - Instalace Apache, PHP a MySQL na Windows), ale to již určitě máte. Přesuňme se tedy do výchozí složky (v XAMPP defaultně c:/xampp/htdocs) a vše v ní odstraňme.

Pozor! Je opravdu důležité, aby jste systém vložili právě do této kořenové složky, když pro něj uděláte podsložku, nebude fungovat!

Je to kvůli hezkým URL adresám, viz dále. Pokud máte na localhostu více projektů, nastavte pro projekt subdoménu.

Adresářová struktura

Vytvořme si 3 složky pro nám již známe 3 typy komponent. Budou se jmenovat: kontrolery/, modely/ a pohledy/. Rovnou si zde připravte i soubory .htaccess a index.php.

Adresářová struktura MVC frameworku v PHP

.htaccess

Určitě znáte soubor .htaccess. Slouží ke konfiguraci webserveru Apache a určuje, co se má stát, když uživatel vyťuká URL adresu. Vše se děje ještě předtím, než se vůbec spustí jazyk PHP jako takový.

Vytvořme tedy tento soubor (i s tou tečkou na začátku názvu) v kořenové složce. Kolega jednou pravil, že stejně jako nechce pochopit ženu, tak nechce pochopit .htaccess. Nastavení Apache je opravdu dost krkolomné a proto si s ním nelamte hlavu a berte ho jako hotovou věc.

Jako první zakážeme výpis souborů ve složce na webu, budou tedy zvenku skryty, což my chceme. Přidáme řádek:

Options -Indexes

Hezké URL adresy

Klasická adresa PHP aplikace vypadá asi takto:

http://wwww.domena.cz/index.php?clanek=nazev-clanku&parametr=hodnota

To je poměrně ošklivé, že? V naší aplikaci budeme používat tzv. hezké URL adresy, ta samá adresa bude u nás vypadat takto:

http://wwww.domena.cz/nazev-clanku/hodnota

To je mnohem hezčí. Zpracování URL adres necháme plně na PHP a v Apache toto "vypneme" a všechny dotazy přesměrujeme na soubor index.php, kde si URL sami zpracujeme. Apache by totiž jinak bral nazev-clanku jako složku a tam hledal podsložku hodnota a v ní index. Zapneme přesměrovávací engine a přidáme pravidlo k přesměrování, kde až na určité typy souborů v URL přesměrujeme vždy na index.php:

RewriteEngine On
# RewriteBase /

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule !\.(css|js|icon|zip|rar|png|jpg|gif|pdf)$ index.php [L]

Znak "#" je komentář, příkaz RewriteBase / je tedy zakomentovaný. To proto, že na localu ho nepotřebujeme, ale některé webservery v produkci (až budeme web nahrávat na internet) bez tohoto přepínače nefungují, až budete hotový web nahrávat na server, musíte to zkusit :)

RewriteCond určují, že se nemá přesměrovat v případě, že soubor nebo složka existuje. Pokud tedy voláme např.:

http://wwww.domena.cz/soubor.txt

Bude tento soubor stažen (pokud existuje) a pokud ne, budeme přesměrováni na index. Kdybychom přesměrovali úplně vše (bez těch několika podmínek), nemohli bychom stahovat žádné soubory a vždy by se nám zobrazil index. Přesměrování jsme si ještě pojistili výčtem nejdůležitějších přípon, které se nebudou přesměrovávat. Všechny ostatní URL adresy směřují na index.

Jako poslední do .htaccess přidáme zpracování přípony .phtml, aby nám nikdo nekoukal do zdrojáků šablon. Soubor .phtml se bude chovat úplně stejně, jako soubor .php. Přidejme tento řádek:

AddType application/x-httpd-php .php .phtml

Zde máme hotovo.

index.php

Vytvořme si index.php, tedy soubor, ve kterém započne veškerá komunikace a kam se budou směřovat všechny URL adresy.

Celý web budeme tvořit v kódování UTF-8, nastavte si tedy toto kódování jako výchozí ve svém editoru. Pokud používáte IDE (PHPStorm, Netbeans, Eclipse), tak tam je již velmi pravděpodobně nastavené. Pokud se vás bude editor ptát na BOM, tak s ním pracovat nebudeme (kódování UTF-8 without BOM).

Jako první samozřejmě vložíme direktivu <?php a nastavíme PHP interní kódování na UTF-8, díky tomu poté budou správně fungovat PHP funkce pro práci s textovými řetězci. Docílíme toho takto:

<?php
mb_internal_encoding("UTF-8");

Autoloader

V systému budeme často tvořit instance různých tříd a je velmi nepohodlné tyto třídy ručně requirovat/in­cludovat. Proto PHP disponuje možností vytvořit funkci, která se zavolá poté, co je volána neexistující třída. V této funkci si třídu načteme. Třídy se tedy automaticky načítají ve chvíli, kdy je potřebujeme použít.

Načítat budeme vlastně 2 typy tříd: modely (ze složky modely/) a kontrolery (ze složky kontrolery/). Pohledy u nás nebudou třídami a budou připomínat spíše HTML stránky. Naše autoload funkce tedy bude muset poznat, zda se jedná o model nebo o kontroler a podle toho sáhne do příslušné složky. Jak to uděláme?

Opět úplně jednoduše, názvy tříd kontrolerů budou končit na "Kontroler". Funkce autoload by mohla vypadat takto:

function autoloadFunkce(string $trida) : void
{
    // Končí název třídy řetězcem "Kontroler" ?
    if (preg_match('/Kontroler$/', $trida))
        require("kontrolery/" . $trida . ".php");
    else
        require("modely/" . $trida . ".php");
}

Prvotní podmínka používá tzv. regulární výraz, který ověří, zda název třídy končí řetězcem "Kontroler". Pokud ano, načte soubor ze složky kontrolery/, v opačném případě třídu bude hledat ve složce modely/.

Naší funkci ještě zaregistrujeme, aby ji PHP vykonávalo jako autoloader:

spl_autoload_register("autoloadFunkce");

Až budeme mít nějaké kontrolery a modely, bude nám stačit napsat:

$mu = new ManazerUzivatelu();
$smerovac = new SmerovacKontroler();

Jelikož PHP nebude vědět o třídách ManazerUzivatelu.php ve složce modely/ a třídě SmerovacKontroler.php ve složce kontrolery/, zavolá naší funkci spl_autoload_register(). Ta nám v prvním případě vykoná příkaz:

require('modely/ManazerUzivatelu.php');

a v druhém případě:

require('kontrolery/SmerovacKontroler.php');

Stačí založit třídu a nemusíme se o nic starat.

Třída se samozřejmě musí jmenovat stejně, jako soubor, ve kterém je obsažena, ale tak by tomu mělo být vždy.

Ve velmi starém PHP, které bohužel ještě běží na některých free serverech, není možné používat funkci spl_autoload_register() a je místo ní třeba použít starší __autoload(), viz PHP manuál.

Obecný kontroler

Vytvořme si ještě abstraktní třídu pro obecný kontroler, ze kterého budou všechny naše kontrolery dědit. Ve složce kontrolery/ si založíme Kontroler.php. Na začátek souboru nezapomeneme vložit direktivu <?php.

Třída bude obsahovat 3 atributy. První bude pole s daty, tam si bude kontroler ukládat data získaná od modelů. Toto pole se následně předá pohledu, který data vypíše uživateli. Tímto způsobem tedy zrealizujeme předávání dat mezi modelem a pohledem. Druhý atribut bude název pohledu, který se má vypsat. Poslední atribut bude hlavička HTML stránky, přesněji 3 její atributy: titulek, keywords a description, kterou musí mít každá HTML stránka. Atributům přiřadíme i výchozí hodnoty a datové typy, to je možné od verze PHP 7.

<?php
abstract class Kontroler
{

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

}

Třída bude mít další 3 metody. Jedna bude ta hlavní, ve které zpracuje kontroler své parametry. Tu si bude každý kontroler implementovat sám, proto ji zde označíme pouze jako abstraktní. Pojmenujeme ji jednoduše zpracuj() a její argument pojmenujeme $parametry. Jako datový typ použijeme array. Funkce nebude sama o sobě vracet žádnou hodnotu a proto jí nastavíme návratový typ na void:

abstract function zpracuj(array $parametry) : void;

Další funkce vypíše pohled uživateli. Pokud je nějaký pohled zadaný, jednoduše ho requirujeme. Předtím rozbalíme proměnné z pole $data pomocí funkce extract(). Všechny indexy (klíče) pole budou v šabloně přístupné jako běžné proměnné. Funkce také nevrací žádnou hodnotu:

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

Pokud si tedy v kontroleru uložíme takto nějaký klíč do pole:

$this->data['promenna'] = 'hodnota';

V šabloně bude proměnná přístupná jednoduše takto:

<strong>Proměnná je: </strong><?= $promenna ?>

Pokud jste si všimli, že nejsou ošetřené HTML entity, máte bod. Časem to napravíme.

Jako poslední metodu si do kontroleru přidáme jednoduché přesměrování na jinou stránku a zastavení zpracování současného skriptu. Bude se nám často hodit. Jako argument bude funkce přijímat řetězec s URL adresou, na kterou má přesměrovat. Jelikož běh skriptu končí uvnitř funkce, použijeme pro ní návratový typ never.

public function presmeruj(string $url) : never
{
    header("Location: /$url");
    header("Connection: close");
    exit;
}

Zdrojové kódy dnešního výtvoru máte samozřejmě jako vždy ke stažení níže.

V příští lekci, Směrovač (router), si vytvoříme svůj první kontroler, bude jím směrovač (router) :)


 

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 3926x (3.01 kB)
Aplikace je včetně zdrojových kódů v jazyce PHP

 

Předchozí článek
Popis MVC architektury
Všechny články v sekci
MVC - Jednoduchý redakční systém v PHP objektově
Přeskočit článek
(nedoporučujeme)
Směrovač (router)
Článek pro vás napsal David Čápka
Avatar
Uživatelské hodnocení:
80 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 (107)

Avatar
Milan Turyna
Tvůrce
Avatar
Milan Turyna:25.1.2020 20:51

Moc jsem nepochopil tvou otazku. V PhpStormu ale editovat .htaccess můžeš.

 
Odpovědět
25.1.2020 20:51
Avatar
Odpovídá na Milan Turyna
Vojtech Palec:26.1.2020 10:20

Ano, na to jediné jsem se ptal, děkuju.

 
Odpovědět
26.1.2020 10:20
Avatar
lukasfornusek:31.1.2020 18:47

Nazdar BEJCI !!!
Problém na obzoru a to s třídou Kontroler. Jest toliko abstraktní, nevytvářím podle ní žádnou instanci a v kódu taky nikde není $blabla = new Kontroler ( ).
Jenže, JENŽE pokud v class Kontroler mám
public function __construct( ) { echo (' TEXT ')} a já mám, tak se mi text TEXT vypíše na obrazovku. To je ale škaredá legrace co !. Instanci netvořím, new nepíšu, ale text mam.
Jak je to vlastně je ???

Dík a moc se nevožerte (je pátek).

 
Odpovědět
31.1.2020 18:47
Avatar
LudvaCT
Člen
Avatar
LudvaCT:17.6.2020 14:19

ahoj, nedaří se mi pochopit fci extract v metodě Kontroleru
<code>
public function vypisPohled()
{
if ($this->pohled)
{
extract($this->data);
require("pohledy/" . $this->pohled . ".phtml");
}
}
</code>

píšete, že "Všechny indexy (klíče) pole budou v šabloně přístupné jako běžné proměnné."

nechápu, jak se mi ty proměnné dostanou ven z té funkce.. zdá se mi že nic nevrací pouze něco requiruje a extrahuje proměnné z pole dovnitř té funkce. Děkuji

Editováno 17.6.2020 14:21
 
Odpovědět
17.6.2020 14:19
Avatar
Radek Veverka
Tvůrce
Avatar
Odpovídá na LudvaCT
Radek Veverka:17.6.2020 15:52

Nedostanou se ven z funkce, ten vložený kód přes require se vykoná uvnitř té funkce, takže na všechny lokální proměnné vidí. Funkce je opuštěna až poté, co se provedé celý kód z vloženého souboru.

Z dokumentace PHP:

When a file is included, the code it contains inherits the variable scope of the line on which the include occurs. Any variables available at that line in the calling file will be available within the called file, from that point forward. However, all functions and classes defined in the included file have the global scope.

 
Odpovědět
17.6.2020 15:52
Avatar
LudvaCT
Člen
Avatar
Odpovídá na Radek Veverka
LudvaCT:17.6.2020 23:48

Děkuji za rychlou odpověď, moc jsi mi pomohl. Nyní nechápu, jak jsem to mohl předtím nechápat:)

 
Odpovědět
17.6.2020 23:48
Avatar
kozak.martin
Člen
Avatar
kozak.martin:2.3.2021 17:32

Ahoj, prosím o radu...v tomto tutoriálu jsem si nastavil v index php kódování na UTF-8 jak je uvedeno výše: mb_internal_en­coding("UTF-8");, jenže kódování nefunguje. Používám ApacheNetBeans. Když jsem dělal předešlé úkoly v htdocs, kódování fungovalo v pořádku, ale problém nastal, jakmile jsem všechny složky z htdocs odstranil, abych mohl programovat v MVC architektuře.
V dalším díle tohoto tutoriálu se mi správně nevypisuje chybová stránka...dia­kritika

Díky předem za odpověď
Martin

 
Odpovědět
2.3.2021 17:32
Avatar
Lukáš Navrátil:14.3.2021 15:24

Zdravím,

odkud se prosím bere proměnná $trida pro funkci autoloadFunkce? Kde jí definujeme/za­dáváme?

Děkuji.

 
Odpovědět
14.3.2021 15:24
Avatar
Martin Joukl
Člen
Avatar
Odpovídá na Lukáš Navrátil
Martin Joukl:25.6.2021 10:22

Tu proměnnou si PHPko nastaví samo podle jména třídy, kterou právě voláš (tzn. pro volání - new Kniha() - bude hodnota proměnné Kniha).

 
Odpovědět
25.6.2021 10:22
Avatar
Jan Zahradník:19. ledna 17:08

Ahoj, co je důvodem vše ze složky odstranit a mít tam jen tento jediný projekt nebo mít subdoménu? Pokud bych to měl jako další projekt ve stejné složce, tak by to nefungovalo? - Text ze začátku lekce:Přesuňme se tedy do výchozí složky (v XAMPP defaultně c:/xampp/htdocs) a vše v ní odstraňme.
Díky
Honza

 
Odpovědět
19. ledna 17:08
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 117. Zobrazit vše