Search
Tématem dnešního cvičení je implementace jednoduchého MVC frameworku, který implementuje variantu Front Controller.
Připomeňte si architekturu MVC a její variantu Front Controller.
Prvním krokem je přesměrování všech dotazů na danou oblast na jediný skript. My toto zařídíme pomocí konfigurace apache a jeho modulu mod_rewrite.
Konfigurace vypada takto (vložte do .htaccess):
RewriteEngine On RewriteBase /~username RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php?rt=$1 [L,QSA]
Pozor, nahraďte /~username vaším uživatelským jménem.
Tímto zařídíme, že všechna volání budou přesměrována soubor index.php a v jeho parametru rt boudou předány další informace.
rt
Uvažujme následující logiku: V URL bude zakódováno jméno action controlleru a v rámci tohoto controlleru také jméno metody, kterou v rámci daného ActionControlleru zavoláme.
Například url http://webdev.felk.cvut.cz/~jmeno/blog/akce1
bude namapováno na takovéto volání: index.php (FrontController) → blogController (ActionController) → akce1 (metoda)
Připravte si následující strukturu projektu:
/classes // bude obsahovat vsechny tridy nactene pomoci magicke funkce __autoload /controllers // bude obsahovat vsechny action controllery. Zavedme jednotne pojmenovani, pokud je volan controller aaa v url, pak bude existovat soubor // /controllers/aaa.php a v nem trida aaaController /views // zde budou soubory view volane z jednotlivych controlleru. budou to soubory .php /index.php //soubor front controlleru, na ktery bude vse presmerovano /.htaccess // soubor s konfiguraci apache nutnou pro FC /bootstrap.php // definice autoload a dalsich veci pri kazdem volani libovolneho skriptu
Bootstrap.php uz známe z minulých cvičení:
bootstrap.php
<?php define ("CLASSDIR", "classes/"); function __autoload($classname) { $filename = strtolower($classname.".php"); include(CLASSDIR.$filename); } ?>
Implementace základní logiky pro předávání řízení.
Tuto logiku implementujte v třídě Router (soubor classes/router.php).
Zde je kostra třídy:
classes/router.php
<?php class Router { private $controller; private $action; private $file; // registry je parametr predavany v konstruktoru a je to vlastne sada klicu a hodnot (pole zabalene v tride Registry). // je to dobre pro predavani globalnich informaci mezi komponentami MVC private $registry; public function __construct(Registry $registry) { $this->registry = $registry; // pro testovani v tomto kroku, pozdeji ale vymazte $this->getControllerInfo(); } // tato metoda provede vlastni routovani na zaklade ziskanych parametru public function route() { // implementujte v následujícím kroku } // metoda vezme parametr rt a zjisti z nej akci a dalsi parametry // vysledky ve forme naplnenych instancnich promennych $controller, $file, $action protected function getControllerInfo() { // rozdelte retezec v parametru rt podle znaku / // prvni parametr je $controller, druhy $action a file je jmeno controlleru.php // pokud neni v URL nastaven controller, bude rizeni predano standardnimu controlleru s nazvem index // pokud neni v URL nastavena akce, bude rizeni predano akci s nazvem index. } } ?>
Implementace třídy Registry je zde:
classes/registry.php
<?php // tato trida slouzi k uchovavani dat pro pouziti ve view Class Registry { // pole klicu a hodnot private $vars = array(); // pouzijeme setter a gettery // setter public function __set($index, $value) { $this->vars[$index] = $value; } // getter public function __get($index) { return $this->vars[$index]; } } ?>
Vyzkoušejte funkčnost Vašeho kódu například tak, že do souboru index.php vložíte následující kód:
index.php
<?php require "bootstrap.php"; // registry predstavuje globalni kontext $registry = new Registry(); // vyrobime router $router = new Router($registry); print_r($router); ?>
Implementujte metodu route() tridy Controller.
<?php public function route() { // zjistime si, co od nas kdo chce $this->getControllerInfo(); //overime, zda dany controller existuje // napriklad pomoci funkce is_readable("cesta_k_souboru_action_controlleru") if ( /*doplnte*/) { // a pokud ne, tak zvolime jeden specializovany controller // urceny pro nahlaseni chyby 404 $this->file = 'error404.php'; $this->controller = 'error404'; } // ted muzeme includnout ten controller, o ktery slo /* doplnte potrebny include controlleru */ // vyrobime jeho instanci $class = $this->controller . 'Controller'; $controller = new $class($this->registry); // ted zjistime, jestli tento controller ma pozadovanou akci, tedy metodu // a pokud ne, zavolame metodu (tedy akci) index // abychom zjistili pritomnost nejake metody, pouzijeme funkci is_callable if (/*doplnte zjisteni volatelnosti potrebne metody */) { $action = $this->action; } else { $action = 'index'; } // zavolame danou metodu daneho controlleru $controller->$action(); } ?>
Nyní umíte zavolat potřebné controllery a jejich metody. Struktura každého controlleru bude zřejmě vypadat takto:
classes/abstractcontroller.php
<?php Abstract Class AbstractController { //registry si budeme pamatovat ve vsech komponentach MVC protected $registry; function __construct(Registry $registry) { $this->registry = $registry; } //vsechny tridy typu controller musi obsahovat akci index abstract function index(); } ?>
Imlementujte jeden action controller s názvem indexController v souboru /controllers/index.php který obslouží všechna volání. Přidejte potřebné metody podle vlastního uvážení, které obslouží volání jiné než defaultní akce.
Pro pripad volání controlleru se jménem, které nemá implementaci vložte do frameworku také controller s názvem error404Controller v souboru /controllers/error404.php.
View jsme zatím neimplementovali. Zavolání view je starostí každého jednotlivého ActionControlleru.
Jaké máme požadavky na třídu implementující view:
Implementujte třídu Template v souboru /classes/template.php , která zajistí předchozí funkcionalitu
classes/template.php
<?php Class Template { // slouzi jako globalni kontext private $registry; // moje vlastni hodnoty (klic=>hodnota) private $vars = array(); // konstruktor function __construct($registry) { $this->registry = $registry; } // pouzijeme settery a gettery // setter public function __set($key, $value) { $this->vars[$key] = $value; } public function __get($key) { return $this->vars[$key]; } function show($name) { // zverejneni promennych, neni to moc hezke, ale funguje to foreach ($this->vars as $key => $value) { $$key = $value; } // provedte kontrolu, zda pozadovane view existuje // pokud ano, vlozte ho pomoci include // pokud ne, vyhodte vyjimku } } ?>
Imlementujte view například pro index podle vzoru.
views/index.php
Nejake HTML smichane s trivialnim php kodem <?php echo $message; ?> <?php echo $nejaka_jina_promenna; ?>
Nyní máte implementovány všechny komponenty MVC. Vraťte se k front controlleru, tedy k index.php.
Můžeme využít registry k tomu, abychom předávali informace mezi komponentami MVC a to včetně view. Výsledný kód bude vypadat takto:
<?php require "bootstrap.php"; // registry predstavuje globalni kontext $registry = new Registry(); // vyrobime view templatovaci engine $template = new Template($registry); // pridame ho do globalniho kontextu $registry->template = $template; // vyrobime router $router = new Router($registry); // pridame ho do globalniho kontextu $registry->router = $router; $router->route(); ?>
Potom volání view z nějakého controlleru bude vypadat takto:
controllers/index.php
<?php Class indexController Extends AbstractController { public function index() { //nastaveni nejake vlastnosti, toto je volani modelu $this->registry->template->helloWorld = 'Ahoj světe'; //predani rizeni view $this->registry->template->show('index'); } } ?>
wa1-cv10_macik.pdf