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
include('albums_model.php');
$nev = ''; $leiras = ''; if (isset($_POST['nev'])) { $nev = $_POST['nev']; } if (isset($_POST['leiras'])) { $leiras = $_POST['leiras']; }
$albums = get_albums($nev, $leiras); //modell
include('list.php'); //view 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' => $sor['id'], 'nev' => $sor['nev'], 'leiras' => $sor['leiras'], ); } $stmt->free_result(); $mysqli->close();
return $albums; } Nézet (view)
11
Albumok listazasa
Nézet (view)12
Azonosító | Név | Leírá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) { function index() { case "index": index(); } break; case "page1": function page1() { page1(); break; } case "page2": page2(); function page2() { break; case "page3": } page3(); break; function page3() { default: index(); } } Front Controller 2.
19 if (function_exists($oldal)) { function index() { call_user_func($oldal); } }
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" ); if (file_exists("controllers/" . $class . ".php")) { session_start(); include_once("controllers/" . $class . ".php"); if (class_exists($class . "_Controller")) { function __autoload($class_name) { $class .= "_Controller"; require_once $ctrl = new $class(); strtolower($class_name) . '.php'; if (method_exists($ctrl, $method)) { } //call_user_func(array($class, $method)); $ctrl->$method(); require "models/db.php"; } //Konfiguracio betoltese else { //Jogosultsagok ellenorzese include_once("error.html"); } $class = 'main'; } if (isset($_GET['class'])) { else { $class = $_GET['class']; include_once("error.html"); } } $method = 'index'; } if (isset($_GET['method'])) { else { $method = $_GET['method']; include_once("error.html"); } } Front Controller vs bootstrap
23
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)
Controller
bootstrap
Controller Front Front Könyvtárszerkezet
24 Vezérlő
25
$model = new Albums_Model(); $albums = $model->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
class Albums_Model { function get_albums($nev = '', $leiras = '') { ... return $albums; } } Nézet
28
Azonosító | Név | Leírás | Funkciók |
---|---|---|---|
Torol |
29
30 Modell finomítása Modell finomítása31
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 Műveletkategóriák rétege Kapcsolatkezelés Többféle interfészt támogat CUBRID (PDO) Tranzakciókezelés MS SQL Server (PDO) Paraméterezett SQL Firebird/Interbase (PDO) utasítások és tárolt eljárások IBM (PDO) kezelése Informix (PDO) Hibakezelés
MySQL (PDO) LOB-ok kezelése Oracle (PDO) http://www.php.net/manual ODBC and DB2 (PDO) /en/book.pdo.php PostgreSQL (PDO) SQLite (PDO) 4D (PDO) PHP PDO
37
PDO PDOStatement exec bindColumn query bindParam prepare bindValue lastInsertId execute
beginTransaction rowCount inTransaction fetch commit fetch… rollBack closeCursor
errorCode errorCode errorInfo 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 Keretrendszerbe ActiveRecord ágyazva Doctrine CodeIgniter Eloquent Laravel Propel Symfony CakePHP Kohana PHP Yii 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); } }
Az alma .
A körte .
Transform view52
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ó, alapváltozók a 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) Layout 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/ Layout ő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); } } Layout használata
72
public function index() { $albums = $this->Albums_Model->get_albums(); $this->display_template('list', array( 'nev' => $nev, 'leiras' => $leiras, 'albums' => $albums, )); } 73 CodeIgniter plusz tulajdonságok
Input Class, Routing (remap) Input Class
74
Az Input Class biztonságos adatelérést tesz lehetővé megszűri a POST és COOKIE adatokat igény szerint XSS szűrést hajt végre
Nem kell külön betölteni (automatikusan történik)
Metódusok $this->input->post('vmi' [, true]) $this->input->cookie() $this->input->server() Speciális útvonalak
75
Alapból az URI eldönti, melyik vezérlő melyik metódusát hívjuk
Ez utóbbi felülbírálható a _remap függvénnyel Ha van, akkor mindig meghívódik 1. paraméter: metódus 2. paraméter: opcionális paraméterlista
Pl. speciális előtag használata (pl. AJAX hívásnál) Speciális útvonal
76
public function _remap($method, $params = array()) { if ($this->request->is_ajax()) { $method = '_ajax_'.$method; } if (method_exists($this, $method)) { return call_user_func_array(array($this, $method), $params); } show_404(); } Speciális útvonalak
77
Átirányítási szabályok (config/routes.php)
//Egy-egy leképezés $route['journals'] = "blogs"; $route['blog/joe'] = "blogs/users/34";
//Joker karakterek $route['product/:num'] = "catalog/product_lookup"; $route['product/(:any)'] = "catalog/product_lookup"; $route['product/(:num)'] = "catalog/product_lookup_by_id/$1";
//Reguláris kifejezések $route['products/([a-z]+)/(\d+)'] = "$1/id_$2"; // products/shirts/123 --> shirts/id_123