Webtechnológia gyakorlatok 10. Laboratóriumi gyakorlat
Model View Controller alapú alkalmazásfejlesztés A gyakorlat célja: Az MVC elv megértése és ismerkedés egy egyszerű MVC keretrendszerrel. Felkészüléshez szükséges anyagok: 1. A 17-es segédlet anyaga. 2. A honlapon található MVC diagramm és kód tanulmányozása. Az "MVC osztályok" csomag felhasználható a projekt megírásához. A laboron átnézzük a csomag szerkezetét és megírjuk egy használati eset kódját. A keretrendszer itt található: http://www.ms.sapientia.ro/~lszabo/webtechnologia/peldak/mvc/ . Az MVC osztályok bemutatása............................................................................................................1 A kontroller: index.php........................................................................................................................2 A modell: MVC_Module.....................................................................................................................3 A View megvalósítása: MVC_View ................................................................................................5 Egy használati eset implementálása.....................................................................................................6
Az MVC osztályok bemutatása Az osztályok kapcsolatát az alábbi diagramm ábrázolja:
A csomagban a fenti diagrammot megvalósító alap osztályok, illetve néhány használati esetet megvalósító kód található. 1
Webtechnológia gyakorlatok
A bemutató során utalunk majd az osztályok forráskódjaira, amelyek itt: http://www.ms.sapientia.ro/~lszabo/webtechnologia/peldak/mvc/mvcdoc/ böngészhetőek.
A kontroller: index.php A kontroller részt az index.php valósítja meg. A teljes kód itt tekinthető meg: http://www.ms.sapientia.ro/~lszabo/webtechnologia/peldak/mvc/mvcdoc/ . Az alkalmazás oldalai az alábbi sémát megvalósító GET paraméterekkel hívhatóak: /index.php?page=login&go=send
A fenti paramétereket az index.php értelmezi, és végrehajtja egy adott objektum, jelen esetben a login.php adott metódusát, ez a fenti példában a send metódus. Az indítást az alábbi kód valósítja meg: //a modul neve if (isset ($_GET['page'] )) $page = $_GET['page']; else $page = 'start'; //letezik egy start modul ha nincs modul nev //az esemeny nevet if (isset ($_GET['go'] )) $event = $_GET['go']; else $event = 'start'; //letezik egy start fuggveny minden modulban //ha nincs go parameter //betoltom a $page altal meghatarozott objektumot //PHP-ben az oszt'ly nevek nem erzekenyek a kis-nagybetűre //es a belso sztruktúrákban kisbetűsen tárolódnak //ezért nem kell azzal foglalkozni, hogy kis vagy nagybetűsen jött if (class_exists($page)) { $module = new $page (); if (!$module instanceof MVC_Module ) trigger_error("Rossz objektum, nem modul.", E_USER_ERROR); } else trigger_error("Hiányzó osztály", E_USER_ERROR); //meghivom a go parameterben levo esemenyt //megnezem, hogy van-e ilyen metodusa //illetve van-e __CALL metodusa, ha proxy-kent van megirva if ( method_exists ( $module, $event ) || method_exists ( $module, "__call" )){ //a meghivott lap itt lefut //amennyiben lenne visszateritett erteke, az a $RES be kerul $res = $module->$event(); }else trigger_error("Hiányzó metódus", E_USER_ERROR);
2
Webtechnológia gyakorlatok
Látható, hogy a létrehozott $module objektum MVC_Module típusú. Ez lesz tehát a futtatható objektumok típusa: így valamennyi objektumnak, amely az alkalmazás által indítható meg kell valósítania ezt az osztályt. Ezeknek a $event nevű metódusa lesz elindítva amennyiben ilyen létezik. Így az alkalmazás futtatható objektumait moduloknak is fogjuk nevezni. A következő lépésben lefut a modul (pl. a login.php). Ez végrehajtja azt az alkalmazás logikát amit megvalósít. Azokat a tartalmakat amelyeket ki kell írni a kliensnek előkészíti egy adatstruktúrában (később majd megnézzük hogyan). Az index.php tovább fut, és az alábbi kódra lép: //ez lesz a megjelenitest megvalosito objektum $view = new MVC_View ( 'smarty', $module ); // a model valtozoinak beallitasa $view->assign(); //... itt kód kihagyva ... //ez a vegso kiiras: a display felepiti az index.tpl -bol az //index.html-t es kiirja $view->display();
A kontroller létrehoz egy $view nevű objektumot, amelynek 2 paramétert ad: -a 'smarty' paraméterrel azt kéri a megjelenítőtől, hogy a Smarty-t használja sablonkezelőként -a $module megadásával megadja a MVC_View osztálynak, hogy melyik objektumtól kell a kiírandó adatokat átvenni
A modell: MVC_Module Az MVC_Module osztály az alapja minden futtatható osztálynak. Ez valósítja meg a tervezési minta "modell" részét. Minden példánya tartalmaz egy referenciát az alábbi objektumokra: MVC_Session: ez a PHP $_SESSION változójának objektumorientált elérését biztosítja. Ilyenként ez egy singleton (egyke) típusú osztály. Alább láthatjuk az osztály get_instance függvényét, valamint a statikus singleton objektumot tartalmazó változót: class MVC_Session { /** * Ez a valtozó tartalmazza a singleton osztályt * * @access private * @var Session szesszió */ private static $session;
3
Webtechnológia gyakorlatok /** * Indításkor megnyitja a szessziót */ private function __construct () { session_start(); } /** * A singleton osztályt lekérő függvény */ public static function get_instance () { if (!isset (self::$session)) { self::$session = new MVC_Session (); //ha nincs bejelentkezve a felhasznalo a user //erteke 0 if (!isset(self::$session->user_id)) self::$session->user_id=0; //a szesszio tatalmaz egy data tombot HTML kiirasok //megjegyzesehez if (!isset(self::$session->data)) self::$session->data = array(); } return self::$session; } ... a kód folytatódik
Figyeljük meg azt, hogy az osztály a PHP _set() és _get függvényét használja az adatok szesszióba való írására. /** * Változó kiolvasása * * @param string $n változó neve * */ public function __get ($n) { return $_SESSION[$n]; }
Így a szesszióba való írást az alábbi szintaxissal használhatjuk egy adott objektumból: $this->sess = MVC_Session::get_instance(); $this->sess->user_id = 1;
//szesszio nyitas
Az MVC_Module második referenciája egy MyDbiS nevű, adatbázis elérést biztosító osztályra mutat. A Dbi osztályt már ismerjük. A MyDbiS ezt terjeszti ki singleton típusúvá. Ez az osztály biztosítja a modul adatbázis elérését:
4
Webtechnológia gyakorlatok
Az MVC_View is kierjeszt egy osztályt: az MVC_Object-et amelyik egy $data nevű tömböt és egy get_view_data() nevű függvényt tartalmaz. Ebbe az osztályba fogja az MVC_Module a kiírandó adatokat tárolni, egyszerűen név-érték párként. A get_view_data() függvényt fogja meghívni az MVC_View és így veszi át a kiírandó adatokat (láthatjuk, hogy ezt az osztályt MVC_Object - más osztály is kiterjeszti).
A View megvalósítása: MVC_View Az MVC_View osztály valósítja ezt meg. Ha a konstruktorát megnézzük: function __construct ( $tpl_engine, $model ) { //a megjelenites kulonbozo template osztalyokat //hasznalhat //ezeknek implementalniuk kell az assign es display //fuggvenyeket switch ( $tpl_engine ){
5
Webtechnológia gyakorlatok case 'regex'
: $this->engine = new Template(); break; case 'smarty' : default : $this->engine = new Smarty(); } $this->model = $model; $this->passive = array(); }
látható, hogy létrehoz egy engine nevű változót, amelyik egy sablon kezelőre tartalmaz hivatkozást. Ezen kívül elmenti a model változójába a modellre vonatkozó referenciát. A kiírást a View display() függvénye végzi: /** * A weblapot (index.tpl) kiíró függvény * * Az index.tpl 'content' nevű zónájába beírja a modell által * generált HTML-t (a modell nevével azonos nevű sablont) * * utána kiírja a weblapot az Internetre */ function display ( ) { //a model sablonja, azonos nevu a model nevevel $template_file = strtolower ( $this->model->getname() ) . ".tpl"; $s = $this->engine->fetch ( $template_file ); //ez az index.tpl content reszebe kerul $this->engine->assign('content', $s ); // ! itt kód kimarad: lásd a teljes forrást //vegso kiiras $this->engine->display ( 'index.tpl' ); }
Az alkalmazás sablonja egy index.tpl nevű sablon, ebben egy $content nevű Smarty változó helyére ír be a View egy HTML-kódot: ez úgy áll össze, hogy a modell nevével azonos kis sablont a View feltölti a kiírandó adatokkal, és behelyettesíti a content helyébe. Ezt látjuk az előző függvényben. Az MVC osztályok még tartalmaznak un. passzív objektumokat, pl. reklámok megvalósítására. Ezeket az MVC_Passive osztály kiterjesztésével kell megvalósítani.
Egy használati eset implementálása Vegyünk egy használati esetet, pl. a bejelentkezést. Ez három műveletet kell megvalósítson: 1. bejelentkező űrlap kiírása 2. űrlap fogadása és bejelentkezés, ha a név/jelszó jó 3. kijelentkezés 6
Webtechnológia gyakorlatok
Ilyenként úgy fogjuk implementálni, hogy egy osztályon belül (legyen az osztály neve login) 3 metódust valósítunk meg (legyenek ezek start(), send() és leave() ). Ezeket, amint már láttuk az alábbi relatív URL-ekkel hívhatjuk: /index.php?page=login /index.php?page=login&go=send /index.php?page=login&go=leave
A start metódust mindig meg kell valósítani egy MVC_Module-t kiterjesztő osztálynál. Ez akkor hívódik meg, ha nem adunk az URL-ben meg go paramétert. Így a start() az első, a send() a második míg a leave() a login harmadik műveletét valósítja majd meg. Megtervezzük a login eset kimenetét (a View által használt sablont) , ennek tartalmaznia kell az első esetben kiírt űrlapot, és a két másik eset kimenetét: {if $form == 1}
{else}
{$form_ok}
{/if}
Látható, hogy két alternatívából áll (egyik az űrlap, a másik egy üzenet kiküldését biztosítja). A megírandó függvények feladata helyesen beállítani a sablon változóit, és megvalósítani a használati eset műveleteit. Íme a login.php osztály, a részletes magyarázatok elégségesek a működés megértéséhez:
7
Webtechnológia gyakorlatok
auth() ) { $this->data['form_error']='Be volt jelenkezve. Kijelentkeztettük.'; $this->leave(); //kijlentkezteti } $this->data['form']=1; //beallitom az ures div magassagat
8
Webtechnológia gyakorlatok $this->data['holder_height'] = HOLDER_HEIGHT; } /** * A POST metódus kezelését valósítja meg * * Akkor hívódik meg ha a 'go' paraméter értéke 'send' */ function send () { $u= new User(); if (($_SERVER['REQUEST_METHOD'] == 'POST') && //ha POST $u->get_data_by_email ($_POST['email']) && //van ilyen felhasznalo $u->check_passwd($_POST['password'])) { //jo a jelszo //bejelentkezes sikerult, a user_id a szesszioba kerul $this->sess->user_id=$u->user_id; //nem irjuk ki az urlapot $this->data['form']=0; // $this->data['form_ok']='Bejelentkezés sikerült.'; //ez az uzenet a jobb oldalon jelenik meg, es jelzi, hogy //a felhasznalo be van jelenkezve //mivel a szesszioba irjuk, ott marad addig amig a felhsznalo //kijelentkezik $this->sess->set_data ('right_msg', $u->name . ' ' ); }else { //beallitja az urlapon a hibauzenetet $this->data['form_error']='Hibás email vagy rossz jelszó.'; //visszakuldi a beirt email cimet $this->data['form_email']=$_POST['email']; //ez a valtozo allitja be az urlap kiirasat $this->data['form']=1; } //beallitom az ures div magassagat $this->data['holder_height'] = HOLDER_HEIGHT; } /** * A kijelentkezést valósítja meg */ function leave () { //ha be volt jelentkezve if ( $this->sess->user_id > 0 ) { $this->sess->user_id=0; //mar nem irom ki a felhasznalo nevet $this->sess->set_data ('right_msg', ''); //uzenet az ablakban $this->data['form_ok']='Kijelentkezés megtörtént.'; }else {
9
Webtechnológia gyakorlatok $this->data['form_ok']='Ne csaljon, nem is volt bejelentkezve.'; } //sablon form=0 ága $this->data['form']=0; //beallitom az ures div magassagat $this->data['holder_height'] = HOLDER_HEIGHT; } } ?>
A figyelemre méltó az MVC típusú alkalmazásokban az, hogy ha a keret működik, akkor az alkalmazás fejlesztés során az alábbiakat kell elvégezni: -meghatározzuk a használati eseteket -lebontjuk elemi web "műveletekre": HTTP kérések sorozatára -megtervezzük az eset kimenetét (view) minden kérésre -létrehozunk egy modellt megvalósító osztályt -annak függvényeiben megvalósítjuk a modell feladatait -a feladat elvégzése után/közben beállítjuk a kimenetet A legfontosabb: miközben egy használati esetet fejlesztünk, (viszonylag) függetlenek vagyunk az alkalmazás többi részétől. Rendelkezésünkre áll a szesszió objektum és az adatbázis elérés. Az MVC keret pedig stabil működést biztosít. A honlapon levő példa alkalmazás több kis minta használati eset implementálását tartalmazza itt: http://www.ms.sapientia.ro/~lszabo/webtechnologia/peldak/mvc/ .
10