WEBES ALKALMAZÁSFEJLESZTÉS 1. Horváth Győző Egyetemi adjunktus 1117 Budapest, Pázmány Péter sétány 1/C, 2.420 Tel: (1) 372-2500/1816
Tartalom 2
MVC ismétlés Mini MVC – saját MVC keretrendszer MVC részeinek finomítása Modell
finomítása Nézet finomítása Vezérlő finomítása
Egyéb funkciók beillesztése az MVC mintába
6
MVC ismétlés
Ismétlés 7
Kiindulási pont: egyszerű listázó alkalmazás vegyes
HTML és PHP kód vegyes funkcionalitások
Kód funkcionális (és ezzel nyelvi) szétválasztása Nézet
(view) Modell (model) Vezérlő (controller)
Vezérlő (controller) 8
//view
//modell
Modell (model) 9
connect_error) { die('Kapcsolodasi hiba' . $mysqli->connect_errno . $mysqli->connect_error); } $mysqli->query('set names utf8'); $stmt = $mysqli->prepare( "select * from photo_albums where nev like ? and leiras like ?"); $stmt->bind_param('ss', $nev, $leiras); $nev = "%{$nev}%"; $leiras = "%{$leiras}%"; $stmt->execute();
Modell (model) 10
$meta = $stmt->result_metadata(); $sor = array(); foreach ($meta->fetch_fields() as $field) { $params[] = &$sor[$field->name]; } call_user_func_array(array($stmt, 'bind_result'), $params); $albums = array(); while ($stmt->fetch()) { $albums[] = array( 'id' 'nev' 'leiras' ); } $stmt->free_result(); $mysqli->close(); return $albums;
}
=> $sor['id'], => $sor['nev'], => $sor['leiras'],
Nézet (view) 11
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
Albumok listazasa
Nézet (view) 12
Új album...
Ismétlés 13
Ez a megvalósítás az MVC architekturális tervezési mintát követi, valósítja meg
Vezérlő
Modell
Nézet
Ismétlés 14
MVC minta általánosságban MVC keretrendszerek CodeIgniter (professzionális MVC keretrendszer)
15
Mini MVC Saját MVC-s keretrendszer
Saját MVC keretrendszer 16
Elvárások Egy
belépési pontja legyen az alkalmazásnak Az MVC részei külön könyvtárba kerüljenek Elnevezési konvenciók Sablon nyelv a PHP (echo, if, foreach) Oldalsablon támogatás legyen OOP
Front Controller 17
Az alkalmazás belépési pontja Vezérlők közös részeit tartalmazza
munkamenet-kezelés, konfiguráció beolvasása, authentikáció, előszűrések, stb.
Többi fájl elérhetetlensége .htaccess token definiálása a FC-ben és ellenőrzése a vezérlőkben
index.php Kért oldal jelzése GET paraméterrel index.php?oldal=main
Front Controller 1. 18
switch ($oldal) { case "index": index(); break; case "page1": page1(); break; case "page2": page2(); break; case "page3": page3(); break; default: index(); }
function index() { }
function page1() { } function page2() { } function page3() { }
Front Controller 2. 19
if (function_exists($oldal)) { call_user_func($oldal); }
function index() { }
function page1() { } function page2() { } function page3() { }
Front Controller 3. 20
switch ($oldal) { case "index": include "v3_index.php"; break; case "page1": include "v3_page1.php"; break; case "page2": include "v3_page2.php"; break; case "page3": include "v3_page3.php"; break; default: include "v3_index.php"; }
Front Controller 4. 21
if (file_exists("v3_" . $oldal . ".php")) { include_once("v3_" . $oldal . ".php"); }
Front Controller (index.php) 22
index.php?class=products&method=list
define( "TOKEN", "mini_mvc" ); session_start(); function __autoload($class_name) { require_once strtolower($class_name) . '.php'; }
require "models/db.php"; //Konfiguracio betoltese //Jogosultsagok ellenorzese $class = 'main'; if (isset($_GET['class'])) { $class = $_GET['class']; } $method = 'index'; if (isset($_GET['method'])) { $method = $_GET['method']; }
if (file_exists("controllers/" . $class . ".php")) { include_once("controllers/" . $class . ".php"); if (class_exists($class . "_Controller")) { $class .= "_Controller"; $ctrl = new $class(); if (method_exists($ctrl, $method)) { //call_user_func(array($class, $method)); $ctrl->$method(); } else { include_once("error.html"); } } else { include_once("error.html"); } } else { include_once("error.html"); }
Front Controller vs bootstrap 23
Controller
Front Controller
A fenti megoldásban a Front Controller procedurális, nem OOP-s Ha a Front Controllert osztálypéldányként (singleton) szeretnénk használni, akkor az az állomány, amely előkészíti és példányosítja a bootstrap állomány (index.php) bootstrap
Könyvtárszerkezet 24
Vezérlő 25
get_albums($nev, $leiras);
//modell
$view = new View(); $view ->set('nev', $nev) ->set('leiras', $leiras) ->set('albums', $albums); $content = $view->get_include_contents('views/list.php'); include('views/template.php'); }
}
View osztály 26
class View { private $vars = array(); public function get_include_contents($filename) { extract($this->vars); if (is_file($filename)) { ob_start(); include $filename; $contents = ob_get_contents(); ob_end_clean(); return $contents; } return false; } public function set($name, $value) { $this->vars[$name] = $value; return $this; } }
Modell 27
Nézet 28
Új album...
Oldalsablon 29
30
Modell finomítása
Modell finomítása 31
Az alkalmazás adatait és az ezek feldolgozásához szükséges üzleti logikát tartalmazza További rétegekre bontható üzleti
logikai réteg (üzleti logikához illeszkedő adatszerkezetek, objektumok) adatbázis-absztrakciós réteg (adatbázis- és táblaszerkezetnek megfelelő objektumok hierarchiája) adatbázis-elérési absztrakciós réteg adatbázis (táblák, tárolt eljárások, nézetek)
Modell minták 32
Domain model: olyan réteg, amely a valós világ adatainak megfelelő absztrakt objektumokat, logikákat tartalmazza Simple
Domain Model: egy-egy kapcsolat az üzleti objektumok és táblák között Active
Record minta (rekord szintű) Table Data Gateway minta (tábla szintű) Data Mapper minta (táblaadatok üzleti objektum) Rich
Domain Model: üzleti objektumok bonyolultabb kapcsolatrendszere (tipikusan nagyvállalati projektek)
Adatbázis-elérési absztrakció 33
A modellünk jelenleg egy bizonyos adatbázis driverhez kötődik. Ha megváltozik az alkalmazásunk mögötti adatbáziskezelő, vagy egyszerűen olyan szerverre költözünk, ahol egy bizonyos driver nincsen feltelepítve, az átállás nagyon nehéz lehet. Megoldás: adatbázis-elérési absztrakciós réteg kialakítása, mely egy magasabb szintű absztrakt nyelvvel elfedi a konkrét adatbázis függvényeit.
Adatbázis-elérési absztrakció 34
Adatbázis-elérési absztrakciós lehetőségek Saját
magunk készítünk ilyen réteget Előre megírt függvénykönyvtárat alkalmazunk PHP
PDO Doctrine Database Abstraction Layer
http://www.doctrine-project.org/projects/dbal
Saját absztrakciós réteg 35
db.php: konfiguráció mysql.php: absztrakt műveletek Mysqli interfésszel kifejtve oracle.php: absztrakt műveletek OCI8 interfésszel kifejtve albums.php: absztrakt interfésszel megvalósított modell
PHP PDO 36
PHP „natív” absztrakciós rétege Többféle interfészt támogat
CUBRID (PDO) MS SQL Server (PDO) Firebird/Interbase (PDO) IBM (PDO) Informix (PDO) MySQL (PDO) Oracle (PDO) ODBC and DB2 (PDO) PostgreSQL (PDO) SQLite (PDO) 4D (PDO)
Műveletkategóriák
Kapcsolatkezelés Tranzakciókezelés Paraméterezett SQL utasítások és tárolt eljárások kezelése Hibakezelés LOB-ok kezelése
http://www.php.net/manual /en/book.pdo.php
PHP PDO 37
PDO
PDOStatement
exec query prepare lastInsertId
beginTransaction inTransaction commit rollBack
errorCode errorInfo
bindColumn bindParam bindValue execute rowCount fetch fetch… closeCursor errorCode errorInfo
PHP PDO példa 38
$sth = $dbh->prepare('SELECT name, colour, calories FROM fruit WHERE calories < :calories AND colour = :colour'); $sth->bindParam(':calories', $calories, PDO::PARAM_INT); $sth->bindParam(':colour', $colour, PDO::PARAM_STR, 12); $calories = 150; $colour = 'red'; $sth->execute();
$sth = $dbh->prepare('SELECT name, colour, calories FROM fruit WHERE calories < ? AND colour = ?'); $calories = 150; $colour = 'red'; $sth->execute(array($calories, $colour));
Adatbázis-absztrakciós megoldások 40
Általában egy adatbázis-elérési absztrakciós rétegen ülnek Tipikusan egy-egy kapcsolatot teremtenek az adatbázisbeli táblák és az üzleti objektumok adattagjai között Az adatbázist az üzleti objektum magas szintű absztrakt metódusain keresztül manipuláljuk get,
getAll, save (insert vagy update)
Általában ORM megoldások: Object Relational Mapping
Adatbázis-absztrakciók 41
Nem jelennek meg benne direkt SQL utasítások OOP felületen adjuk meg a logikát, mit szeretnénk, s az absztrakciós réteg generálja le a neki megfelelő SQL utasítást A megoldások egy részében saját lekérdező nyelven keresztül Hibernate
HQL Doctrine DQL
Object Relational Mapping 42
Az ORM egy tábla-egy objektum kapcsolatot létesít Lehetőség van kapcsolatok megadására (1-N, N-N viszonyok) és ezeknek OOP-s felületen történő kezelésére pl.
$user->subjects
PHP ORM megoldások 43
Független projektek ActiveRecord
Keretrendszerbe ágyazva
Doctrine
CodeIgniter
Eloquent
Laravel
Propel
Symfony
CakePHP Kohana
Yii
PHP
PHP ORM példa (ActiveRecord) 44
class User extends ActiveRecord\Model { } // create Tito $user = User::create(array('name' => 'Tito', 'state' => 'VA')); // read Tito $user = User::find_by_name('Tito'); // update Tito $user->name = 'Tito Jr'; $user->save(); // delete Tito $user->delete();
PHP ORM példa (ActiveRecord) 45
PHP ORM példa (ActiveRecord) 46
CodeIgniter specifikumok 47
CodeIgniter a Database Classon keresztül biztosít adatelérési-absztrakciós
réteget adatbázis-absztrakciós réteget (ActiveRecord minta alapján, de nem teljes ORM funkcionalitás)
48
Nézet finomítása
Nézet finomítása 49
Alkalmazott tervezési minták Template
view Transform view Layout view (oldalsablon megadása)
Template view 50
Általában ezt használjuk webalkalmazásokban A nézet ebben az esetben egy sablon, amiben speciális jelölőket cserélünk ki a modellből kinyert adatokkal Előző előadásban is ezt mutattuk be Részei sablon
(PHP, Smarty, stb.) nézethez tartozó logika, melynek során a jelölőkhöz adatokat rendelünk, valamint a feldolgozó rész
Template view példa (CodeIgniter) 51
class Products extends CI_Controller { public function index() { $data = array( 'alma' => 'piros', 'korte' => 'kukacos', ); $this->load->view('proba', $data); } }
My Blog Az alma .
A körte .
Transform view 52
A transform view adatokat nyer ki a modellből, és azokat a kimenetnek megfelelően átalakítja. A template view a kimenet vázlatával kezdi, és abba illeszti be az adatokat A transform view-nál az adatok az elsők, és abból építi fel a kimenetet Tipikusan technológia: XSLT
Layout view 53
A nézet további részekre bontását végzi el Egy weboldalon általában vannak viszonylag állandó részek: fejléc, az oldalelrendezés, lábléc, globális navigáció És vannak az oldalról oldalra változó tartalmi részek A nézetet ennek megfelelően szokták szétválasztani oldal
elrendezésére (layout) tartalmi sablonra (template)
CodeIgniter specifikumok 54
Template view támogatott a View classon keresztül $this->load->view('sablon.php');
Transform view-t PHP támogatja az xml és xslt függvényein keresztül Layout view ld. később
55
Controller finomítása Bootstrap, Front Controller, Application Controller
Controller finomítása 56
Példánkban a vezérlő nem csinált túl sok mindent Általában a vezérlőknek elég sok mindent kell elintéznie, mielőtt a konkrét művelet végrehajtásába kezd Ezek között sok olyan dolog van, ami minden vezérlő számára közös A közös dolgokat külön választják, ez lesz a Front Controller, az egyedi dolgok pedig az egyes műveletekben kerülnek megvalósításra
További finomítás 57
Alap esetben a Front Controller biztosítja az alkalmazás belépési pontját (ez előny) De ekkor szükségképpen tartalmaz procedurális kódot, így a Front Controller csak félig OOP-s A megfelelő művelet kiválasztása is külön vehető Ezért többfelé választják Bootstrap
Front
Controller Application Controller
Bootstrap 58
index.php alkalmazás belépési pontja procedurális feladata Front
Controller példányosításának előkészítése
konfiguráció,
a
alapváltozók
példányosítás (singleton) a példány kezdőmetódusának meghívása (run)
Front Controller 59
Objektum-orientált megközelítés tipikusan singleton Közös feladatok centralizált elvégzése kérés
feldolgozása routing caching biztonság konfiguráció
Intercepting filter helye
Intercepting filter 60
Globális kódok és funkcionalitás a Front Controllerben az Intercepting filter minta felhasználásával kerülhetnek Kétféle megközelítés szűrők
szekvenciális végrehajtása az alkalmazásvezérlőig elő- és utószűrők megadása
Intercepting filter példa 61
class FrontController { var $_filter_chain = array();
function registerFilter(&$filter) { $this->_filter_chain[] =& $filter; } function run() { foreach(array_keys($this->_filter_chain) as $filter) { $this->_filter_chain[$filter]->preFilter(); } $this->_process(); foreach(array_reverse(array_keys($this->_filter_chain)) as $filter) { $this->_filter_chain[$filter]->postFilter(); } } function _process() { // FrontController tennivalói } }
Intercepting filter példa 62
Szűrő példa class HtmlCommentFilter { function preFilter() { ob_start(); } function postFilter() { $page = ob_get_clean(); echo preg_replace( ‘~~ims’ ,’’ ,$page); } }
Application Controller 63
Ez az MVC vezérlő központi része: ő dönti el, hogy a bejövő kérés alapján az alkalmazás hogyan válaszoljon Tipikusan egy
nagy elágazás (if, switch) vagy felparaméterezett tömb vagy elnevezési konvenciók (kérés, file és osztálynév)
Application Controller példa 64
Paraméterezett asszociatív tömb
$action_map = array( 'del' => 'DeleteBookmark' ,'upd' => 'UpdateBookmark' ,'add' => 'InsertBookmark' ); $action_class = (array_key_exists($_POST['action'], $action_map)) ? $action_map[$_POST['action']] : 'DisplayBookmark'; if (!class_defined($action)) { require_once 'actions/'.$action_class.'.php'; } $action =& new $action_class; $action->run();
65
Egyéb funkciók helye MVC-ben Authentikáció, authorizácó (ACL), oldalsablon
Vitatott kérdések 66
Az MVC minta a vezérlő, a nézet és az adatok helyéről nyilatkozik általánosságban Egy webalkalmazásban azonban számos további funkció is helyet kap munkamenet-kezelés authentikáció authorizáció oldalsablon
használata
Munkamenet-kezelés 67
Többféle elképzelés van, de ezek fontossága azonban elvi jelentőségű $_SESSION modell:
adatok vannak benne vezérlő: az alkalmazás input oldalán jelennek meg nézet: ha sütikkel oldjuk meg, akkor viszont HTTP specifikus
Authentikáció 68
Az authentikációs logika külön osztályban (library) megvalósítható Hol épüljön az alkalmazásba? Lehetőségek őscontroller hook
Authorizáció 69
Ez is külön osztályban megvalósítható Sok helyen ACL (Access Control List) néven hivatkoznak rá Sokféle megvalósítása van Népszerű a Zend ACL használata Helye őscontroller hook controller még alsóbb szinteken (feladatfüggő, pl. mezőszintű elérési szabályok)
Oldalsablon használata 70
Helye őscontrollerben
megírni
set_template_data display_template
hook-ot
készíteni rá
http://hasin.wordpress.com/2007/03/05/adding-yield-
codeigniter/ library-t
készíteni rá
http://codeigniter.com/wiki/layout_library/
Oldalsablon ősvezérlőben 71
class MY_Controller extends CI_Controller { protected $template = 'template'; protected $template_data = array(); protected function set_template_part($part, $view, $data = array()) { $this->template_data[$part] = $this->load->view($view, $data, true); } protected function set_template_data($part, $data) { $this->template_data[$part] = $data; } protected function display_template($view = null, $data = array()) { if (!is_null($view)) { $this->set_template_part('content', $view, $data); } $this->load->view($this->template, $this->template_data); } }
Oldalsablon használata 72
public function index() { $albums = $this->Albums_Model->get_albums(); $this->display_template('list', array( 'nev' => $nev, 'leiras' => $leiras, 'albums' => $albums, )); }