Conectare

Mi-am uitat parola

Panou De Control
Profilul tau
Informatii
Preferinte
Semnatura
Avatar
Social
Lista de prieteni si lista userilor ignorati
Membrii forumului
Grupuri de utilizatori
Mesaje private
Mesaje primite
Mesaje trimise
Subiecte monitorizate
Subiecte monitorizate
Parteneri
Steel Arena
Sondaj

Cum vi se pare siteul nostru?

81% 81% [ 13 ]
13% 13% [ 2 ]
0% 0% [ 0 ]
0% 0% [ 0 ]
6% 6% [ 1 ]

Total voturi : 16

Cuvinte-cheie


MVC 2+3: Implementare

In jos

MVC 2+3: Implementare

Mesaj Scris de +gZ.Flyking la data de Mier Iun 10, 2009 11:56 am

S-ar putea ca printre cei care au citit articolele despre MVC sa existe un programator brav care sa incerce sa construiasca un astfel de framework. Daca el exista, prin acest articol vreau sa-l dau inapoi. Desi prin implementarea unei arhitecturi MVC se pot invata multe despre programare. De exemplu, am invatat cum sa extind clase si cum sa lucrez cu ele pentru a-mi face viata mai usoara, am invatat patternuri de programare (registry si singleton), am invatat sa fac scaffolding si am invatat ca un framework nu poate niciodata sa cuprinda tot.

Unii pot crede ca a invata un framework deja facut este prea greu, daca nu inutil. Cand construiesti singur ceva, ajungi sa il stii ca in palma, sa-i stii fiecare problema si fiecare feature. Construind MVC-ul meu in PHP am aflat ca nu este deloc asa. Daca nu ai un set de conventii pe care trebuie sa le memorezi si sa le folosesti peste tot, nu vei reusi sa-ti aduci aminte lucruri banale ca nume de functii sau de constante. In plus, daca nu ai un plan cu tot ce trebuie facut, vei uita daca ai scris acea functie sau nu. Iarasi, o alta problema sta in faptul ca nu poti declara niciodata o clasa ca fiind completa. Pe masura ce vei avansa cu dezvoltarea, iti vei da seama ca multe lucruri trebuiesc shimbate - asta cel putin la inceput, cand construiesti fundatia. Si chiar daca termini fundatia, se poate intampla ca lucrurile sa se complice prea mult, deoarece vorbim de multe obiecte. Atunci, vei avea desigur nevoie sa simplifici, ceea ce inseamna sa te intorci din nou la baza.

Nu incerc sa descurajez pe nimeni, din contra: scriu toate problemele pe care le veti intalni aproape sigur, mai ales daca este prima experienta cu un script PHP ce are mai mult de 2-3 clase. Va sfatuiesc sa va ganditi foarte serios la adoptarea unui framework deja existent. Cake si Symfony sunt exemple minunate, iar Zend vine din spate destul de repede, si sunt sigur ca puteti ajuta la dezvoltarea acestora decat sa va aventurati singuri in "necunoscut".

Voi incerca sa descriu totusi procesele cele mai importante ale implementarii unui MVC, pentru cei putini ce vor dori sa faca acest lucru. Lauda lor, pentru ca dupa ce vor reusi, le va fi intr-adevar mult mai usor sa prinda orice MVC. Recent mi-am aruncat ochii prin Rails si ma descurc foarte bine, desi nu cunosc limbajul de programare (care este aparent paradoxalul Ruby: extrem de puternic si extrem de light).

1. BaseObject
Aceasta este clasa de baza din care va fi extinsa fiecare clasa a aplicatiei. Este bine sa o implementati, pentru ca va ofera un mod usor de a crea metode comunte tuturor claselor. Ati putea implementa de exemplu un debugger sau un log de erori, lucru pe care eu nu l-am facut pana acum.

2.iDB
Clasa de interfata pentru fiecare clasa ce va lucra cu baza de date. Ce vrea sa insemne acest lucru? Aceasta clasa va implementa toate metodele de care aveti nevoie pentru a lucra cu baza de date: query(), select(), insert() etc. Insa nu se va folosi de nici un DB engine, ergo nu va avea nici o functie care sa ruleze vreun query. Compunerea si rularea queryurilor o vor face clasele specifice db engineurilor: clasa pentru mysql, clasa pentru mssql etc. Iata functia selectAll() din iDB, cea pe care o vom apela din controllere pentru a selecta toate inregistrarile dintr-un tabel:


