73
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

WEBES ALKALMAZÁSFEJLESZTÉS 1.webprogramozas.inf.elte.hu/weaf1/ea/weaf1_11.pdf · WEBES ALKALMAZÁSFEJLESZTÉS 1. Horváth Győző ... PHP PDO Doctrine Database ... Saját absztrakciós

Embed Size (px)

Citation preview

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

Tartalom2

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

MVC ismétlés6

Ismétlés7

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

<?php

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

<?phpfunction get_albums($nev = '', $leiras = '') {

$mysqli = new mysqli('localhost', 'wabp3', 'ab2009', 'wabp3');if ($mysqli->connect_error) {

die('Kapcsolodasi hiba' . $mysqli->connect_errno .$mysqli->connect_error);

}$mysqli->query('set names utf8');

$stmt = $mysqli->prepare("select * from photo_albumswhere 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

<html><head>

<title></title><meta http-equiv="Content-Type"

content="text/html; charset=UTF-8"></head><body>

<h1>Albumok listazasa</h1><form action="index2.php" method="post">

Nev: <input type="text" name="nev"value="<?php echo $nev; ?>" ><br>

Leiras: <input type="text" name="leiras"value="<?php echo $leiras; ?>" ><br>

<input type="submit" value="Szur"></form>

Nézet (view)12

<table><tr>

<th>Azonosító</th><th>Név</th><th>Leírás</th>

</tr><!--Dinamikus resz kezdete --><?php foreach ($albums as $sor) : ?><tr>

<td><?php echo $sor['id']; ?></td><td><?php echo $sor['nev']; ?></td><td><?php echo $sor['leiras']; ?></td>

</tr><?php endforeach; ?><!--Dinamikus resz vege -->

</table><a href="insert.php">Új album...</a>

</body></html>

Ismétlés13

Ez a megvalósítás az MVC architekturális tervezési

mintát követi, valósítja meg

Vezérlő

NézetModell

Ismétlés14

MVC minta általánosságban

MVC keretrendszerek

CodeIgniter (professzionális MVC keretrendszer)

Saját MVC-s keretrendszer

Mini MVC15

Saját MVC keretrendszer16

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 Controller17

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

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

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'];}

Front Controller vs bootstrap23

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)

boots

trap

Front

Contr

olle

r

Contr

olle

r

Könyvtárszerkezet24

Vezérlő25

<?php if (!defined('TOKEN')) exit('Ejnye-bejnye');class Main_Controller {

function index() {include('models/albums.php');$nev = '';$leiras = '';if (isset($_POST['nev'])) {

$nev = $_POST['nev'];}if (isset($_POST['leiras'])) {

$leiras = $_POST['leiras'];}

$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ály26

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;

}}

Modell27

<?php if (!defined('TOKEN')) exit('Ejnye-bejnye');

class Albums_Model {function get_albums($nev = '', $leiras = '') {

...return $albums;

}}

Nézet28

<table><tr>

<th>Azonosító</th><th>Név</th><th>Leírás</th><th>Funkciók</th>

</tr><!--Dinamikus resz kezdete --><?php foreach ($albums as $sor) : ?><tr>

<td><?php echo $sor['id']; ?></td><td><a href="index.php?class=images&method=index&album_id=<?php echo

$sor['id']; ?>"><?php echo $sor['nev']; ?></a></td><td><?php echo $sor['leiras']; ?></td><td><a href="index.php?class=main&method=delete&id=<?php echo

$sor['id']; ?>">Torol</a></td></tr><?php endforeach; ?><!--Dinamikus resz vege -->

</table><a href="index.php?class=main&method=insert">Új album...</a>

Oldalsablon29

<div id="wrap"><div id="header">

<h1 id="logo-text"><a href="index.html">Sablon</a>

</h1></div>

<!-- content-wrap starts here --><div id="content-wrap">

<div id="main"><?php echo $content; ?>

</div></div>

</div>

Modell finomítása30

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ák32

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éteg35

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

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

36

PHP PDO

PDO

exec

query

prepare

lastInsertId

beginTransaction

inTransaction

commit

rollBack

errorCode

errorInfo

PDOStatement

bindColumn

bindParam

bindValue

execute

rowCount

fetch

fetch…

closeCursor

errorCode

errorInfo

37

PHP PDO példa38

$sth = $dbh->prepare('SELECT name, colour, caloriesFROM fruitWHERE 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, caloriesFROM fruitWHERE calories < ? AND colour = ?');

$calories = 150;$colour = 'red';$sth->execute(array($calories, $colour));

Adatbázis-absztrakciós megoldások40

Á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 RelationalMapping

Adatbázis-absztrakciók41

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 Mapping42

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

Független projektek

ActiveRecord

Doctrine

Eloquent

Propel

Keretrendszerbe

ágyazva

CodeIgniter

Laravel

Symfony

CakePHP

Kohana PHP

Yii

43

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 specifikumok47

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)

Nézet finomítása48

Nézet finomítása49

Alkalmazott tervezési minták

Template view

Transform view

Layout view (oldalsablon megadása)

Template view50

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

}}

<html><head>

<title>My Blog</title></head><body>

<p>Az alma <?php echo $alma; ?>.</p><p>A körte <?php echo $korte; ?>.</p>

</body></html>

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 view53

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 specifikumok54

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

Bootstrap, Front Controller, Application Controller

Controller finomítása55

Controller finomítása56

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ás57

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

Bootstrap58

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 Controller59

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 filter60

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élda61

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élda62

Szűrő példa

class HtmlCommentFilter {function preFilter() {

ob_start();}function postFilter() {

$page = ob_get_clean();echo preg_replace(

‘~<!—.*—>~ims’,’’,$page);

}}

Application Controller63

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élda64

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

Authentikáció, authorizácó (ACL), oldalsablon

Egyéb funkciók helye MVC-ben65

Vitatott kérdések66

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és67

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álata70

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őben71

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álata72

public function index(){

$albums = $this->Albums_Model->get_albums();$this->display_template('list', array(

'nev' => $nev,'leiras' => $leiras,'albums' => $albums,

));}

Input Class, Routing (remap)

CodeIgniter plusz tulajdonságok73

Input Class74

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 útvonalak75

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 útvonal76

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 útvonalak77

Á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