Warning
This page is located in archive.

MVC - front controller

Implementace vlastního MVC frameworku

Tématem dnešního cvičení je implementace jednoduchého MVC frameworku, který implementuje variantu Front Controller.

Úkol č. 1

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.

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)

Úkol 2

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);
}
?>

Úkol 3 - založení routeru

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);
 
 
?>

Úkol 4 - implementace metody route()

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();
 
	}
?>

Úkol 5 - implementace ActionControlleru

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.

Úkol 6 Implementace view

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:

  1. k view je možné přiřadit libovolná data ve formě KLIC - HODNOTA
  2. view má metodu show(“nazev_view”), která zobrazí soubor umístěný v adresáři views /views/nazev_view.php
  3. pro soubor “nazev_view” před jeho zavoláním připraví všechna data přiřazená jak klíč-hodnota tak, aby byla viditelná jako globální proměnná.

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; ?>

Úkol 7 Orchestrace

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:

index.php

<?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

Reference

courses/a7b39wa1/tutorials/12/start.txt · Last modified: 2015/09/23 11:18 by xklima