function selectAll($where='',$order_by='',$limit=''){
return $this->_fetchRows($this->_select('',array('*'),$where,$order_by,$limit));
}


Functia $this->_select(...) este implementata de clasa specifica pentru db engineul pe care il alegem. Sa presupunem ca este MySQL. Atunci functia $this->_select(...) nu va face decat sa intoarca un query de tipul "SELECT * FROM ... ". Functia $this->_fetchRows(...) se gaseste tot in clasa de MySQL si este responsabila pentru executarea acestui query si pentru returnarea rezultatului sau intr-o structura de tip array. In interiorul ei se apeleaza functia urmatoare, inclusa tot in clasa de MySQL:


function _execute($sql){
return mysql_query($sql,$this->connection);
}


Pentru clasa de SQLite, aceasta functie ar fi:


function _execute($sql){
return sqlite_query($sql,$this->connection);
}


Un query SQLite se executa printr-o alta functie PHP, si anume sqlite_query (dupa cum era de asteptat), functie care, dupa cum vedem, necesita un handle catre conexiune. De aici ne dam seama ca functii importante ale claselor specifice DB engine-urilor vor trebui sa aiba functii de conectare diferite.

Ideea principala aici este urmatoarea: fiecare functie trebuie definita in toate clasele specifice (DBMysql, DBsqlite etc), insa un alias al ei trebuie definit si in clasa mama, iDB. Astfel, in controllere vom chema functiile din iDB, care la randul lor vor chema functiile corespondente din clasele specifice. Desigur, acest lucru este necesar deoarece nu vom instantia toate clasele specifice, ci doar aceea de care avem nevoie:

class DBsqlite extends iDB{
Daca vom dori sa ne luam o baza de date MySQL, tot ce trebuie sa facem este sa instantiem clasa de MySQL:

class DBMySQL extends iDB {
si atat. Nu suntem nevoiti sa schimbam toate functiile din controllere.

3.iModel
Este clasa din care vom extinde fiecare fisier de model din cadrul aplicatiei noastre("app/models/"). Modelul va include functii de calcul ale datelor. De aceea, este normal ca modelul sa stabileasca conexiunea cu baza de date. O clasa de model va arata astfel:


class Mdoc extends iModel {

var $dbTable = 'docs';
var $dbEngine = 'mysql';

}


PHP va initializa intai metodele din clasa Mdoc, apoi va trece la clasa iModel. Aici, in functie de $dbEngine, initializam clasa pentru baza de date. Astfel, de cate ori vom avea nevoie de acces la baza de date, vom crea o clasa model pe langa controllerul nostru care ne va oferi automat functiile pentru DB.

4.iController
Toate controllerele se vor extinde de aici. Un controller este, dupa cum stim, locul unde preluam date din model si le oferim viewului pentru afisare. Se stie ca globalele nu sunt sigure in PHP, asa ca avem nevoie de o metoda in toate clasele controller pentru a asigna variabile catre view. Aceasta metoda va primi ca parametru o variabila si o va stoca intr-un array pentru a putea fi preluata de view.


function set($name,$value){
$this->viewVars[$name] = $value;
}


In controller, cand vom dori sa trimitem o variabila catre view, o vom face cu $this->set('nume_variabila',$variabila_sau_functie);

5.iView
Aici vom folosi acelasi principiu ca pentru iDB si copiii sai. De ce? Pentru ca vrem mai multe view-uri: unul care va randa HTML simplu, altul PDF, altul va integra Smarty, altul RSS etc. Pentru aceasta, fiecare clasa specifica de View va avea o metoda numita render() in care va randa in felul ei template-ul folosit de action-ul la care ne aflam la un moment dat (am spus ca, practic, fiecare pagina afisata userului corespunde unui action). In functie de numele actiunii si al modelului, View-ul de HTML simplu va include templateul gasit in 'app/views/nume_model/nume_actiune.tpl'. Smarty are alt mod de a randa templateurile:

$this->smarty->display($this->routes->action.'.tpl');

Pentru clasa VSmarty, functia render() este deci diferita.
Smarty are nevoie si de o initializare pentru a functiona:


function __init(){
$this->smarty = new Smarty();
$this->smarty->template_dir = $this->registry->routes->appPath.'views/'.$this->model.'/templates';
$this->smarty->compile_dir = $this->registry->routes->appPath.'views/'.$this->model.'/templates_c';
$this->smarty->config_dir = $this->registry->routes->appPath.'views/'.$this->model.'/configs';
$this->smarty->cache_dir = $this->registry->routes->appPath.'views/'.$this->model.'/cache';

}


Aceasta functie trebuie executata in fisierul iView, parintele claselor specifice de View. Iata intreaga clasa iView:


abstract class iView extends BaseObject {

/**
* The constructor sets the variables assigned by the controller to be used in views(templates).
* Also, if a child has defined an __init function, it executes it.
*
* @param array $viewVars
* @param string $model
* @param string $action
*/
function __construct(){
parent::__construct();
if(!empty($this->controller->viewVars)){
extract($this->controller->viewVars);
foreach($this->controller->viewVars as $key=>$value){
$this->$key = $value;
}
}

if(method_exists($this,'__init')){
$this->__init();
}
}


/**
* Basic render interface for all views. It is responsible for all output to the browser.
*
*/
abstract function render();

}


Functia __construct() se ocupa si de $viewVars, arrayul care stocheaza toate variabilele ce au fost asignate catre view din controller, folosind functia iController::set(); O data ce toate variabilele trec prin bucla foreach, le vom putea folosi in template-uri folosind $this->nume_variabila.

Am vazut totusi ca arrayul cu variabilele de view este apelat cu $this->controller->viewVars. Insa nu am aratat cum de clasa iView stie de obiectul $controller.

6. asRegistry
Prefixul a vine de la auxiliar, iar s de la singleton. asRegistry implementeaza patternul Singleton Registry si ne ajuta sa stocam referinte catre toate obiectele importante ale frameworkului intr-un singur loc. Pentru detalii vedeti www.patternsforphp.com. Iata clasa pe care am implementat-o eu:
class asRegistry {

public $storage = array();

static private $thisInstance = null;

static public function getInstance() {
if(self::$thisInstance == null)
{
self::$thisInstance = new sRegistry();
}
return self::$thisInstance;
}

function register($label, $object) {
$this->storage[$label] = $object;
}

function unregister($label) {
if(isset($this->storage[$label]))
{
unset($this->storage[$label]);
}
}

function get($label) {
if(isset($this->storage[$label]))
{
return $this->storage[$label];
}
return false;
}

function has($label) {
if(isset($this->storage[$label]))
{
return true;
}
return false;
}

}


Pentru a o folosi, de fiecare data cand vom initializa o clasa, vom inregistra instanta in registry.
$object = new ClassName();
$registry = sRegistry::getInstance();
$registry->register('object',$object);


Astfel, fiecare obiect ne va fi la indemana. Iar in clasa BaseObject ne vom asigura ca fiecare clasa va primi o instanta catre toate celelalte clase aflate in registry:


function __construct(){
$registry = sRegistry::getInstance();
if(!empty($registry->storage))foreach(array_keys($registry->storage) as $name){
$this->$name = &$registry->storage[$name];
}
}


7.Bootstrap
Fisierul de bootstrap este cel care leaga toate clasele impreuna: index.php, daca vreti. Fiecare cerere HTTP va avea minim doi parametri: model si action. Atentie, intotdeauna sa specificati valorile default, pe care le veti folosi in cazul in care URL-ul nu contine model sau action. Acestea vor contine de obicei modelul de homepage si actiunea standard a unui model (de obicei numita index). In functie de $_GET['model'] si $_GET['action'] ne vom initializa clasele de model (daca exista) si de controller - nu uitati sa folositi registry-ul. Vom rula apoi actiunea:
$controller->$action();
finalizand cu initializarea clasei de View si randarea templateului:


if(isset($model->presenter)){
$presenterClassName = 'V'.$model->presenter;
}
else{
$presenterClassName = 'VSimpleHTML';
}
$view = new $presenterClassName();
$view->render();



Pe masura ce voi primi feed-back, voi incerca sa descriu si alte clase importante pentru un framework. Voi incepe cu o functie importanta in MVC si nu numai, si anume generarea automata de back-enduri simple (Scaffolding) si clasa aSanitize pentru masuri generale de securitate. Acestea sunt insa clasele de baza, peste care va puteti crea propriile modele, controllere si templateuri.

_________________
One shot, one opportunity, one kill.
Let there be banned users!
avatar
+gZ.Flyking
Administrator
Administrator

Numarul mesajelor : 237
Data de inscriere : 05/06/2009
Varsta : 24
Localizare : Galati

Vezi profilul utilizatorului

Sus In jos

Sus


 
Permisiunile acestui forum:
Nu puteti raspunde la subiectele acestui forum