Documente Academic
Documente Profesional
Documente Cultură
Ce este Yii
Yii este o platforma PHP bazata pe componente de mare perfomanta pentru dezvoltarea aplicatiilor
Web de orice fel. Permite reutilizarea la maxim a codului scris si poate accelera drastic procesul de
dezvoltare. Numele Yii (pronuntat /i:/) inseamna usor, eficient si extensibil.
1. Cerinte
Pentru a rula o aplicatie bazata pe Yii, trebuie sa avem un server Web care pune la dispozitie minim
versiunea 5.1.0 de PHP.
Pentru programatorii care vor sa foloseasca Yii, intelegerea programarii orientata pe obiecte (POO)
ajuta foarte mult, pentru ca Yii este o platforma pura POO.
Yii exceleaza, fata de celelalte platforme PHP, la capitolele eficienta, feature-uri si documentatie. Yii
a fost inca de la inceput foarte atent proiectata pentru a fi utila dezvoltarii serioase de aplicatii Web.
Nu este nici un rezultat al vreunui proiect, nici un conglomerat de componente third-party. Este
rezultatul experientei bogate a autorilor in dezvoltarea aplicatiilor Web. In aceeasi masura este
rezultatul unei investigari atente ale celor mai populare aplicatii si platforme de programare Web.
Instalare
Instalarea platformei Yii implica in mare urmatorii doi pasi:
1
Ghid de utilizare Yii
1. Cerinte
Dupa instalarea Yii, ar trebui sa verificam daca serverul nostru indeplineste toate cerintele pentru
folosirea corespunzatoare a platformei Yii. Putem face acest lucru prin scriptul de verificare cerinte
al carui URL este urmatorul:
http://hostname/path/to/yii/requirements/index.php
Cerinta minima este ca serverul Web sa suporte versiunea PHP 5.1.0 (sau o versiune mai recenta).
Platforma Yii a fost testata cu serverul HTTP Apache in Windows si Linux. Poate rula de asemenea
si pe alte servere Web atat timp cat este pus la dispozitie PHP 5.
Nota: Atunci cand rulam yiic in Mac OS, Linux sau Unix, va trebui sa modificam permisiunile
fisierului yiic pentru a fi executabil. Altfel, putem rula unealta si in felul urmator:
% cd WebRoot/myproject
Aceste comenzi vor crea un schelet de aplicatie Yii in directorul WebRoot/myproject. Aplicatia are o
structura de directoare care este necesarea pentru majoritatea aplicatiilor Yii.
Fara sa scriem nici o linie de cod, putem testa prima noastra aplicatie Yii prin accesarea urmatorului
URL intr-un browser Web:
http://hostname/myproject/index.php
2
Ghid de utilizare Yii
Dupa cum putem vedea, aplicatia are trei pagini: pagina home, pagina contact si pagina de logare.
Pagina home contine cateva informatii despre aplicatie si despre utilizator. Pagina de contact
afiseaza un formular de contact care poate fi completat de catre utilizatori. Pagina de logare permite
utilizatorilor sa fie autentificati inainte de a accesa continut pentru care au nevoie de anumite
privilegii. Putem vedea screenshot-urile urmatoare pentru mai multe detalii.
Pagina home
Pagina contact
3
Ghid de utilizare Yii
4
Ghid de utilizare Yii
5
Ghid de utilizare Yii
Pagina de logare
myproject/
6
Ghid de utilizare Yii
7
Ghid de utilizare Yii
return array(
......
'components'=>array(
......
'db'=>array(
'connectionString'=>'sqlite:protected/data/source.db',
),
),
......
);
Trebuie sa cream o baza de date SQLite astfel incat configuratia de mai sus sa functioneze.
Folosind orice unealta de administrare SQLite, putem crea o baza de date cu urmatoarea structura:
Pentru simplitate, cream o singura tabela (User) in baza de date. Fisierul bazei de date SQLite este
salvat avand urmatoarea cale: WebRoot/myproject/protected/data/source.db. Trebuie
remarcat ca fisierul bazei de date si directorul care contine acest fisier trebuie sa permita scrierea de
catre serverul Web server.
2. implementarea operatiilor CRUD
8
Ghid de utilizare Yii
Acum este partea interesanta. Pentru ca vrem sa implementam operatiile CRUD (create, read,
update si delete) pentru tabela User tocmai creata. Aceste operatii sunt foarte folosite in aplicatiile
reale.
In loc de a ne chinui sa scriem codul pentru aceste operatii, mai bine folosim unealta yiic din nou
pentru a genera automat codul pentru noi. Acest proces mai este cunoscut sub numele
de scaffolding. Deschidem o fereastra cu linia de comanda, si executam urmatoarele comenzi:
% cd WebRoot/myproject
% protected/yiic shell
generate User.php
The 'User' class has been successfully created in the following file:
D:\wwwroot\myproject\protected\models\User.php
If you have a 'db' database connection, you can test it now with:
$model=User::model()->find();
print_r($model);
generate UserController.php
generate create.php
mkdir D:/wwwroot/myproject/protected/views/user
generate update.php
9
Ghid de utilizare Yii
generate list.php
generate show.php
Crud 'user' has been successfully created. You may access it via:
http://hostname/path/to/index.php?r=user
http://hostname/myproject/index.php?r=user
Acest URL va afisa o lista cu intrari de utilizatori din tabela User. Din moment ce tabela noastra este
goala, nu apare nimic in acest moment.
Apasam pe link-ul New User din pagina. Va aparea pagina de logare in cazul in care nu ne-am mai
logat anterior. Dupa ce ne-am logat, apare un formular de intrare care ne permite sa adaugam o
noua intrare de utilizator. Completam formularul si apasam pe butonul Create. Daca avem vreo
eroare la intrare, va aparea un prompt dragut cu erorile in cauza. Astfel suntem impiedicati sa
salvam datele. Revenind la lista de utilizatori, ar trebui sa vedem utilizatorul nou adaugat ca apare in
lista.
Repetam pasii de mai sus pentru a adauga mai multi utilizatori. Putem remarca faptul ca pagina cu
lista utilizatorilor va face paginare automat daca sunt prea multi utilizatori de afisat pe o singura
pagina.
http://hostname/myproject/index.php?r=user/admin
Vom vedea aici o tabela cu intrarile utilizatorilor. Putem apasa pe celulele header ale tabelei pentru
a sora coloanele corespunzator. La fel ca si pagina cu lista utilizatorilor, pagina admin face paginare
automata.
Beneficiem de toate aceste feature-uri fara sa scriem nici macar o linie de cod!
11
Ghid de utilizare Yii
Model-View-Controller (MVC)
MVC este un concept foarte raspandit in programarea Web. Scopul MVC este de a tine separate
logica business-ului si interfata utilizator, astfel incat cei care intretin aplicatia sa schimbe mult mai
usor o parte, fara a afecta alte parti. In MVC, modelul contine informatiile (datele) si regulile
business; view contine elemente din interfata utilizator (texte, input-uri ale formularelor etc);
controller-ul genstioneaza comunicatia dintre model si view.
In afara de MVC, Yii introduce un front-controller, cu numele application, care reprezinta contextul in
care se executa procesarea cererii client. Application rezolva cererea utilizator si o trimite mai
departe controller-ului corespunzator care va trata efectiv cererea.
12
Ghid de utilizare Yii
1. Fluxul tipic
Urmatoarea diagrama arata fluxul tipic de lucru al unei aplicatii Yii atunci cand trateaza o cerere
client:
13
Ghid de utilizare Yii
Fisierul de intrare
Fisierul de intrare este fisierul PHP care trateaza initial cererile utilizatorilor. Este singurul fisier PHP
accesibil pe care utilizatorii il pot executa direct.
In cele mai multe cazuri, fisierul de intrare al unei aplicatii Yii contine codul urmator:
// comentam urmatoarea linie de cod atunci cand site-ul este facut public
defined('YII_DEBUG') or define('YII_DEBUG',true);
// includem fisierul bootstrap Yii
require_once('path/to/yii/framework/yii.php');
// cream instanta application si o rulam
$configFile='path/to/config/file.php';
Yii::createWebApplication($configFile)->run();
Acest fisier include intai fisierul bootstrap al platformei Yii yii.php. Apoi creaza o instanta de
aplicatie Web cu configuratia specificata, si apoi o ruleaza.
1. Modul Debug
O aplicatie Yii poate rula ori in modul debug, ori in modul production. Valoarea
constantei YII_DEBUGdetermina acest mod. Implicit, valoarea constantei estefalse, aceasta
insemnand ca modul implicit este production. Pentru a rula aplicatia in modul debug, definim
constanta ca fiind true inainte de a include fisierulyii.php. Rularea aplicatiei in modul debug este
mult mai putin eficienta din cauza log-urilor interne necesare in timpul stadiului de dezvoltare al
aplicatiei cand avem nevoie de mai multe informatii atunci cand apar erori de programare.
Application
Application este locul unde se executa procesarea cererilor client. Rolul principal este analizarea
cererii client si transmiterea ei la controller-ul corespunzator pentru a fi procesata in continuare. De
asemenea, Application joaca un rol central pentru pastrarea configuratiilor la nivel de aplicatie. De
aceea, application mai este numita front-controller (controller radacina, principal).
Application este creata ca singleton de catre fisierul de intrare. In acest fel, accesul este posibil de
oriunde viaYii::app().
1. Configurare
15
Ghid de utilizare Yii
La baza, application este o instanta a CWebApplication. Pentru customizare, in mod normal trebuie
sa furnizam un fisier de configurare (care este de fapt un array) pentru a initializa valorile
proprietatilor atunci cand instanta application este creata. Ca alternativa de customizare, putem
extinde CWebApplication.
Configuratia in sine este un array cu perechi key-value (cheie-valoare). Fiecare key reprezinta
numele proprietatii instantei application, iar valoarea reprezinta valoarea initiala a proprietatii. De
exemplu, asa se configureaza proprietatile name si defaultController:
array(
'name'=>'Yii Framework',
'defaultController'=>'site',
)
return array(...);
$app=Yii::createWebApplication($configFile);
Sfat: Daca aplicatia are nevoie de o configuratie complexa, putem sa o separam in mai multe fisiere,
fiecare intorcand un array de configurare. Dupa aceea, in fisierul de configurare principal, adaugam
cu include() fiecare fisier creat.
16
Ghid de utilizare Yii
3. Componente
Functionalitatea aplicatiei poate fi usor customizata si imbogatita datorita arhitecturii foarte flexibile
de componente. Application gestioneaza un set de componente, fiecare implementand diverse
features. De exemplu, application analizeaza o cerere client cu ajutorul
componentelor CUrlManager si CHttpRequest.
Configurand proprietatea components, putem customiza orice valori ale componentelor folosite in
aplicatie. De exemplu, putem configura componenta CMemCache pentru a folosi mai multe servere
memcache:
array(
......
'components'=>array(
......
'cache'=>array(
'class'=>'CMemCache',
'servers'=>array(
array('host'=>'server1', 'port'=>11211, 'weight'=>60),
array('host'=>'server2', 'port'=>11211, 'weight'=>40),
),
),
),
)
4. Componente nucleu
Yii activeaza implicit un set de componente nucleu pentru a asigura anumite features intalnite in
majoritatea aplicatiilor Web. De exemplu, componenta request este folosita pentru a analiza cererile
client si pentru a furniza informatii folositoare despre URL, cookies. Prin configurarea proprietatilor
acestor componente nucleu, putem schimba comportamentul implicit al Yii aproape in orice privinta.
Mai jos este o lista de componente nucleu care sunt pre-declarate de catre CWebApplication.
17
Ghid de utilizare Yii
18
Ghid de utilizare Yii
Controller
Un controller este o instanta a clasei CController sau a unei clase derivate. Este creat de
application atunci cand o cerere client are nevoie. Atunci cand ruleaza, un controller executa un
action cerut de client. De obicei, action apeleaza modelele necesare si va genera un view
corespunzator (rezultatul vazut de client). Un action, in cea mai simpla forma, este doar o metoda a
clasei controllerului al carei nume incepe cu action.
Orice controller are un default action (actiune implicita). Atunci cand cererea client nu specifica ce
action va fi apelat, default action va fi apelat. Implicit, default action are numele index. Dar poate fi
schimbat prin setareaCController::defaultAction.
Mai jos este codul minim necesar pentru un controller. Din moment ce acest controller nu defineste
nici un action, orice cerere pentru acest controller va genera o exceptie.
1. Rute
Controller-ele si action-urile sunt identificate prin ID-uri. ID-ul controller are
formatul cale/catre/xyz care corespunde fisierului fizic al controller-
ului protected/controllers/cale/catre/XyzController.php, where the token xyz should be
replaced by actual names (ex. post corespunde
cuprotected/controllers/PostController.php). ID-ul action este numele metodei action fara
prefixulaction. De exemplu, daca o clasa controller contine o metoda cu numele actionEdit,
atunci ID-ul este edit.
Nota: Inainte de versiunea 1.0.3, ID-ul controller-ului era in formatul cale.catre.xyz in loc
decale/catre/xyz.
Utilizatorii cer un anumit controller si un anumit action prin intermediul unui route (ruta). Route este
format prin concatenarea unui ID controller si al unui ID action separate prin slash (/). De exemplu,
ruta post/edit se refera la controllerul PostController si la action-ul edit. Si implicit, URL-
ul http://hostname/index.php?r=post/edit va cere acest controller si acest action.
Nota: Implicit, rutele sunt case-sensitive. De la versiunea 1.0.1, este posibila crearea de rute case-
insensitive prin setarea CUrlManager::caseSensitive cu valoarea false in configuratia aplicatiei. In
modul case-insensitive, trebuie sa ne asiguram ca urmam conventia ca directoarele care contin
fisierele cu clasele controller-ului sunt in lower case, si ca atat controller map cat si action
mapfolosesc key-uri in lower case.
Incepand cu versiunea 1.0.3, o aplicatie poate contine module. Ruta pentru un action dintr-un
controller din interiorul unui modul are formatul moduleID/controllerID/actionID. Pentru mai
multe detalii, trebuie vazuta sectiunea despre module.
2. Instantierea Controller-ului
19
Ghid de utilizare Yii
Pentru ca acest action sa fie vizibil de catre controller, suprascriem metoda actions() din clasa
controller-ului:
20
Ghid de utilizare Yii
{
return array(
'edit'=>'application.controllers.post.UpdateAction',
);
}
}
protected/
controllers/
PostController.php
UserController.php
post/
CreateAction.php
ReadAction.php
UpdateAction.php
user/
CreateAction.php
ListAction.php
ProfileAction.php
UpdateAction.php
4. Filter
Filter (filtru) este o bucata de cod care poate fi executata inainte si/sau dupa ce un action al unui
controller a fost executat. De exemplu, un filtru de control de acces poate fi executat, pentru a se
asigura ca utilizatorul este autentificat inainte de a executa action-ul cerut; sau un filtru de
21
Ghid de utilizare Yii
performanta poate fi folosit inainte si dupa executia unui action, pentru a masura timpul de executie
al action-ului.
Un action poate avea mai multe filtre. Filtrele sunt executate in ordinea in care apar in lista de filtre.
Un filtru poate sa interzica executia celorlalte filtre ramase si a action-ului.
Un filtru poate fi definit ca metoda in clasa controller-ului. Numele metodei trebuie sa inceapa
cu filter. De exemplu, daca exista metoda filterAccessControl atunci se defineste un filtru cu
numele accessControl. Metoda filtru trebuie sa fie in forma urmatoare:
22
Ghid de utilizare Yii
......
public function filters()
{
return array(
'postOnly + edit, create',
array(
'application.filters.PerformanceFilter - edit, create',
'unit'=>'microsecunde',
),
);
}
}
Model
Un model este o instanta a clasei CModel sau a unei clase derivate. Modelele sunt folosite pentru a
pastra date si regulile lor de functionare relevante.
Un model reprezinta un singur obiect de date. Poate fi un rand dintr-o tabela a bazei de date, sau
poate fi un form cu input-uri venite de la utilizator. Fiecare camp al modelului reprezinta un atribut al
modelului. Fiecare atribut are un label care poate fi validat cu un set de reguli.
Yii implementeaza doua tipuri de modele: modelul form si active record. Ambele sunt derivate din
aceeasi clasa de baza CModel.
Un model form este o instanta a clasei CFormModel. Modelul form este folosit pentru a pastra datele
furnizate de utilizatorii Web. De obicei, aceste date sunt preluate, folosite, si apoi sterse. De
exemplu, intr-o pagina login, putem folosi un model form care va contine numele utilizatorului si
23
Ghid de utilizare Yii
parola lui. Ele vor fi preluate de la un utilizator web. Pentru mai multe detalii, trebuie citita
sectiunea Lucrul cu formularele
Active Record (AR) este un concept foarte raspandit si folosit prin care se face accesul la baza de
date asemanator accesului unui obiect. Fiecare obiect AR este o instanta a
clasei CActiveRecord sau a unei clase derivate. Un obiect AR reprezinta un singur rand dintr-o
tabela din baza de date. Campurile din acest rand sunt concepute ca proprietati ale obiectului AR.
Detalii despre AR pot fi gasite in sectiunea Active Record.
View
Un view este un fisier PHP care contine in principal elemente ale interfetei cu utilizatorul. Poate
contine instructiuni PHP, dar este recomandat ca aceste instructiuni sa nu schimbe modelele de
date si sa fie relativ simple. In spiritul de a mentine separarea intre programare si prezentare,
bucatile mari de programare ar trebui puse in controller sau in model, nu in view.
Un view are un nume care este folosit pentru a indentifica fisierul atunci cand trebuie generat view-
ul. Numele unui view este acelasi cu numele fisierului view. De exemplu, view-ul edit se refera la
un fisier view cu numeleedit.php. Pentru a genera un view, apelam CController::render() cu
numele view-ului. Metoda va cauta fisierul view corespunzator in
directorul protected/views/ControllerID.
In fisierul view, putem accesa instanta controller-ului folosind $this. Putem astfel
sa primim informatii din afara view-ului, in special proprietatile controller-ului, prin evaluarea $this-
>propertyName in interiorul view-lui.
Putem de asemenea sa folosim metoda de a trimite date view-ului inainte de generarea lui:
$this->render('edit', array(
'var1'=>$value1,
'var2'=>$value2,
));
Atfel, metoda render() va extrage al doilea array de parametri. Acesti parametri vor deveni variabile
in interiorul view-ului. Le vom putea accesa ca variabile locale, $var1 si $var2.
1. Layout
Layout-ul este un view special. Este folosit pentru a crea un container unic pentru view-uri. Poate sa
contina portiuni ale interfetei utilizator care sunt la fel in mai multe view-uri. De exemplu, un layout ar
putea contine portiuni header si footer si sa includa la mijloc continutul view-ului:
24
Ghid de utilizare Yii
sau
A doua metoda este folosita atunci cand widget-ul nu necesita vreun continut body.
Pentru a le customiza comportamentul, widget-urile pot fi configurate prin setarea valorilor initiale ale
proprietatilor atunci cand se apeleaza CBaseController::beginWidget sau CBaseController::widget.
De exemplu, atunci cand folosim widget-ul CMaskedTextField, am vrea sa specificam un mask care
va fi folosit. Putem face acest lucru transmitand un array cu acele proprietati si cu valorile lor initiale
dupa cum urmeaza:
<?php
$this->widget('CMaskedTextField',array(
'mask'=>'99/99/9999'
));
?>
Ca de obicei, array-ul contine perechi key-value. Key contine numele proprietatii, iar value contine
valoarea initiala a proprietatii respective.
25
Ghid de utilizare Yii
La fel ca un controller, un widget poate de asemenea avea un view personal. Implicit, fisierele view
ale widget-urilor se gasesc in subdirectorul views al directorului care contine fisierul clasei widget-
ului. Aceste view-uri pot fi generate prin apelarea CWidget::render(), la fel ca in controller. Singura
diferenta este ca la view-ul unui widget nu ii este aplicat nici un layout.
3. View-uri sistem
View-urile sistem se refera la view-urile folosite de platforma Yii pentru a afisa informatii despre
erori. De exemplu, atunci cand un utilizator cere un controller sau un action inexistent, Yii va genera
o exceptie, prin care se explica eroarea. Yii afiseaza exceptia folosind un view sistem specific.
Denumirea view-urilor sistem se face dupa unele reguli. Numele de genul errorXXX se refera la
view-uri pentru afisarea CHttpException cu codul de eroare XXX. De exemplu, daca este
generat CHttpException cu codul de eroare 404, atunci va fi afisat view-ul error404.
Yii furnizeaza un set de view-uri sistem implicite. Acestea sunt localizate in
directorul framework/views. Pot fi customizate prin crearea unor fisiere view cu acelasi nume dar in
directorul protected/views/system.
Componente
Aplicatiile Yii sunt construite pe baza componentelor, acestea fiind obiecte scrise pentru a indeplini
un anumit rol. O componenta este o instanta a clasei CComponent - sau a unei clase derivate din
ea. Folosirea unei componente implica in general accesarea proprietatilor sale si activarea/tratarea
(raise/handle) evenimentelor sale. Clasa de baza CComponent contine metodele de definire a
proprietatilor si evenimentelor.
1. Proprietatile unei componente
O proprietate este asemanatoare cu o variabila publica a unui obiect. Putem citi valoarea ei sau
putem sa ii atribuim o valoare. De exemplu:
26
Ghid de utilizare Yii
Pentru a defini o proprietate intr-o componenta, putem sa declaram simplu o variabila publica in
clasa componentei. Totusi, o modalitate mai flexibila ar fi definirea de metode getter si setter in felul
urmator:
27
Ghid de utilizare Yii
$this->raiseEvent('onClicked', $event);
}
$event este o instanta a CEvent sa a unei clase copil care reprezinta parametrul evenimentului.
Putem sa atasam o metoda acestui eveniment in felul urmator:
$component->onClicked=$callback;
$callback se refera la un callback valid PHP. Poate fi o functie globala sau o metoda a unei clase.
Daca este o metoda a unei clase, callback-ul trebuie dat ca un
array: array($object,'numeMetoda').
Un event handler va fi in felul urmator:
function numeMetoda($event)
{
......
}
28
Ghid de utilizare Yii
Behavior poate fi accesat ca o proprietate normala a unei componente. De exemplu, daca behaviour
cu numele tree este atasat unei componente, atunci putem obtine referinta la acest obiect
behaviour astfel:
$behavior=$component->tree;
// echivalentu cu urmatoarea linie:
// $behavior=$component->asa('tree');
Behavior poate fi temporar dezactivat. Astfel metodele sale nu mai sunt disponibile componentei. De
exemplu:
$component->disableBehavior($name);
// urmatoarea instructiune va genera o exceptie
$component->test();
Este posibil ca doua behaviours atasate la aceeasi componenta sa aiba metode cu acelasi nume. In
acest caz, metoda primului behaviour atasat va avea prioritate.
Atunci cand clasele behaviour sunt folosite impreuna cu evenimente, devin si mai puternice. Un
behaviour, atunci cand este atasat unei componente, poate atasa unele din metodele sale unor
evenimente ale componentei. Astfel, behaviour are posibilitatea de a observa sau de a schimba
fluxul de executie normal al componentei.
Module
Nota: Suportul pentru module este disponibil incepand cu versiunea 1.0.3.
Un modul este o unitate software de sine statatoare care este formata din modele, view-
uri, controllere si alte componente suportate. In multe privinte, un modul seamana cu o aplicatie.
Principala diferenta este ca un modul nu poate fi decat in interiorul unei aplicatii. Utilizatorii pot
accesa controller-ele dintr-un modul la fel cum acceseaza controller-ele normale din aplicatie.
Modulele sunt utile in cateva scenarii. Pentru o aplicatie complexa, putem alege sa o impartim in mai
multe module, fiecare fiind dezvoltat si intretinut separat. Unele feature-uri, folosite in mod obisnuit,
29
Ghid de utilizare Yii
precum gestiunea utilizatorilor, gestiunea comentariilor, pot fi dezvoltate ca module astfel incat ele
vor fi reutilizate usor in proiectele viitoare.
1. Creare modul
Un modul este organizat ca un director al carui nume este ID-ul sau unic. Strunctura directorului
modulului este similara cu cea a directorului de baza al aplicatiei. Structura de directoare tipica a
unui modul cu numele forumar arata in felul urmator:
forum/
Un modul trebuie sa aiba o clasa de modul care sa fie derivata din CWebModule. Numele clasei
este determinat folosind expresia ucfirst($id).'Module', unde $id se refera la ID-ul modulului
(acelasi cu numele directorului modulului). Clasa modulului serveste ca loc central pentru stocarea
informatiilor vizibile peste tot in codul modulului. De exemplu, putem
folosi CWebModule::params pentru a stoca parametrii modulului si sa
folosim CWebModule::components pentru a shera componentele aplicatiei la nivelul modulului.
Sfat: Putem folosi unealta yiic pentru a crea un schelet simplu al unui nou modul. De exemplu,
pentru a crea modulul forum de mai sus, putem executa urmatoarele comenzi intr-o linie de
comanda:
30
Ghid de utilizare Yii
% cd WebRoot/testdrive
% protected/yiic shell
2. Folosirea modulului
Pentru a folosi un modul, trebuie sa punem directorul modulului in directorul modules din directorul
de baza al aplicatiei. Apoi sa declaram ID-ul modulului in proprietatea modules a aplicatiei. De
exemplu, pentru a folosi modulul forum de mai sus, putem folosi urmatoarea configuratie de
aplicatie:
return array(
......
'modules'=>array('forum',...),
......
);
Un modul poate sa fie de asemenea configurat cu valori initiale pentru proprietatile sale. Folosirea
este foarte similara cu configurarea componentelor aplicatiei. De exemplu, modulul forum poate
avea o proprietate cu numele postPerPage (in clasa sa) care poate fi configurata in configuratia
aplicatiei in felul urmator:
return array(
......
'modules'=>array(
'forum'=>array(
'postPerPage'=>20,
),
),
......
);
Instanta modulului poate fi accesata prin proprietatea module a controller-ului activ in acest moment.
Prin instanta modulului, putem accesa apoi informatiile care sunt sherate la nivel de modul. De
exemplu, pentru a accesa informatia postPerPage de mai sus, putem folosi urmatoarea expresie:
31
Ghid de utilizare Yii
$postPerPage=Yii::app()->controller->module->postPerPage;
// sau urmatoarea daca $this se refera la instanta controller-ului
// $postPerPage=$this->module->postPerPage;
RootAlias.cale.catre.destinatie
32
Ghid de utilizare Yii
Yii::import('system.web.CController');
Putem folosi de asemenea urmatoarea sintaxa pentru a importa un intreg director, astfel incat toate
clasele publice din fisierele din acel director vor fi automat incluse atunci cand este necesar.
Yii::import('system.web.*');
Pe langa import, alias-urile sunt de asemenea folosite in multe alte locuri pentru a se face referinta
catre clase. De exemplu, un alis poate fi transmis catre Yii::createComponent() pentru a crea o
instanta a clasei corespunzatoare, chiar daca fisierul clasei nu a fost inclus anterior.
Sa nu confuzam path alias-urile cu namespace-uri. Un namespace se refera la o grupare logica a
unor nume de clase, astfel incat acestea sa fie diferite de alte clase care au acelasi nume. Path
alias-urile sunt folosite pentru a se face referinta la o clasa dintr-un fisier fizic sau la un director. Path
alias-ul nu intra in conflict cu
Sfat: Pentru ca inainte de versiunea PHP 5.3.0 nu exista suport pentru namespace, nu putem crea
instante a doua clase care au acelasi nume (dar definitii diferite). Din acest motiv, toate clasele
platformei Yii sunt prefixate cu litera 'C' (de la 'clasa') astfel incat vor fi diferite fata de clasele definite
ulterior de catre utilizatori. Este recomandat sa prefixam cu 'C' doar clasele din platforma Yii, iar
clasele definite de utilizatori sa fie prefixate cu alte litere.
Conventii
Yii este in favoarea conventiilor si sustine mai putin configuratiile. Doar urmand conventiile se pot
crea aplicatii Yii sofisticate, fara a scrie sau a gestiona configuratii complexe. Totusi, Yii poate fi
customizat in aproape orice aspect atunci cand configuratii noi sunt necesare.
Mai jos descriem conventiile care sunt recomandate pentru programarea in Yii. Pentru convenienta,
presupunem ca WebRoot este directorul in care este instalat aplicatia Yii.
1. URL
Implicit, Yii recunoaste URL-uri cu urmatorul format:
33
Ghid de utilizare Yii
http://hostname/index.php?r=ControllerID/ActionID
Variabila r (face parte din GET) se refera la ruta, care este folosit de Yii pentru a extrage controller-
ul si action-ul. Daca ActionID nu este precizat, controller-ul va lansa action-ul implicit (definit
inCController::defaultAction); iar daca ControllerID lipseste si el (sau variabla r lipseste cu totul),
atunci aplicatia va folosi controller-ul implicit (definit prin CWebApplication::defaultController).
Cu ajutorul clasei CUrlManager, este posibila crearea si recunoastea mai multor URL SEO-friendly,
precumhttp://hostname/ControllerID/ActionID.html. Acest feature este prezentat in detaliu
in Gestiunea URL.
2. Cod
Yii recomanda ca denumirea variabilelor, functiilor si tipurilor de clase sa se faca in stil camel. Stilul
camel inseamna capitalizarea fiecarui cuvant (prima litera a cuvantului este litera mare) din interiorul
numelui si alaturarea cuvintelor fara spatii intre ele. Pentru a se face diferenta fata de numele de
clase, numele de variabile si functii ar trebui sa inceapa cu un cuvant complet in lower-case.
(ex. $basePath,runController(), LinkPager). Pentru variabilele private ale unei clase, este
recomandat sa punem un caracter underscore _ in fata numelor (ex. $_actionList).
Conceptul de namespace a fost implementat o data cu versiunea PHP 5.3.0. Pentru ca versiunile
anterioare de PHP nu au implementat acest concept, recomandam denumirea claselor intr-un fel
unic pentru a evita conflictele de nume cu alte clase externe Yii. that classes be named in some
unique way to avoid name conflict with Tot din acest motiv, toate clasele Yii au in fata litera "C".
In ce priveste controller-ele, regula speciala este ca numele lor trebuie sa fie urmate de
cuvantul Controller. De exemplu, clasa PageController va avea ID-ul page. Deci ID-ul
controller-ului este numele clasei (cu prima litera mica), din care se indeparteaza apoi
cuvantulController. Aceasta regula face aplicatia mai sigura din punctul de vedere al securitatii.
De asemenea, URL-urile sunt mai compacte (ex. /index.php?r=page/indexin loc
de /index.php?r=PageController/index).
3. Configuratie
O configuratie este de fapt un array cu perechi key-value. Fiecare key reprezinta numele proprietatii
obiectului configurat. Fiecare value reprezinta valoarea initiala a proprietatii respective. De
exemplu,array('name'=>'My application', 'basePath'=>'./protected') initializeaza
proprietatile name sibasePath cu valorile initiale My application, respectiv ./protected.
Orice proprietate cu drept de scriere din orice obiect poate fi configurata. Daca nu este configurata,
proprietatea va lua valoarea implicita. Cand configuram o proprietate, este folositor sa citim
documentatia ei, astfel in cat valoarea initiala sa fie valida.
34
Ghid de utilizare Yii
4. Fisiere
Conventia pentru numele de fisiere depinde de tipurile lor.
Fisierele claselor ar trebui sa fie denumite cu numele public al claselor respective. De exemplu,
clasaCController este in fisierul CController.php. O clasa publica este o clasa care poate fi folosita
de orice alta clasa. Fiecare fisier care contine o clasa ar trebui sa contina cel mult o clasa publica.
Clasele private (care sunt folosite doar de catre o clasa publica) ar trebui sa existe in acelasi fisier in
care exista clasa publica.
Fisierele de tip view ar trebui sa aiba numele view-ului respectiv. De exemplu, view-ul view1 ar
trebui sa fie in fisierul view1.php. Un fisier view este un fisier PHP care contine cod PHP si HTML
doar cu scop de prezentare pentru client.
Fisierele de configurare pot fi denumite in orice fel. Un fisier de configurare este un fisier PHP al
carui singur scop este sa returneze un array care contine configuratia.
5. Directoare
Yii este structurat initial conform unui set implicit de directoare folosite pentru diverse scopuri.
Fiecare director poate fi customizat daca este nevoie.
35
Ghid de utilizare Yii
1. Cream scheletul structurii de directoare. Pentru a accelera acest pas, poate fi folosita
unealta yiicdescrisa in Crearea primei aplicatii Yii.
2. Configuram application. Acest pas este facut prin modificarea fisierul de configurare al
aplicatiei. Acest pas poate necesita scrierea unor componente (ex. componenta user).
3. Cream o clasa model pentru fiecare tip de date care trebuie administrate. Din
nou, yiic poate fi folosit pentru a genera automat clasa active record pentru fiecare tabela de
interes din baza de date.
4. Cream o clasa controller pentru fiecare tip de cerere utilizator. In general, daca o clasa
model trebui sa fie accesata de catre utilizatori, ar trebui sa aiba o clasa controller
corespunzatoare. Unealta yiic poate de asemenea sa faca acest pas automat.
5. Implementam action-uri si view-urile lor corespunzatoare. Acest pas implica din partea
noastra programare cu adevarat.
6. Configuram filtre necesare de action-uri in clasele controller.
7. Cream teme daca este necesar.
8. Cream mesaje traduse daca este necesara internationalizarea.
9. Identificam date si view-uri care pot fi introduse in cache si aplicam tehnici de caching.
10. In final, facem optimizari si apoi facem publica aplicatie.
Pentru fiecare din pasii de mai sus, pot fi create si executate test cases (cazuri de test).
Lucrul cu formulare
Colectarea datelor de la utilizator prin formulare HTML este unul din task-urile cele mai importante
din dezvoltarea unei aplicatii Web. In afara de proiectarea formularelor, programatorii trebuie sa
populeze formularele cu date existente sau cu valori implicite, sa valideze input-urile primite de la
utilizatori, sa afiseze mesaje de eroare corespunzatoare atunci cand exista input-uri care nu sunt
36
Ghid de utilizare Yii
valide, sa salveze datele valide primite de la utilizatori intr-un mediu de stocare permanent. Yii
simplifica enorm acest task datorita arhitecturii sale MVC.
1. Cream o clasa cu un model care reprezinta campurile de date care vor fi colectate.
2. Cream un action intr-un controller in care scriem cod care primeste datele de la utilizatori.
3. Cream un formular intr-un fisier view asociat cu action-ul controller-ului.
In urmatoarele sub-sectiuni, vom descrie in detaliu fiecare din acesti pasi.
Creare model
Inainte de a scrie cod HTML necesar pt un formular, trebuie sa ne decidem de ce fel de date vom
avea nevoie de la utilizatori si ce reguli trebuie sa indeplineasca aceste date. O clasa de model
poate fi folosita pentru a inregistra aceste date. Un model, asa cum este definit in sb-
sectiunea Model, este locul central pentru pastrarea input-urilor de la utilizatori si pentru validarea
lor.
In functie de cum folosim input-urile primite de la utilizator, putem crea doua tipuri de modele. Daca
datele de la utilizator sunt colectate, folosite si apoi abandonate, atunci cream unmodel de formular;
daca datele de la utilizator sunt colectate si apoi salvate in baza de date, atunci folosim unactive
record. Ambele tipuri de model sunt derivate din aceeasi clasa CModel care defineste interfata
necesara unui formular.
Nota: In general, folosim modele de formular in exemplele din aceasta sectiune. Dar toate aceste
exemple pot fi aplicate si modelelor de tip active record.
37
Ghid de utilizare Yii
Info: In loc sa denumim proprietati aceste trei variabile, folosim termenul de atribute pentru a face
diferenta fata de proprietatile normale. Un atribut este o proprietate care este in special folosita
pentru a pastra date care au venit de la utilizator sau din baza de date.
38
Ghid de utilizare Yii
AttributeList este un string de nume de atribute separate prin virgula care trebuie sa fie validate
cu regula in cauza; Validator specifica ce fel de validare ar trebui executata; parametrul on este
optional si specifica o lista de scenarii in care regula ar trebui sa fie aplicata; optiunile aditionale sunt
date in perechi de nume-valoare care sunt folosite pentru a initializa valorile proprietatilor
corespunzatoare din clasa validator.
Sunt trei cazuri in care putem specifica Validator in regula de validare. In primul
caz, Validator poate fi numele unei metode dintr-o clasa de model, ca de exemplu
metoda authenticate din exemplul de mai sus. Metoda validator trebuie sa aiba urmatoarea
declaratie:
/**
* @param string numele atributului care trebuie validat
* @param array optiuni specificate in regula de validare
*/
public function ValidatorName($attribute,$params) { ... }
In al doilea caz, Validator poate fi numele unei clase validator. Cand este aplicata regula, o
instanta a acestei clase validator va fi creata pentru a executa validarea efectiva. Optiunile aditionale
din regula sunt folosite pentru a initializa valorile atributelor instantei. O clasa validator trebui sa fie
derivata din CValidator.
Nota: Cand specificam reguli pentru un model de tip active record, putem folosi o optiune
specialaon. Optiunea poate fi 'insert' sau 'update', astfel incat regula va fi aplicata doar la
inserarea, sau respectiv actualizarea inregistrarii. Daca nu este precizat on, regula va fi aplicata in
ambele cazuri atunci cand vom apela metoda save().
39
Ghid de utilizare Yii
Dupa ce o instanta a unui model a fost creata, trebuie sa populam atributele sale cu datele trimise
de catre utilizatorul web. Putem face acest lucru mai usor folosind o asignare masiva:
$model=new LoginForm;
if(isset($_POST['LoginForm']))
$model->setAttributes($_POST['LoginForm'], 'login');
40
Ghid de utilizare Yii
foreach($_POST['LoginForm'] as $name=>$value)
{
if($name este atribut sigur)
$model->$name=$value;
}
Task-ul de a decide daca o informatie de intrare este sigura sau nu revine unei
metode safeAttributes() cu un scenariu specificat. In cazul modelului CFormModel, implicit,
metoda returneaza toate variabilele publice ale modelului, acest lucru insemnand ca toate aceste
variabile sunt sigure. In cazul modelului CActiveRecord, implicit, metoda returneaza toate coloanele
tabelei cu exceptia cheii primare, acest lucru insemnand ca toate aceste atribute sunt sigure. In
practica, trebuie sa suprascriem de obicei aceasta metoda pentru a enumera acele atribute care
sunt intr-adevar sigure, in functie de scenariu. De exemplu, un model user poate contine multe
atribute, dar in scenariul login avem nevoie doar de atributele username si password. Putem
specifica aceasta limitare in felul urmator:
array(
// aceste atribute pot fi asignate masiv in orice scenariu
// care nu este specificat mai jos
'attr1, attr2, ...',
*
41
Ghid de utilizare Yii
Daca un model nu se potriveste cu vreun scenariu (spre exemplu este folosit doar intr-un scenariu,
sau toate scenariile impart acelasi set de atribute sigure) valoarea returnata poate fi simplificata sub
forma unui singur string:
In cazul intrarilor de date care nu sunt sigure, trebuie sa le asignam atributelor corespunzatoare
folosind instructiuni de asignare individuale, in felul urmator:
$model->permission='admin';
$model->id=1;
4. Declansarea validarii
O data ce modelul deste populat cu datele trimise de utilizator, putem
apela CModel::validate() pentru a declansa procesul de validare a datelor. Metoda returneaza o
valoare care indica daca procesul de validare a avut succes sau nu. In cazul
modelului CActiveRecord, validarea poate de asemenea fi declansata atunci cand apelam
metoda CActiveRecord::save().
Cand apelam CModel::validate(), putem specifica un parametru de scenariu. Vor fi executate doar
regulile de validare care se aplica scenariului respectiv. O regula de validare se aplica intr-un
scenariu daca optiunea ona regulii nu este setata, sau daca contine numele de scenariu specificat.
Daca nu specificam scenariul atunci cand apelam CModel::validate(), vor fi executate doar acele
reguli pentru care optiunea on nu este setata.
De exemplu, executam urmatoarea instructiune pentru a executa validarea in cazul inregistrarii unui
utilizator:
$model->validate('register');
42
Ghid de utilizare Yii
Prin urmare, prima regula va fi aplicata in toate scenariile, in timp ce urmatoarele doua reguli vor fi
aplicate doar in cazul scenariului register.
Nota: Validarea in functie de scenariu este disponibila incepand cu versiunea 1.0.1 a Yii.
Implicit, CModel va returna label-ul unui atribut ca fiind numele respectivului atribut. Acest
comportament poate fi modificat prin suprascrierea metodei attributeLabels(). Dupa cum vom vedea
in urmatoarele sub-sectiuni, specificand label-uri in model ne permite sa cream un formular mult mai
puternic si mult mai rapid.
Creare action
O data ce avem un model, putem incepe sa scriem codul care este necesar pentru a ne folosi de
model. Putem crea acest cod intr-un action al unui controller. Pentru exemplul cu formularul de
logare, este necesar urmatorul cod:
43
Ghid de utilizare Yii
$this->redirect(Yii::app()->user->returnUrl);
}
// afiseaza formularul de logare
$this->render('login',array('user'=>$form));
}
In codul de mai sus, mai intai cream o instanta LoginForm; daca cererea este o cerere POST (adica
s-a apasat butonul de submit), populam $form cu datele primite aflate in $_POST['LoginForm'];
apoi putem valida input-urile si daca sunt valide, atunci se face redirectarea utilizatorului catre
pagina care a cerut autentificarea. Daca validarea esueaza, sau daca action-ul este accesat prima
data, atunci generam view-ullogin al carui continut va fi descris in urmatoarea sub-sectiune.
Sfat: In action-ul login, folosim Yii::app()->user->returnUrl pentru a afla URL-ul paginii care
a avut nevoie anterior de autentificare. Componenta Yii::app()->user este de tip CWebUser(sau
de timpul unei clase derivate din aceasta) care contine informatiile despre utilizator (ex. username,
status). Pentru mai mult detalii, trebuie vazuta sectiunea Autentificare si autorizare.
Trebuie sa acordam o atentie speciala urmatoarei instructiuni PHP care apare in action-ul login:
$form->attributes=$_POST['LoginForm'];
Dupa cum am explicat in Securizarea asignarilor de atribute, aceasta linie de cod populeaza modelul
cu datele trimise de catre utilizator. Proprietatea attributes este definita de catre CModel, care
asteapta un array de perechi nume-valoare si care asigneaza fiecare valoare la atributul
corespunzator al modelului. Deci, daca$_POST['LoginForm'] ne da un astfel de array,
presupunand ca fiecare atribut necesar este in acest array, codul de mai sus ar fi echivalent cu
urmatorul cod (care poate fi foarte lung daca sunt multe atribute):
$form->username=$_POST['LoginForm']['username'];
$form->password=$_POST['LoginForm']['password'];
$form->rememberMe=$_POST['LoginForm']['rememberMe'];
Creare formular
44
Ghid de utilizare Yii
Scrierea view-ului login este destul de directa. Pornim cu tagul form al carui atribut action ar trebui
sa fie URL-ul action-ului login descris anterior. Apoi inseram label-uri si campuri input pentru
atributele declarate in clasa LoginForm. La sfarsit, inseram un buton submit care poate fi apasat de
utilizator pentru a trimite datele. Toate acestea pot fi facute in HTML simplu.
Yii pune la dispozitie unele clase ajutatoare (helper) pentru a facilita compunerea view-urilor. De
exemplu, pentru a crea un camp input text, putem apela CHtml::textField(); pentru a crea o lista
drop-down, apelamCHtml::dropDownList().
Info: Ar putea aparea intrebarea: 'Dar care este beneficiul folosirii helper-elor daca este nevoie de o
cantitate similara de cod comparativ cu codul simplu HTML?'. Raspunsul este ca helper-ele pot pune
la dispozitie mai multe lucruri decat cod HTML. De exemplu, urmatorul cod va genera un camp input
text care va declansa submiterea formularului in cazul in care valoarea sa este schimbata de catre
utilizator:
CHtml::textField($name,$value,array('submit'=>''));
<div class="yiiForm">
<?php echo CHtml::form(); ?>
<div class="simple">
<?php echo CHtml::activeLabel($user,'username'); ?>
<?php echo CHtml::activeTextField($user,'username'); ?>
</div>
<div class="simple">
<?php echo CHtml::activeLabel($user,'password'); ?>
<?php echo CHtml::activePasswordField($user,'password');
?>
</div>
<div class="action">
<?php echo CHtml::activeCheckBox($user,'rememberMe'); ?>
Remember me next time<br/>
<?php echo CHtml::submitButton('Login'); ?>
</div>
45
Ghid de utilizare Yii
</form>
</div><!-- yiiForm -->
46
Ghid de utilizare Yii
Avand action-ul pregatit, trebuie sa cream view-ul batchUpdate pentru a afisa campurile input intr-o
tabela HTML.
<div class="yiiForm">
<?php echo CHtml::form(); ?>
<table>
<tr><th>Name</th><th>Price</th><th>Count</th><th>Description</th></tr>
<?php foreach($items as $i=>$item): ?>
<tr>
<td><?php echo CHtml::activeTextField($item,"name[$i]"); ?></td>
<td><?php echo CHtml::activeTextField($item,"price[$i]"); ?></td>
<td><?php echo CHtml::activeTextField($item,"count[$i]"); ?></td>
<td><?php echo CHtml::activeTextArea($item,"description[$i]"); ?></td>
</tr>
<?php endforeach; ?>
</table>
47
Ghid de utilizare Yii
Desi Yii DAO si Yii AR se pot descurca in aproape toate situatiile, putem totusi folosi propriile
noastre biblioteci de acces la baze de date. De fapt, platforma Yii este proiectata foarte atent pentru
a fi folosita usor cu biblioteci externe third-party.
Yii DAO este construit pe baza PDO (obiecte date PHP) care este o extensie PHP ce pune la
dispozitie accesul unificat la date stocate in diverse DBMS cunoscute, precum MySQL si
PostgreSQL. De aceea, pentru a folosi Yii DAO, trebuie instalate extensia PDO si driverul PDO de
baze de date specific (ex. PDO_MYSQL).
Yii DAO este format in principal din urmatoarele patru clase:
48
Ghid de utilizare Yii
$connection=new CDbConnection($dsn,$username,$password);
// stabilirea conexiunii. Putem incerca try-catch pentru a identifica exceptii
posibile
$connection->active=true;
......
$connection->active=false; // inchidere conexiune
Formatul DSN-ului depinde de driverul PDO folosit. In general, DSN este format din numele driver-
ului PDO, urmat de semnul :, urmat de sintaxa conexiunii specifice driver-ului. Pentru informatii
complete, trebuie vazutadocumentatia PDO. Mai jos este o lista de format-uri obisnuite pentru DSN:
SQLite: sqlite:/path/to/dbfile
MySQL: mysql:host=localhost;dbname=testdb
PostgreSQL: pgsql:host=localhost;port=5432;dbname=testdb
Pentru ca CDbConnection este derivata din clasa CApplicationComponent, o putem folosi de
asemenea pe postul de componenta aplicatie. Pentru a face acest lucru, configuram componenta
aplicatie db (sau alta componenta aplicatie, daca se doreste) din configurarea aplicatiei dupa cum
urmeaza:
array(
......
'components'=>array(
......
'db'=>array(
'class'=>'CDbConnection',
'connectionString'=>'mysql:host=localhost;dbname=testdb',
'username'=>'root',
'password'=>'password',
),
),
)
Putem dupa aceea sa accesam conexiunea DB prin Yii::app()->db, care este activata automat
(putem interzice acest comportament prin setarea cu false a
proprietatii CDbConnection::autoConnect. Folosind aceasta metoda conexiunea DB poate fi utilizata
oriunde in cod.
49
Ghid de utilizare Yii
$command=$connection->createCommand($sql);
// daca este necesar, instructiunea SQL poate fi actualizata asa:
// $command->text=$newSQL;
O instructiune SQL este executata prin CDbCommand in unul din urmatoarele doua moduri:
execute(): executa o instructiune SQL, precum INSERT, UPDATE si DELETE. Daca are succes,
returneaza numarul de randuri afectate.
query(): executa o instructiune SQL care returneaza randuri de date, precum SELECT. Daca
are succes, returneaza o instanta CDbDataReader pe care putem sa o parcurgem pentru a
folosi randurile de date rezultate. Pentru usurinta, este implementat de asemenea un set de
metode queryXXX() care returneaza direct rezultatele cererii.
Va fi generata o exceptie daca apare vreo eroare in timpul executiei instructiunilor SQL.
$dataReader=$command->query();
// apelam repetat read() pana cand returneaza false
while(($row=$dataReader->read())!==false) { ... }
// folosim foreach pentru a trece prin fiecare rand de date
foreach($dataReader as $row) { ... }
50
Ghid de utilizare Yii
4. Folosirea tranzactiilor
Cand o aplicatie executa cateva cereri, operatii de citire sau/si scriere in baza de date, este
important sa ne asiguram ca baza de date sa contina toate schimbarile facute de aceste operatii. In
aceste cazuri, poate fi initiata o tranzactie, reprezentata prin instanta CDbTransaction din Yii:
Incepem tranzactia.
Executam cererile una cate una. Orice actualizare nu este vizibila in exteriorul bazei de date.
Executam tranzactia. Actualizarile devin vizibile daca tranzactia a avut succes.
Daca o cerere esueaza, intreaga tranzactie este derulata inapoi.
Fluxul de lucru de mai sus poate fi implementat folosind urmatorul cod:
$transaction=$connection->beginTransaction();
try
{
$connection->createCommand($sql1)->execute();
$connection->createCommand($sql2)->execute();
//.... alte executii SQL
$transaction->commit();
}
catch(Exception $e) // daca o cerere esueaza, este generata o exceptie
{
$transaction->rollBack();
}
Placeholder-ele de parametri pot avea nume (token-uri unice) sau pot fi anonime (prin semne de
intrebare). Apelam CDbCommand::bindParam() sau CDbCommand::bindValue() pentru a inlocui
aceste placeholder-e cu parametrii reali. Conectarea parametrilor trebuie facuta inainte ca
instructiunea SQL sa fie executata.
51
Ghid de utilizare Yii
52
Ghid de utilizare Yii
Active Record
Desi Yii se poate descurca virtual cu orice task in ce priveste bazele de date, este foarte posibil ca in
90% din timpul nostru in care scriem instructiuni SQL sa scriem instructiuni SQL pentru operatii
CRUD obisnuite (create, read, update si delete). In plus, este dificil de intretinut codul in viitor.
Pentru a rezolva aceste probleme, putem folosi Active Record.
Active Record (AR) este o tehnica foarte populara ORM (Object-Relational Mapping). Fiecare clasa
AR reprezinta o tabela din baza de date ale carei atribute sunt reprezentate ca proprietati ale clasei
AR, iar o instanta a clasei AR reprezinta un rand din acea tabela din baza de date. Operatiile CRUD
obisnuite sunt implementate ca metode in clasa AR. Rezultatul este ca putem accesa tabela din
baza de date exact la fel cum accesam un obiect al unei clase oarecare. De exemplu, putem sa
folosim urmatorul cod pentru a insera un nou rand in tabela Post:
$post=new Post;
$post->title='Titlu post';
$post->content='Continutul post-ului';
$post->save();
In cele ce urmeaza descriem cum se configureaza Active Record si cum folosim Active Record in
operatiile CRUD obisnuite. Vom arata si cum putem folosi Active Record pentru a ne descurca cu
relatiile dintre tabele, dar in sectiunea urmatoare. Pentru simplitate, vom folosi urmatoarea tabela
dintr-o baza de date pentru toate exemplele din aceasta sectiune.
Nota: AR nu are scopul de a rezolva toate task-urile in legatura cu bazele de date. AR este cel mai
bine folosit in cazul operatiunilor SQL obisnuite. Pentru scenarii complexe, ar trebui folosit Yii DAO.
return array(
'components'=>array(
53
Ghid de utilizare Yii
'db'=>array(
'class'=>'system.db.CDbConnection',
'connectionString'=>'sqlite:path/to/dbfile',
// stergem comentariul de mai jos pentru a activa schema de caching
pentru performanta
// 'schemaCachingDuration'=>3600,
),
),
);
Suportul pentru AR este limitat de catre DBMS. In acest moment, au suport doar urmatoarele
DBMS:
54
Ghid de utilizare Yii
Nota: Clasele AR sunt de obicei folosite in mai multe locuri si de aceea putem importa intregul
director care contine clasele AR, in loc sa le includem pe fiecare una cate una. De exemplu, daca
toate fisierele cu clasele AR sunt in directorul protected/models, putem configura aplicatia in felul
urmator:
return array(
'import'=>array(
'application.models.*',
),
);
Implicit, numele clasei AR este acelasi cu numele tabelei din baza de date. Daca se doreste altfel,
trebuie suprascria metoda tableName(). Metoda model() este declarata astfel pentru fiecare clasa
AR (vom explica imediat).
Valorile coloanelor unui rand dintr-o tabela pot fi accesate ca proprietati ale instantei clasei AR
corespunzatoare. De exemplu, urmatorul cod seteaza coloana (atributul) title:
$post=new Post;
$post->title='Titlul post-ului';
3. Creating Record
Ca sa inseram un nou rand intr-o tabela a bazei de date, cream o instanta a clasei AR
corespunzatoare, ii setam proprietatile (care sunt asociate cu coloanele tabelei) si apelam
metoda save() pentru a termina inserarea.
55
Ghid de utilizare Yii
$post=new Post;
$post->title='Titlul post-ului';
$post->content='Continutul post-ului';
$post->createTime=time();
$post->save();
Daca cheia primara a tabelei se incrementeaza automat, dupa ce se termina inserarea, instanta AR
va contine o cheie primara actualizata. In exemplul de mai sus, proprietatea id va reflecta valoarea
cheii primare a post-ului nou inserat, chiar daca nu il modificam explicit.
Daca o coloana este definita cu o valoare implicita oarecare (ex. un string, un numar) in schema
tabelei, proprietatea corespunzatoare in instanta AR va avea automat atribuita aceasta valoare
atunci cand instanta AR este creata. O cale de a modifica aceasta valoare este prin a declara
explicit proprietatea in clasa AR:
$post=new Post;
echo $post->title; // Aceasta instructiune va afisa: Va rugam introduceti un titlu
$post=new Post;
$post->createTime=new CDbExpression('NOW()');
// $post->createTime='NOW()'; nu va functionat pentru ca
// 'NOW()' va fi tratat ca un string
$post->save();
4. Citirea inregistrarilor
Pentru a citi date dintr-o tabela, putem folosi una dintre metodele find dupa cum urmeaza.
56
Ghid de utilizare Yii
Putem de asemenea folosi $condition pentru a specifica alte conditii mult mai complexe. In loc de
un string,$condition poate fi o instanta a clasei CDbCriteria, care ne permite sa specificam alte
conditii pe langa clauza WHERE. De exemplu:
$criteria=new CDbCriteria;
$criteria->select='title'; // selecteaza doar coloana 'title'
$criteria->condition='postID=:postID';
$criteria->params=array(':postID'=>10);
$post=Post::model()->find($criteria); // $params nu este necesar
$post=Post::model()->find(array(
'select'=>'title',
57
Ghid de utilizare Yii
'condition'=>'postID=:postID',
'params'=>array(':postID'=>10),
));
Info: Atunci cand cream o conditie pentru cautarea unor coloane cu anumite valori, putem
folosifindByAttributes(). Parametrii $attributes vor fi transmisi intr-un array de valori indexate dupa
numele de coloana. In unele framework-uri, acest task poate fi facut prin apelarea unor metode de
genul findByNameAndTitle. Desi pare atractiva aceasta abordare, de obicei cauzeaza confuzie,
conflicte si probleme (ex. daca numele coloanelor sunt case-sensitive sau nu).
Atunci cand se potrivesc mai multe coloane cu conditia noastra, putem sa le extragem pe toate in
acelasi timp folosind metodele findAll, fiecare metoda find avand un corespondent findall.
Daca nu gaseste nimic, findAll va intoarce un array gol. Spre deosebire de find care ar intoarce
null daca nu gaseste nimic.
Pe langa metodele find si findAll descrise mai sus, urmatoarele metode sunt de asemenea
disponibile:
5. Actualizarea inregistrarilor
Dupa ce o instanta AR este populata cu valorile coloanelor, putem modifica valorile si apoi putem
salva noul stadiu in baza de date.
$post=Post::model()->findByPk(10);
$post->title='Titlul nou';
58
Ghid de utilizare Yii
Este de notat ca, dupa stergere, instanta AR ramane neschimbata, dar randul corespunzator din
tabela este deja sters.
Urmatoarele metode sunt puse la dispozitie pentru a sterge randuri fara a fi nevoie sa le incarcam
intai intr-o instanta AR:
59
Ghid de utilizare Yii
7. Validarea datelor
Atunci cand se insereaza sau se actualizeaza un rand, deseori trebuie sa verificam daca valorile
coloanelor sunt valide. Validarea datelor este importanta in special daca valorile coloanelor sunt
furnizate de catre utilizatori. In general, nu ar trebui sa avem niciodata incredere in informatiile care
vin de la client.
AR executa validarea datelor automat atunci cand este apelata save(). Validarea este bazata pe
regulile specificate in metoda rules() a clasei AR. Pentru mai multe detalii despre specificarea
regulilor de validare, trebuie sa vedem sectiunea Declararea regulilor de validare. Mai jos este un
flux de lucru necesar salvarii unei inregistrari:
if($post->save())
{
// datele sunt valide si sunt inserate/actualizate cu succes
}
else
{
// datele nu sunt valide, trebuie apelata getErrors() pentru a primi mesajele de
eroare
}
Atunci cand datele care trebuie inserate/actualizate sunt furnizate de catre clienti printr-un form
HTML, trebuie sa asignam aceste date proprietatilor AR corespunzatoare. Putem face acest lucru in
felul urmator:
$post->title=$_POST['title'];
$post->content=$_POST['content'];
$post->save();
Daca sunt multe coloane, vom vedea un sir lung de astfel de atribuiri. Pentru a ocoli acest lucru, se
poate folosi proprietatea attributes ca in exemplul de mai jos. Mai multe detalii le gasim in
sectiunea Securizarea asignarii atributelor si in sectiunea Creare action.
8. Compararea inregistrarilor
La fel ca randurile tabelei, instantele AR sunt identificate unic prin valorile cheilor lor primare. De
aceea, pentru a compara doua instante AR, trebuie sa comparam doar valorile cheilor lor primare,
60
Ghid de utilizare Yii
presupunand ca ele instantele apartin aceleiasi clase AR. O cale mai simpla, totusi, este sa
apelam CActiveRecord::equals().
Info: Spre deosebire de implementarile AR din alte framework-uri, Yii are suport pentru chei primare
compuse in instantele sale AR. O cheie primara compusa este formata din doua sau mai multe
coloane. Valoarea cheii primare este reprezentata in Yii ca un array.
Proprietatea primaryKeydefineste valoarea cheii primare a unei instante AR.
9. Customizare
CActiveRecord pune la dispozitie cateva metode care pot fi suprascrise in clasele derivate pentru a
schimba fluxul de lucru.
beforeValidate si afterValidate: acestea sunt apelate inainte si dupa validare.
beforeSave si afterSave: acestea sunt apelate inainte si dupa salvarea unei instante AR.
beforeDelete si afterDelete: acestea sunt apelate inainte si dupa ce o instanta AR este
stearsa.
afterConstruct: aceasta este apelata pentru fiecare instanta AR creata cu operatorul new.
afterFind: aceasta este apelata pentru fiecare instanta AR creata ca rezultat al cererii.
10. Folosirea tranzactiilor cu AR
Fiecare instanta AR contine o proprietate cu numele dbConnection care este o
instanta CDbConnection. De aceea, putem folosi tranzactii, feature pus la dispozitie de Yii DAO in
cazul in care se doreste folosirea lor cu AR.
$model=Post::model();
$transaction=$model->dbConnection->beginTransaction();
try
{
// find si save sunt doi pasi care pot fi intrerupti de o alta cerere
// de aceea, folosirea unei tranzactii asigura consistenta si integritate
datelor
$post=$model->findByPk(10);
$post->title='Titlu nou post';
$post->save();
$transaction->commit();
}
catch(Exception $e)
{
$transaction->rollBack();
}
61
Ghid de utilizare Yii
Am aratat deja cu se foloseste Active Record (AR) pentru a selecta date dintr-o singura tabela a
bazei de date. In aceasta sectiune, descriem cum se foloseste AR pentru a face join intre mai multe
tabele din baza de date si pentru a intoarce setul de date compus.
Pentru a folosi AR relational, estenecesar ca relatiile dintre cheile primare de tip foreign sa fie clar
definite intre tabelele carora li se aplica join. AR se bazeaza pe metadatele despre aceste relatii
pentru a determina cum se aplica join acestor tabele.
Nota: Incepand cu versiunea 1.0.1 a Yii, putem folosi AR relational chiar daca nu definim
constrangeri intre cheile foreign in baza de date.
Pentru simplicate, vom folosi schema bazei de date din diagrama ER (entity-relationship) de mai jos
in exemplele din aceasta sectiune.
ER Diagram
MySQL are suport pentru astfel de constrangeri doar cu engine-ul InnoDB. De aceea este
recomandat sa folosim InnoDB in bazele de date MySQL. Atunci cand se foloseste MyISAM, putem
sa exploatam urmatorul truc pentru a putea sa executam cereri relationale folosind AR:
62
Ghid de utilizare Yii
In cele de mai sus, folosim cuvantul cheie COMMENT pentru a descrie constrangerea foreign care
poate fi citita de catre AR pentru a recunoaste relatia descrisa.
1. Declararea relatiei
Inainte de a folosi AR pentru a executa cereri relationale, trebuie sa informam AR despre tipul de
relatie dintre clasele AR.
Relatia dintre doua clase AR este direct asociata cu relatia dintre tabelele bazei de date
reprezentate de catre clasele AR. Din punctul de vedere al bazei de date, o relatie dintre doua
tabele A si B este de trei tipuri: one-to-many (ex. User si Post), one-to-one (ex. User si Profile) si
many-to-many (ex. Category si Post). In AR, exista patru tipuri de relatii:
BELONGS_TO: Daca relatia dintre tabelele A si B este one-to-many, atunci B apartine lui A
(ex. Postapartine lui User);
HAS_MANY: daca relatia dintre tabelele A si B este one-to-many, atunci A are mai multi B
(ex. User are multe Post);
HAS_ONE: acesta este un caz special al lui HAS_MANY, in care A are cel mult un B
(ex. User are cel mult unProfile);
MANY_MANY: acesta corespunde cu relatia many-to-many din baza de date. O tabela
asociativa este necesara pentru a sparge o relatie many-to-many in relatii one-to-many, din
moment ce majoritatea DBMS nu au suport pentru relatii many-to-many direct. In schema bazei
de date din exemplul nostru,PostCategory serves for this purpose. In AR terminology, we can
explain MANY_MANY as the combination of BELONGS_TO and HAS_MANY. For
example, Post belongs to many Category and Category has manyPost.
Declararea relatiei in AR implica suprascrierea metodei relations() din clasa CActiveRecord. Metoda
returneaza un array cu configuratiile de relatii. Fiecare element din array reprezinta o singura relatie
cu urmatorul format:
63
Ghid de utilizare Yii
VarName este numele relatiei; RelationType specifica tipul relatiei, care poate fi unul din patru
constante:self::BELONGS_TO, self::HAS_ONE, self::HAS_MANY si self::MANY_MANY; ClassName
este numele clasei AR in relatie cu aceasta clasa AR; si ForeignKey precizeaza cheile foreign key
implicate in relatie. Optiuni aditionale pot fi specificate la sfarsitul fiecarei relatii (se va descrie mai
tarziu acest lucru).
Urmatorul cod arata cum declaram relatiile pentru clasele User si Post.
Info: O cheie foreign poate fi compusa, fiind formata din doua sau mai multe coloane. In acest caz,
ar trebui sa concatenam numele coloanelor care contin cheile foreign si sa separam cu spatiu sau
cu virgula. Pentru tipul de relatie MANY_MANY, tabela asociativa trebuie sa fie specificata de
asemenea in cheia foreign. De exemplu, relatia categories din Post este specificata cu cheia
foreign PostCategory(postID, categoryID).
Declararea relatiilor intr-o clasa AR adauga implicit o proprietate clasei pentru fiecare relatie. Dupa
ce este executata o cerere relationala, proprietatea corespunzatoare va fi populata cu instantele AR
64
Ghid de utilizare Yii
Info: Daca nu este nici o instanta reprezantand relatia respectiva, proprietatea va fi null sau un array
gol. Pentru relatiile BELONGS_TO si HAS_ONE, proprietatea va fi null; pentru
relatiile HAS_MANYsi MANY_MANY, proprietatea va fi un array gol.
Abordarea lazy loading este foarte convenabila, dar in unele scenarii nu este eficienta deloc. De
exemplu, daca vrem sa accesam informatiile despre autor pentru N post-uri, folosind abordarea lazy
ar implica executarea a Ncereri join. In acest caz, abordarea eager loading este de preferat.
Abordarea eager loading extrage instantele AR de legatura in acelasi timp cu instanta AR principala.
Acest lucru este facut folosind metoda with() impreuna cu una dintre metodele find sau findAll din
AR. De exemplu:
$posts=Post::model()->with('author')->findAll();
Codul de mai sus va returna un array de instante Post. Spre deosebire de abordarea lazy,
proprietateaauthor din fiecare instanta Post este deja populata cu instantele
corespunzatoare User inainte ca noi sa accesam proprietatea. In loc de a executa o cerere join
pentru fiecare post, prin abordarea eager loading se extrag toate post-urile cu autorii lor intr-un
singura cerere join!
Putem specifica mai multe nume de relatii in metoda with(). Astfel, abordarea eager loading va crea
toate relatiile impreuna in acelasi timp. De exemplu, urmatorul cod va extrage toate post-urile
impreuna cu autorii si categoriile lor:
$posts=Post::model()->with('author','categories')->findAll();
Putem de asemenea sa facem eager loading pe nivele. In loc sa furnizam o lista de nume de relatii,
furnizam o reprezentare ierarhica de nume de relatii catre metoda with(), ca in exemplul urmator:
65
Ghid de utilizare Yii
$posts=Post::model()->with(
'author.profile',
'author.posts',
'categories')->findAll();
Codul de mai sus va extrage toate post-urile impreuna cu autorul si categoriile lor. De asemenea,
vor fi extrase post-urile fiecarui autor si profilul sau.
Nota: Folosirea metodei with() a fost modificata incepand cu versiunea 1.0.2 a Yii. Trebuie citita cu
atentie documentatia API in cauza.
Implementarea AR din Yii este foarte eficienta. Atunci cand se aplica eager loading cu o ierarhie de
obiecte aflate in N relatii HAS_MANY sau MANY_MANY vor fi necesare N+1 cereri SQL pentru a obtine
rezultatele necesare. Aceasta inseamna ca, in exemplul anterior, trebuie executate 3 cereri SQL din
cauza proprietatilorposts si categories. Alte framework-uri au o abordare mult mai radicala
folosind doar o singura cerere SQL. La prima vedere, aceasta abordare pare mai eficienta, pentru ca
ar fi implicata doar o singura cerere SQL. In realitate, nu este deloc practic din doua motive. In
primul rand, sunt multe coloane de date repetitive in rezultat care necesita un timp in plus pentru a fi
transmise si procesate. In al doilea rand, numarul de randuri din setul de rezultate creste exponential
cu numarul de tabele implicate. Daca sunt mai multe relatii implicate, totul devine atat de greoi si
complex incat nu mai poate fi gestionat corespunzator.
Din versiunea 1.0.2 a Yii, putem de asemenea sa fortam o cerere relationala sa fie facuta intr-o
singura cerere SQL. Trebuie doar sa adaugam un apel together() dupa after with(). De
exemplu:example,
$posts=Post::model()->with(
'author.profile',
'author.posts',
'categories')->together()->findAll();
Codul de mai sus va fi facut intr-o singura cerere SQL. Fara apelarea together, ar fi fost necesare
doua cereri SQL: una in care se aplica join intre tabelele Post, User si Profile, iar cealalta in care
se aplica join intre tabelele User si Post.
3. Optiuni in cererile relationale
Am mentionat ca pot fi specificate optiuni aditionale in declaratia relatiei. Aceste optiuni, specificate
intr-un array de perechi key-value, sunt folosite pentru a customiza cererea relationala. Avem un
sumar mai jos.
select: o lista de coloane care vor fi selectate pentru clasa AR de legatura. Implicit, aceasta
lista este '*', adica toate coloanele. Numele de coloane ar trebui sa fie diferentiate
folosind aliasToken daca apar intr-o expresie (ex. COUNT(??.name) AS nameCount).
66
Ghid de utilizare Yii
params: parametrii care vor fi legati la instructiunea SQL. Ar trebui sa primeasca un array cu
perechi nume-valoare. Aceasta optiune este disponibila incepand cu versiunea 1.0.3.
condition: clauza WHERE. Implicit nu contine nimic, Referintele catre coloane trebuie sa fie
diferentiate folosind aliasToken (ex. ??.id=10).
on: clauza ON. Conditia specificata aici va fi adaugata la conditia join folosind operatorul AND.
Aceasta optiune este disponibila incepand cu versiunea 1.0.2 a Yii.
order: clauza ORDER BY. implicit nu contine nimic. Referintele catre coloane trebuie sa fie
diferentiate folosind aliasToken (ex. ??.age DESC).
with: o lista cu obiectele inrudite care ar trebui incarcate impreuna cu acest obiect. Aceasta
lista este creata doar prin abordarea lazy loading, nu eager loading.
joinType: tipul de join pentru aceasta relatie. Implcit este LEFT OUTER JOIN.
aliasToken: placeholder pentru prefix de coloana. Va fi inlocuit cu alias-ul tabelei
corespunzatoare pentru a se putea discrimina referintele la coloane. Implicit este '??.'.
alias: alias pentru tabela asociata cu aceasta relatie. Aceasta optiune este disponibila din
versiunea 1.0.1 a Yii. Implicit este null, adica alias-ul tabelei este generat automat. Este diferit
fata de aliasToken.aliasToken este doar un placeholder si va fi inlocuit cu alias-ul tabelei in
cauza.
together: daca tabela asociata cu aceasta relatie should ar trebui sa faca un join fortat cu
tabela primara. Aceasta optiune are sens pentru relatiile HAS_MANY si MANY_MANY. Daca
optiunea nu este setata sau este false, fiecare relatie HAS_MANY sau MANY_MANY va avea
instructiunea ei JOIN proprie pentru a imbunatati performanta. Aceasta optiune este disponibila
incepand cu versiunea 1.0.3.
In plus, sunt disponibile urmatoarele optiuni pentru anumite relatii in timpul abordarii lazy loading:
group: clauza GROUP BY. Implicit nu contine nimic. De notat ca referintele la coloane trebuie
diferentiate folosind aliasToken (ex. ??.age). Aceasta optiune este valabila doar in cazul
relatiilor HAS_MANY siMANY_MANY.
having: clauza HAVING. Implicit nu contine nimic. De notat ca referintele la coloane trebuie
sa fie diferentiate folosind aliasToken (ex. ??.age). Aceasta optiune este valabila doar in
cazul relatiilorHAS_MANY si MANY_MANY. Este disponibila incepand cu versiunea 1.0.1 a Yii.
limit: clauza limit pentru limitarea randurilor selectate. Aceasta optiune NU se aplica
relatiei BELONGS_TO.
offset: offset pentru randurile care vor fi selectate. Aceasta optiune NU se aplica
relatiei BELONGS_TO.
Mai jos, modificam declaratia de relatie posts din User prin includerea unor optiuni de mai sus:
67
Ghid de utilizare Yii
Acum, daca accesam $author->posts, ar trebui sa obtinem post-urile autorului sortate dupa timpul
de creare, in ordine descendenta. Fiecare instanta post are de asemenea categoriile incarcate deja.
Info: Atunci cand un nume de coloana apare in doua sau mai multe tabele care au fost legate printr-
un JOIN, trebuie sa fie diferentiate. Acest lucru il facem prin prefixarea numelui de coloana cu
numele tabelei. De exemplu, id devine Team.id. Totusi, in cererile relationale AR nu avem aceasta
libertate deoarce instructiunile SQL sunt generate automat de catre AR, deci fiecare tabela va primi
automat un alias. De aceea, pentru a evita eventuale conflicte dintre numele coloanelor, folosin un
placeholder pentru a indica existenta unei coloane care trebuie sa fie diferentiata fata de celelalte.
AR va inlocui placeholder-ul cu un alias de tabela corespunzator pentru a diferentia corect coloana
in cauza.
User::model()->with(array(
'posts'=>array('order'=>'??.createTime DESC'),
'profile',
))->findAll();
Cache
Caching este o modalitate eficienta si ieftina de a imbunatati performanta unei aplicatii Web. Prin
memorarea datelor statice in cache si prin servirea acestora atunci cand este necesar, se castiga
timpul in care aceste date statice ar fi fost generate de catre serverul Web.
68
Ghid de utilizare Yii
Folosirea caching-ului in Yii implica in general configurarea si accesarea unei componente cache a
aplicatiei Yii. Codul urmator configureaza componenta cache sa foloseasca clasa memcache cu
doua servere de cache:
array(
......
'components'=>array(
......
'cache'=>array(
'class'=>'system.caching.CMemCache',
'servers'=>array(
array('host'=>'server1', 'port'=>11211, 'weight'=>60),
array('host'=>'server2', 'port'=>11211, 'weight'=>40),
),
),
),
);
Caching-ul poate fi folosit la diferite nivele. La La nivelul cel mai de jos, folosim cache-ul pentru a
memora o singura entitate (ex. o variabila) si acest nivel poarta numele de data caching. La nivelul
urmator, memoram in cache un fragment dintr-o pagina web care este generat de o portiune a unui
fisier view. La cel mai inalt nivel, memoram o intreaga pagina web in cache si o servim din cache
atunci cand este necesar.
In urmatoarele cateva subsectiuni, vom arata cum sa folosim cache-ul in toate aceste cazuri.
69
Ghid de utilizare Yii
Nota: prin definitie, cache-ul este un mediu volatil de stocare. Nu asigura existenta unei portiuni de
date care a fost memorata in cache, chiar daca aceasta portiune teoretic nu a expirat inca. De
aceea, cache-ul nu trebuie folosit ca un mediu de stocare persistent (ex. a nu se folosi cache-ul
pentru a memora date despre sesiune).
Cache de date
Cashing-ul de date se refera la memorarea unor variabile PHP in cache si folosirea lor mai tarziu
prin extragerea lor din cache. In acest scop, clasa componentei de baza cacheCCache furnizeaza
doua metode care sunt folosite de obicei: set() si get().
Pentru a memora o variabila $value in cache, alegem un ID unic si apelam set() pentru a o
memora:
Yii::app()->cache->set($id, $value);
Datele memorate in cache vor ramane in cache pe un termen nedefinit pana cand sunt sterse
datorita unor politici de caching (ex. spatiul de memorie cache este plin si cele mai vechi date
trebuie sterse). Pentru a modifica acest comportament, putem de asemenea sa furnizam o perioada
de expirare atunci cand apelamset(), si astfel datele vor fi sterse din cache dupa o perioada de timp
specificata:
Mai tarziu, cand trebuie sa accesam aceasta variabila (in aceeasi cerere web sau in alta cerere
web), apelamget() cu ID-ul necesar pentru a extrage variabila din cache. Daca valoarea returnata
este false, atunci inseamna ca valorea respectiva nu mai este valabila in cache si ca ar trebui sa o
regeneram.
$value=Yii::app()->cache->get($id);
if($value===false)
{
// regeneram $value pentru ca nu mia exista in cache
// si o salvam iar in cache pentru o utilizare ulterioara:
Yii::app()->cache->set($id,$value);
}
Atunci cand alegem ID-ul pentru o variabila de memorat in cache, trebuie sa ne asiguram ca ID-ul
este unic printre variabilele aplicatiei noastre. Este SUFICIENT sa asiguram unicitatea variabilei in
aplicatia noastra. Componenta cache este suficient de inteligenta pentru a face diferenta intre ID-
urile a doua aplicatii diferite.
70
Ghid de utilizare Yii
Pentru a sterge o valoare din cache, apelam delete(). Pentru a sterge tot ce exista in cache,
apelam flush(). Trebuie sa fim foarte atenti cand apelam flush() pentru ca se sterg si datele din toate
celelalte aplicatii.
Sfat: Pentru ca CCache implementeaza ArrayAccess, o componenta cache poate fi folosita ca un
array, ca in urmatoarele exemple:
$cache=Yii::app()->cache;
$cache['var1']=$value1; // echivalent cu: $cache->set('var1',$value1);
$value2=$cache['var2']; // echivalent cu: $value2=$cache->get('var2');
1. Dependente Cache
In afara de expirare, datele din cache pot fi invalidate in functie de unele schimbari ale unor
dependente. De exemplu, daca introducem in cache continutul unui fisier, iar fisierul se modifica in
vreun fel, atunci ar trebui sa invalidam copia sa din cache si sa citim ultima versiune a fisierului
pentru a o adauga in cache.
1. Optiuni Caching
Atunci cand apelam beginCache(), putem furniza un array ca parametru care va contine optiunile de
caching cu care customizam memorarea in cache a fragmentului. De fapt,
metodele beginCache() si endCache() sunt un wrapper convenabil al widget-ului COutputCache. De
aceea, optiunile caching pot contine valori initiale pentru orice proprietati ale clasei COutputCache.
Durata
Probabil ca cea mai folosita optiune este durata de memorare in cache duration care specifica timpul
in care continutul de memorat va persista in cache. Este similar cu parametrul de expirare
al CCache::set(). Urmatorul cod memoreaza in cache fragmentul pentru cel mult o ora:
Daca nu mentionam optiunea duration, valoarea va fi cea implicita, adica 60, ceea ce inseamna ca
memorarea in cache va persista maxim 60 de secunde.
72
Ghid de utilizare Yii
Dependenta
Ca si in cazul caching-ului de date, fragmentul de memorat in cache poate avea de asemenea
dependente. De exemplu, continutul unui post care se afiseaza depinde de starea post-ului, daca a
fost modificat sau nu.
Pentru a specifica o dependenta, setam optiunea dependency, care poate fi sau un obiect care
implementeaza [ICacheDependency] sau un array de configurare care poate fi folosit pentru a
genera obiectul care contine dependenta. Urmatorul cod specifica dependenta fragmentului de
valoarea coloanei lastModified:
Variatie
Continutul de memorat in cache poate sa varieze in functie de unii parametri. De exemplu, profilul
personal poate sa arate diferit unor utilizatori diferiti. Pentru a memora in cache continutul profilelor,
copiile memorate in cache ar trebui variate in functie de ID-urile utilizatorilor. Esential, acest lucru
inseamna ca ar trebui sa folosim un ID diferit atunci cand apelam beginCache().
Nu trebuie sa cerem programatorilor sa creeze o schema de ID-uri, pentru ca acest feature este deja
implementat in COutputCache. Avem mai jos un sumar.
varyByRoute: prin setarea acestei optiuni cu valoarea true, continutul cache va fi variat in
functie de ruta. De aceea, fiecare combinatie de controller si action va avea un continut
memorat in cache separat.
varyBySession: prin setarea acestei optiuni cu valoarea true, continutul memorat in cache va
fi variat in functie de ID-urile de sesiune. De aceea, fiecare sesiune utilizator poate vedea o
versiune diferita a continutului si in acelasi timp sa fie toti serviti din cache.
varyByParam: prin setarea acestei optiuni cu un array de nume, continutul memorat in cache
poate fi variat in functie de valorile unor parametri specificati in GET. De exemplu, daca o
pagina afiseaza continutul unui post in functie de parametrul id din GET, putem sa
setam varyByParam sa fiearray('id'), astfel in cat sa memoram in cache continutul pentru
fiecare post al utilizatorului. Fara o astfel de variatie, am putea sa memoram in cache un singur
post al utilizatorului respectiv.
Tipuri de cereri
Uneori vrem ca fragmentul sa fie memorat in cache doar cand se face un anumit tip de cerere din
partea utilizatorilor web. De exemplu, pentru o pagina care afiseaza un form, am dori sa memoram
73
Ghid de utilizare Yii
in cache doar starea form-ului cand este initial ceruta de catre utilizatorii web (prin GET). Orice
afisare ulterioara a form-ului (prin POST) nu ar trebui sa fie servita din cache datorita input-urilor
care contin date introduse de utilizator. Pentru a face acest lucru, putem specifica
optiunea requestTypes:
2. Caching pe nivele
Caching-ul de fragmente poate fi pe mai multe nivele (nested). Un fragment memorat in cache poate
sa fie inclus intr-un fragment mai mare care este de asemenea memorat in cache. De exemplu,
comentariile sunt memorate in interiorul unui fragment mai mare din cache, si sunt memorate
impreuna cu post-ul.
Pot fi setate mai multe optiuni in caching-ul pe nivele. De exemplu, fragmentele din exemplul de mai
sus (interior si exterior) pot fi memorate pe durate diferite de timp. Atunci cand fragmentul exterior nu
mai este valid, fragmentul interior inca poate furniza continut valid. Oricum, nu este valabil si invers.
Daca fragmentul exterior furnizeaza continut valid, va furniza mereu copia memorata in cache, chiar
daca fragmentul interior deja a expirat.
Cache de pagini
Caching-ul de pagini se refera la memorarea in cache a continutului unei intregi pagini. Caching-ul
de pagini poate aparea si in aplicatia server si in aplicatia client.
De exemplu, prin alegerea unui header de pagina corespunzator, browser-ul clientului poate
memora in cache pagina pentru o perioada limitata de timp.
74
Ghid de utilizare Yii
Aplicatia web insasi poate memora continutului paginii in cache si in aceasta subsectiune ne referim
la aceasta tehnica.
Caching-ul de pagini poate fi considerat un caz special al caching-ului de fragmente. Continutul unei
pagini este de obicei generat prin aplicarea unui layout la un view. De aceea, caching-ul nu va
functiona daca apelam in interiorul layout-ului beginCache() si endCache(). Cauza este ca layout-ul
este aplicat in interiorul metodeiCController::render() DUPA ce view-ul a fost evaluat.
Pentru a memora in cache o pagina intreaga, ar trebui sa sarim peste action-ul care genereaza
continutul paginii. Putem folosi COutputCache ca filtru al respectivului action pentru a ne atinge
scopul. Urmatorul cod arata cum sa configuram filtrul cache:
Configuratia filtrului de mai sus face ca filtrul sa fie aplicat tuturor action-urilor din controller. Putem
preciza caror action-uri le va fi aplicat filtrul. Putem folosi in acest scop operatorul +. Mai multe detalii
pot fi gasite in sectiunea despre filtre.
Sfat: Putem folosi clasa COutputCache ca filtru pentru ca este derivata din clasa CFilterWidget,
ceea ce inseamna ca este atat filtru cat si widget. De fapt, un widget functioneaza foarte asemanator
cu un filtru: un widget incepe inainte de evaluarea continutului HTML care ii urmeaza, si apoi widget-
ul se termina dupa ce continutul HTML a fost evaluat.
Continut dinamic
Atunci cand folosim caching de fragmente sau caching de pagini, intalnim de obicei situatia in care
intreaga portiune a output-ului este relativ statica cu exceptia unei mici portiuni (sau mai multor mici
portiuni). De exemplu, o pagina de ajutor, poate afisa informatii statice de ajutor, dar cu numele
utilizatorului (inregistrat in acest moment) afisat in partea de sus a paginii.
Pentru a rezolva aceasta situatie, putem retine in cache continutul paginii pentru fiecare nume de
utilizator in parte. Aceasta varianta va duce la o extrema irosire de spatiu cache pretios. In special
pentru ca se afiseaza in mare parte acelasi lucru, cu exceptia numelui utilizatorului.
O alta varianta ar fi sa impartim pagina in mai multe fragmente si sa le memoram in cache pe fiecare
in parte, dar aceasta varianta complica view-ul si face codul prea complex.
75
Ghid de utilizare Yii
O mai buna abordare este sa folosim feature-ul de continut dinamic (dynamic content) furnizata
de CController.
Un continut dinamic inseamna un fragment de output care nu ar trebui sa fie memorat in cache,
chiar daca este in interiorul unui fragment cache. Pentru aface acest continut dinamic mereu, trebuie
sa fie generat de fiecare data, chiar daca portiunea care include fragmentul nostru este generat din
cache la momentul cererii utilizatorului web. Din acest motiv, trebuie sa generam continutul dinamic
printr-o functie sau metoda.
In codul de mai sus, $callback se refera la un callback PHP valid. Poate sa fie un string care se
refera ori la numele unei metode din clasa controller-ului curent ori la numele unei functii globale.
Poate de asemenea sa fie un array care se refera la o metoda a unei clase. Orice parametri in plus
transmisi catre renderDynamic()vor fi transmisi callback. Callback-ul ar trebui sa returneze continutul
dinamic in loc sa il afiseze.
Generalitati
Extinderea platformei Yii este o activitate obisnuita in timpul dezvoltarii. De exemplu, daca scriem un
nou controller, extindem Yii prin derivarea clasei sale CController. In cazul unui nou widget,
derivam CWidget. Daca scriem cod care este proiectat sa fie refolosit de catre alti programatori,
atunci denumim acest cod extensie.
O extensie de obicei foloseste pentru un singur scop. In termenii platformei Yii, o extensie poate fi
clasificata in felul urmator:
componenta de aplicatie
widget
controller
action
filtru
comanda de consola
validator: un validator este o clasa de componenta derivata din CValidator.
helper: un helper este o clasa care contine doar metode statice. Folosim metodele ca niste
functii globale impreuna cu numele clasei din care apartin ca namespace.
76
Ghid de utilizare Yii
modul: un modul este o unitate de sine statatoare care este formata din modele, view-
uri, controllere si alte componente suportate. In multe privinte, un modul seamana cu
o aplicatie. Singura diferenta este ca un modul este in interiorul unei aplicatii. De exemplu,
putem putem avea un modul care pune la dispozitie functionalitati de gestiune utilizatori.
O extensie poate fi de asemenea o componenta care nu apartine nici unei categorii de mai sus. De
fapt, platforma Yii este proiectata foarte atent pentru a permite extinderea oricarei parti din codul sau
pentru a fi potrivita fiecarei nevoi individuale.
Folosirea extensiilor
Folosirea unei extensii implica urmatorii trei pasi:
return array(
// 'preload'=>array('xyz',...),
'components'=>array(
'xyz'=>array(
'class'=>'application.extensions.xyz.XyzClass',
'property1'=>'value1',
'property2'=>'value2',
),
// alte configuratii de componente
),
);
77
Ghid de utilizare Yii
2. Widget
Widget-urile sunt cel mai folosite in view-uri. Daca avem o clasa widget XyzClass (care apartine
extensiei xyz), putem s-o folosim intr-un view in felul urmator:
3. Action
Action-urile sunt folosite de un controller pentru a raspunde diverselor cereri din partea utilizatorilor
web. Daca avem o clasa action XyzClass (care apartine extensiei xyz), putem s-o folosim prin
suprascrierea metodeiCController::actions din clasa controller-ului nostru:
78
Ghid de utilizare Yii
tratata de un action. Daca avem o clasa filtru XyzClass(care apartine extensiei xyz), putem s-o
folosim prin suprascrierea metodeiCController::filters din clasa controller-ului nostru:
In codul de mai sus, putem sa folosim operatorii + si - in primul element al array-ului pentru a aplica
filtrul doar unor anumite action-uri. Pentru mai multe detalii, putem vedea sectiunea
despre CController din documentatie.
5. Controller
Un controller asigura un set de action-uri care pot fi cerute de catre utilizatorii web. Pentru afolosi un
controller al unei extensii, trebuie sa configuram proprietatea CWebApplication::controllerMap din
fisierul care contineconfigurarea aplicatiei:
return array(
'controllerMap'=>array(
'xyz'=>array(
'class'=>'application.extensions.xyz.XyzClass',
'property1'=>'value1',
'property2'=>'value2',
),
// alte controller-e
),
);
79
Ghid de utilizare Yii
7. Comanda de consola
O comanda de consola a unei extensii de obicei imbunatateste unealta yiic prin adaugarea unei
noi comenzi. Daca avem o clasa cu o comanda de consola XyzClass (care apartine unei
extensii xyz), putem s-o folosim prin modificarea fisierului de configurare al aplicatiei de consola:
return array(
'commandMap'=>array(
'xyz'=>array(
'class'=>'application.extensions.xyz.XyzClass',
'property1'=>'value1',
'property2'=>'value2',
),
// alte comenzi
),
);
8. Module
80
Ghid de utilizare Yii
Un modul de obicei este format din mai multe fisiere cu clase si este un mix format din tipurile de
extensii de mai sus. De aceea, ar trebui sa urmam instructiunile corespunzatoare pentru a folosi un
modul.
9. Componenta generica
Pentru a folosi o componenta generica, mai intai trebuie sa includem fisierul care contine clasa sa:
Yii::import('application.extensions.xyz.XyzClass');
Apoi, putem crea o instanta a clasei, putem sa ii configuram proprietatile si sa ii apelam metodele.
De asemenea, putem sa o derivam pentru a crea noi clase derivate.
Crearea extensiilor
O extensie cere un efort in plus din partea programatorului, deoarece o extensie are scopul de a fi
folosita de catre alti programatori. In cele ce urmeaza, avem cateva principii generale:
O extensie ar trebui sa fie de sine statatoare. Adica, toate dependentele sale externe ar
trebui sa fie reduse la minim. Daca extensia ar necesita instalarea de pachete aditionale, alte
clase sau alte fisiere, vor fi prea multe dureri de cap pentru cei care o vor folosi.
Fisierele care apartin extensiei ar trebui sa fie organizate in interiorul directorului extensiei
(care trebuie sa ii poarte numele).
Clasele unei extensii ar trebui sa fie prefixate cu unele litere pentru a evita conflictul de nume
cu clasele altor extensii.
O extensie ar trebui sa aiba informatii despre instalare si documentatie API. Aceste doua
informatii reduc timpul si efortul depus de eventualii programatori care o vor folosi.
O extensie ar trebui sa foloseasca o licenta corespunzatoare. Daca vrem ca extensia sa fie
si open-source si proiect privat, putem lua in considerare licentele BSD, MIT, etc., nu GPL
pentru ca sub GPL, codul va fi open-source de asemenea.
In cele ce urmeaza, vom descrie cum sa cream o extensie noua, pornind de la categorisirea facuta
ingeneralitati despre extensii. Aceste descrieri sunt valabile si in cazul in care cream o componenta
folosita in special in propriile noastre proiecte.
1. Componenta de aplicatie
O componenta de aplicatie trebuie sa implementeze interfata [IApplicationComponent] sau sa fie
derivata din clasa CApplicationComponent. Pricipala metoda care trebuie implementata este
[IApplicationComponent::init] in care se fac initializarile componentei. Aceasta metoda este apelata
dupa ce componenta este creata si dupa ce sunt initializate proprietatile clasei cu valorile initiale
specificate in configurarea aplicatiei.
81
Ghid de utilizare Yii
Implicit, o componenta a aplicatiei este creata si initializata doar atunci cand este accesata prima
data in timpul tratarii unei cereri de la utilizatorul web. Daca o componenta a aplicatiei trebuie sa fie
creata imediat dupa ce este creata instanta aplicatiei insasi, va trebui sa ii adaugam ID-ul in
proprietatea CApplication::preload.
2. Widget
Un widget trebuie sa fie derivat din clasa CWidget sau dintr-o clasa derivata.
Cel mai usor mod de a crea un nou widget este sa il derivam dintr-un widget existent si sa ii
suprascriem metodele sau sa ii schimbam valorile sale initiale. De exemplu, daca vrem sa folosim
un alt stil CSS pentruCTabView, ar putem sa configuram proprietatea sa CTabView::cssFile. Putem
de asemenea deriva CTabViewdupa cum urmeaza, ca sa nu mai fie nevoie sa configuram
proprietatea de fiecare data cand folosim widget-ul.
Un widget poate de asemenea sa aiba propriile sale fisiere view. In cazul acesta, cream un
directorul cu numeleviews in interiorul directorului care contine fisierul clasei widget-ului, si punem
acolo toate fisierele view. In clasa widget-ului, pentru a procesa un view al widget-ului,
folosim $this->render('ViewName'), la fel ca in cazul unui controller.
3. Action
Un action trebuie sa fie derivat din clasa CAction sau una din clasele sale derivate. Principala
metoda care trebuie implementata pentru un action este [IAction::run].
4. Filtru
Un filtru trebuie sa fie derivat din clasa CFilter sau una din clasele sale derivate. Principalele metode
care trebuie implementate pentru un filtru
sunt CFilter::preFilter si CFilter::postFilter. CFilter::preFilter este apelat inainte ca action-ul sa fie
executat. CFilter::postFilter este apelat dupa ce action-ul a fost executat.
7. Console Command
console command ar trebui sa fie derivata din clasa CConsoleCommand si sa implementeze
metodaCConsoleCommand::run. Optional, putem suprascrie CConsoleCommand::getHelp pentru a
pune la dispozitie informatii despre comanda.
8. Modul
Pentru a crea un modul, trebuie vazuta sectiunea despre module.
84
Ghid de utilizare Yii
Un principiu general pentru dezvoltarea unui modul este ca ar trebui sa fie de sine statator. Fisierele
cu resursele lui (CSS, JavaScript, imagini) ar trebui sa fie distribuite impreuna cu modulul. In acelasi
timp, modului trebuie sa le publice pentru a fi accesibile utilizatorilor Web.
9. Componenta generica
Dezvoltarea unei extensii de componenta generica este la fel ca scrierea unei clase. Din nou, ar
trebui sa fie de sine statatoare pentru a fi usor de folosit de alti programatori.
Yii::import('application.vendors.*');
require_once('Zend/Search/Lucene.php');
Codul de mai sus include fisierul Lucene.php. Deoarece folosim o cale relativa, trebuie sa
modificam calea PHP include path pentru ca acest fisier sa fie localizat corespunzator. Acest lucru il
facem prin apelareaYii::import inainte de require_once.
O data ce toate de mai sus sunt pregatite, putem folosi clasa Lucene intr-un action al unui controller
in felul urmator:
$lucene=new Zend_Search_Lucene($pathOfIndex);
$hits=$lucene->find(strtolower($keyword));
URL Management
Complete URL management for a Web application involves two aspects. First, when a user request
comes in terms of a URL, the application needs to parse it into understandable parameters. Second,
the application needs to provide a way of creating URLs so that the created URLs can be
85
Ghid de utilizare Yii
understood by the application. For an Yii application, these are accomplished with the help
of CUrlManager.
1. Creating URLs
Although URLs can be hardcoded in controller views, it is often more flexible to create them
dynamically:
$url=$this->createUrl($route,$params);
/index.php?r=post/read&id=100
where parameters appear in the query string as a list of Name=Value concatenated with the
ampersand characters, and the r parameter specifies the request route. This URL format is not very
user-friendly because it requires several non-word characters.
We could make the above URL look cleaner and more self-explanatory by using the so-
called path format which eliminates the query string and puts the GET parameters into the path info
part of URL:
/index.php/post/read/id/100
array(
......
'components'=>array(
......
'urlManager'=>array(
'urlFormat'=>'path',
),
),
);
86
Ghid de utilizare Yii
Note that we do not need to specify the class of the urlManager component because it is pre-
declared asCUrlManager in CWebApplication.
Tip: The URL generated by the createUrl method is a relative one. In order to get an absolute URL,
we can prefix it with Yii::app()->hostInfo, or call createAbsoluteUrl.
2. User-friendly URLs
When path is used as the URL format, we can specify some URL rules to make our URLs even
more user-friendly. For example, we can generate a URL as short as /post/100, instead of the
lengthy/index.php/post/read/id/100. URL rules are used by CUrlManager for both URL
creation and parsing purposes.
To specify URL rules, we need to configure the rules property of the urlManager application
component:
array(
......
'components'=>array(
......
'urlManager'=>array(
'urlFormat'=>'path',
'rules'=>array(
'pattern1'=>'route1',
'pattern2'=>'route2',
'pattern3'=>'route3',
),
),
),
);
The rules are specified as an array of pattern-route pairs, each corresponding to a single rule. The
pattern of a rule is a string used to match the path info part of URLs. And the route of a rule should
refer to a valid controller route.
Info: Starting from version 1.0.6, a rule may be further customized by setting
its urlSuffix andcaseSensitive options. And starting from version 1.0.8, a rule may also
have defaultParamswhich represents a list of name-value pairs to be merged into $_GET. To
customize a rule with these options, we should specify the route part of the rule as an array, like the
following:
<ParamName:ParamPattern>
array(
'posts'=>'post/list',
'post/<id:\d+>'=>'post/read',
'post/<year:\d{4}>/<title>'=>'post/read',
)
88
Ghid de utilizare Yii
Note: Using URL rules will degrade application performance. This is because when parsing the
request URL, CUrlManager will attempt to match it with each rule until one can be applied. The more
the number of rules, the more the performance impact. Therefore, a high-traffic Web application
should minimize its use of URL rules.
Parameterizing Routes
Starting from version 1.0.5, we may reference named parameters in the route part of a rule. This
allows a rule to be applied to multiple routes based on matching criteria. It may also help reduce the
number of rules needed for an application, and thus improve the overall performance.
We use the following example rules to illustrate how to parameterize routes with named parameters:
array(
'<_c:(post|comment)>/<id:\d+>/<_a:(create|update|delete)>' => '<_c>/<_a>',
'<_c:(post|comment)>/<id:\d+>' => '<_c>/read',
'<_c:(post|comment)>s' => '<_c>/list',
)
In the above, we use two named parameters in the route part of the rules: _c and _a. The former
matches a controller ID to be either post or comment, while the latter matches an action ID to
be create, update ordelete. You may name the parameters differently as long as they do not
conflict with GET parameters that may appear in URLs.
Using the aboving rules, the URL /index.php/post/123/create would be parsed as the
route post/createwith GET parameter id=123. And given the route comment/list and GET
parameter page=2, we can create a URL /index.php/comments?page=2.
Parameterizing Hostnames
Starting from version 1.0.11, it is also possible to include hostname into the rules for parsing and
creating URLs. One may extract part of the hostname to be a GET parameter. For example, the
URLhttp://admin.example.com/en/profile may be parsed into GET
parameters user=admin and lang=en. On the other hand, rules with hostname may also be used to
create URLs with paratermized hostnames.
In order to use parameterized hostnames, simply declare URL rules with host info, e.g.:
array(
'http://<user:\w+>.example.com/<lang:\w+>/profile' => 'user/profile',
)
89
Ghid de utilizare Yii
The above example says that the first segment in the hostname should be treated
as user parameter while the first segment in the path info should be lang parameter. The rule
corresponds to the user/profile route.
Note that CUrlManager::showScriptName will not take effect when a URL is being created using a
rule with parameterized hostname.
Also note that if the application is under a sub-folder of the Web root, then the sub-folder should be
removed from the rule. For example, if the application is
under http://www.example.com/sandbox/blog, then we should still use the same URL rule as
described above without the sub-folder sandbox/blog.
Hiding index.php
There is one more thing that we can do to further clean our URLs, i.e., hiding the entry
script index.php in the URL. This requires us to configure the Web server as well as
the urlManager application component.
We first need to configure the Web server so that a URL without the entry script can still be handled
by the entry script. For Apache HTTP server, this can be done by turning on the URL rewriting
engine and specifying some rewriting rules. We can create the file /wwwroot/blog/.htaccess with
the following content. Note that the same content can also be put in the Apache configuration file
within the Directory element for/wwwroot/blog.
Options +FollowSymLinks
IndexIgnore */*
RewriteEngine on
RewriteRule . index.php
90
Ghid de utilizare Yii
Autentificare si autorizare
Autentificarea si autorizarea sunt necesare daca o pagina Web trebuie sa fie accesata doar de
anumiti utilizatori. Autentificarea in sine asigura ca cineva este cine pretinde ca este. De obicei, este
nevoie de un nume de utilizator si o parola, dar poate include si alte metode pentru demonstrarea
identitatii, precum smart card, amprente, etc. Autorizarea este calea prin care o persoana, o data ce
a fost identificata (autentificata), are permisiunea sa acceseze/modifice anumite resurse. Acest lucru
se face de obicei verificand daca respectiva persoana are un anumit rol care are permisiunea de a
accesa resursele in cauza.
Yii incorporeaza un sistem de autentificare/autorizare care este usor de folosit si de modificat pentru
orice nevoi.
In urmatorul exemplu, validam numele si parola folosind tabela user dintr-o baza de date prin
intermediul Active Record. De asemenea, suprascriem metoda getId pentru a returna
91
Ghid de utilizare Yii
variabila _id care este setata in timpul autentificarii (valoarea returnata implicit pentru ID este
numele utilizatorului username). In timpul autentificarii, memoram informatia title prin apelarea
metodei CBaseUserIdentity::setState.
92
Ghid de utilizare Yii
Folosind calasa de identitate si componenta user, putem implementa usor action-urile de logare si
delogare.
Implicit, un utilizator va fi delogat dupa o anumita perioada de timp de inactivitate, perioada care
depinde deconfiguratia sesiunii. Pentru a modifica acest comportament, putem seta cu true
proprietatea allowAutoLogincomponentei user si sa transmitem un parametru cu durata catre
metoda CWebUser::login. Utilizatorul va ramane dupa aceea logat o perioada egala cu perioada
specificata in acest parametru, chiar daca inchide fereastra browser-ului web. Este de notat faptul ca
acest feature presupune ca browser-ul utilizatorului accepta cookie-uri.
93
Ghid de utilizare Yii
return array(
'accessControl',
);
}
}
In codul de mai sus, specificam ca filtrul access control ar trebui sa fie aplicat pentru toate action-
urile controller-ului PostController. Regulile de autorizarea detaliate folosite de catre filtru sunt
specificate prin suprascrierea CController::accessRules din clasa controller-ului.
Codul de mai sus specifica trei reguli, fiecare reprezentata de un array. Primul element din array
este 'allow'sau 'deny' iar restul perechilor nume-valoare specifica parametrii pattern ai regulii.
Aceste reguli ne spun: action-urile create si edit nu pot fi executate de catre utilizatorii anonimi;
action-ul delete poate fi executat de utilizatorii cu rolul admin; action-ul delete nu poate fi executat
de nimeni.
Regulile de acces sunt evaluate una cate una in ordinea in care sunt specificate. Prima regula care
se potriveste cu pattern-ul curent (ex. numele utilizatorului, rolurile, IP-ul client, adresa) determina
rezultatul autorizarii. Daca aceasta regula este o regula allow, action-ul poate fi executat; daca este
94
Ghid de utilizare Yii
o regula deny, action-ul nu poate fi executat; daca nici una dintre aceste reguli nu se potriveste cu
contextul, action va fi executat.
Sfat: Pentru a ne asigura ca un action nu va fi executat in anumite contexte, este bine sa specificam
mereu o regula deny la sfarsitul setului de reguli care sa interzica executarea action-ului:
return array(
// ... alte reguli...
// urmatoarea regula interzice action-ul 'delete' in absolut orice context
array('deny',
'action'=>'delete',
),
);
Motivul pentru care adaugam aceasta regula este ca daca nici o regula nu se potriveste, action-ul va
fi executat.
If the user is not logged in and if the loginUrl property of the user component is configured to
be the URL of the login page, the browser will be redirected to that page.
95
Ghid de utilizare Yii
array(
......
'components'=>array(
'user'=>array(
// this is actually the default value
'loginUrl'=>array('site/login'),
),
),
)
If the browser is redirected to the login page and the login is successful, we may want to redirect the
browser back to the page that caused the authorization failure. How do we know the URL for that
page? We can get this information from the returnUrl property of the user component. We can thus
do the following to perform the redirection:
Yii::app()->request->redirect(Yii::app()->user->returnUrl);
Overview
A fundamental concept in Yii's RBAC is authorization item. An authorization item is a permission to
do something (e.g. creating new blog posts, managing users). According to its granularity and
targeted audience, authorization items can be classified as operations, tasks and roles. A role
consists of tasks, a task consists of operations, and an operation is a permission that is atomic. For
example, we can have a system withadministrator role which consists of post management task
and user management task. The user management task may consist of create user, update
user and delete user operations. For more flexibility, Yii also allows a role to consist of other roles
or operations, a task to consist of other tasks, and an operation to consist of other operations.
An authorization item is uniquely identified by its name.
96
Ghid de utilizare Yii
An authorization item may be associated with a business rule. A business rule is a piece of PHP
code that will be executed when performing access checking with respect to the item. Only when the
execution returns true, will the user be considered to have the permission represented by the item.
For example, when defining an operation updatePost, we would like to add a business rule that
checks if the user ID is the same as the post's author ID so that only the author himself can have the
permission to update a post.
Using authorization items, we can build up an authorization hierarchy. An item A is a parent of
another item B in the hierarchy if A consists of B (or say A inherits the permission(s) represented
by B). An item can have multiple child items, and it can also have multipe parent items. Therefore, an
authorization hierarchy is a partial-order graph rather than a tree. In this hierarchy, role items sit on
top levels, operation items on bottom levels, while task items in between.
Once we have an authorization hierarchy, we can assign roles in this hierarchy to application users.
A user, once assigned with a role, will have the permissions represented by the role. For example, if
we assign theadministrator role to a user, he will have the administrator permissions which
include post managementand user management (and the corresponding operations such
as create user).
Now the fun part starts. In a controller action, we want to check if the current user can delete the
specified post. Using the RBAC hierarchy and assignment, this can be done easily as follows:
if(Yii::app()->user->checkAccess('deletePost'))
{
// delete the post
}
return array(
'components'=>array(
'db'=>array(
'class'=>'CDbConnection',
'connectionString'=>'sqlite:path/to/file.db',
),
'authManager'=>array(
97
Ghid de utilizare Yii
'class'=>'CDbAuthManager',
'connectionID'=>'db',
),
),
);
CAuthManager::createRole
CAuthManager::createTask
CAuthManager::createOperation
Once we have a set of authorization items, we can call the following methods to establish
relationships between authorization items:
CAuthManager::addItemChild
CAuthManager::removeItemChild
CAuthItem::addChild
CAuthItem::removeChild
And finally, we call the following methods to assign role items to individual users:
CAuthManager::assign
CAuthManager::revoke
Below we show an example about building an authorization hierarchy with the provided APIs:
$auth=Yii::app()->authManager;
$auth->createOperation('createPost','create a post');
$auth->createOperation('readPost','read a post');
$auth->createOperation('updatePost','update a post');
$auth->createOperation('deletePost','delete a post');
$bizRule='return Yii::app()->user->id==$params["post"]->authID;';
$task=$auth->createTask('updateOwnPost','update a post by author himself',$bizRule);
98
Ghid de utilizare Yii
$task->addChild('updatePost');
$role=$auth->createRole('reader');
$role->addChild('readPost');
$role=$auth->createRole('author');
$role->addChild('reader');
$role->addChild('createPost');
$role->addChild('updateOwnPost');
$role=$auth->createRole('editor');
$role->addChild('reader');
$role->addChild('updatePost');
$role=$auth->createRole('admin');
$role->addChild('editor');
$role->addChild('author');
$role->addChild('deletePost');
$auth->assign('reader','readerA');
$auth->assign('author','authorB');
$auth->assign('editor','editorC');
$auth->assign('admin','adminD');
Note that we associate a business rule with the updateOwnPost task. In the business rule we simply
check if the current user ID is the same as the specified post's author ID. The post information in
the $params array is supplied by developers when performing access checking.
Info: While the above example looks long and tedious, it is mainly for demonstrative purpose.
Developers usually need to develop some user interfaces so that end users can use to establish an
authorization hierarchy more intuitively.
Access Checking
To perform access checking, we first need to know the name of the authorization item. For example,
to check if the current user can create a post, we would check if he has the permission represented
by the createPostoperation. We then call CWebUser::checkAccess to perform the access
checking:
if(Yii::app()->user->checkAccess('createPost'))
{
// create post
99
Ghid de utilizare Yii
If the authorization rule is associated with a business rule which requires additional parameters, we
can pass them as well. For example, to check if a user can update a post, we would do
$params=array('post'=>$post);
if(Yii::app()->user->checkAccess('updateOwnPost',$params))
{
// update post
}
return array(
'components'=>array(
'authManager'=>array(
'class'=>'CDbAuthManager',
'defaultRoles'=>array('authenticated', 'guest'),
),
),
);
Because a default role is assigned to every user, it usually needs to be associated with a business
rule that determines whether the role really applies to the user. For example, the following code
defines two roles, "authenticated" and "guest", which effectively apply to authenticated users and
guest users, respectively.
$bizRule='return !Yii::app()->user->isGuest;';
100
Ghid de utilizare Yii
$auth->createRole('authenticated',$bizRule);
$bizRule='return Yii::app()->user->isGuest;';
$auth->createRole('guest',$bizRule);
Theming
Theming is a systematic way of customizing the outlook of pages in a Web application. By applying
a new theme, the overall appearance of a Web application can be changed instantly and
dramatically.
In Yii, each theme is represented as a directory consisting of view files, layout files, and relevant
resource files such as images, CSS files, JavaScript files, etc. The name of a theme is its directory
name. All themes reside under the same directory WebRoot/themes. At any time, only one theme
can be active.
Tip: The default theme root directory WebRoot/themes can be configured to be a different one.
Simply configure the basePath and the baseUrl properties of the themeManager application
component to be the desired ones.
To activate a theme, set the theme property of the Web application to be the name of the desired
theme. This can be done either in the application configuration or during runtime in controller
actions.
Note: Theme name is case-sensitive. If you attempt to activate a theme that does not
exist,Yii::app()->theme will return null.
Contents under a theme directory should be organized in the same way as those under
the application base path. For example, all view files must be located under views, layout view files
under views/layouts, and system view files under views/system. For example, if we want to
replace the create view ofPostController with a view in the classic theme, we should save the
new view file asWebRoot/themes/classic/views/post/create.php.
For views belonging to controllers in a module, the corresponding themed view files should also be
placed under the views directory. For example, if the aforementioned PostController is in a
module named forum, we should save the create view file
as WebRoot/themes/classic/views/forum/post/create.php. If theforum module is nested in
another module named support, then the view file should
beWebRoot/themes/classic/views/support/forum/post/create.php.
Note: Because the views directory may contain security-sensitive data, it should be configured to
prevent from being accessed by Web users.
When we call render or renderPartial to display a view, the corresponding view file as well as the
layout file will be looked for in the currently active theme. And if found, those files will be rendered.
Otherwise, it falls back to the default location as specified by viewPath and layoutPath.
101
Ghid de utilizare Yii
Tip: Inside a theme view, we often need to link other theme resource files. For example, we may
want to show an image file under the theme's images directory. Using the baseUrl property of the
currently active theme, we can generate the URL for the image as follows,
Yii::app()->theme->baseUrl . '/images/FileName.gif'
WebRoot/
assets
protected/
.htaccess
components/
controllers/
models/
views/
layouts/
main.php
site/
index.php
themes/
basic/
views/
.htaccess
layouts/
main.php
site/
102
Ghid de utilizare Yii
index.php
fancy/
views/
.htaccess
layouts/
main.php
site/
index.php
return array(
'theme'=>'basic',
......
);
then the basic theme will be in effect, which means the application's layout will use the one under
the directorythemes/basic/views/layouts, and the site's index view will use the one
underthemes/basic/views/site. In case a view file is not found in the theme, it will fall back to the
one under theprotected/views directory.
Logging
Yii provides a flexible and extensible logging feature. Messages logged can be classified according
to log levels and message categories. Using level and category filters, selected messages can be
further routed to different destinations, such as files, emails, browser windows, etc.
1. Message Logging
Messages can be logged by calling either Yii::log or Yii::trace. The difference between these two
methods is that the latter logs a message only when the application is in debug mode.
103
Ghid de utilizare Yii
When logging a message, we need to specify its category and level. Category is a string in the
format ofxxx.yyy.zzz which resembles to the path alias. For example, if a message is logged
in CController, we may use the category system.web.CController. Message level should be one
of the following values:
trace: this is the level used by Yii::trace. It is for tracing the execution flow of the application
during development.
info: this is for logging general information.
profile: this is for performance profile which is to be described shortly.
warning: this is for warning messages.
error: this is for fatal error messages.
2. Message Routing
Messages logged using Yii::log or Yii::trace are kept in memory. We usually need to display them in
browser windows, or save them in some persistent storage such as files, emails. This is
called message routing, i.e., sending messages to different destinations.
In Yii, message routing is managed by a CLogRouter application component. It manages a set of the
so-calledlog routes. Each log route represents a single log destination. Messages sent along a log
route can be filtered according to their levels and categories.
To use message routing, we need to install and preload a CLogRouter application component. We
also need to configure its routes property with the log routes that we want. The following shows an
example of the neededapplication configuration:
array(
......
'preload'=>array('log'),
'components'=>array(
......
'log'=>array(
'class'=>'CLogRouter',
'routes'=>array(
array(
'class'=>'CFileLogRoute',
'levels'=>'trace, info',
'categories'=>'system.*',
),
array(
'class'=>'CEmailLogRoute',
'levels'=>'error, warning',
'emails'=>'admin@example.com',
),
),
),
104
Ghid de utilizare Yii
),
)
In the above example, we have two log routes. The first route is CFileLogRoute which saves
messages in a file under the application runtime directory. Only messages whose level
is trace or info and whose category starts with system. are saved. The second route
is CEmailLogRoute which sends messages to the specified email addresses. Only messages whose
level is error or warning are sent.
The following log routes are available in Yii:
Message Filtering
As we mentioned, messages can be filtered according to their levels and categories before they are
sent long a log route. This is done by setting the levels and categories properties of the
corresponding log route. Multiple levels or categories should be concatenated by commas.
Because message categories are in the format of xxx.yyy.zzz, we may treat them as a category
hierarchy. In particular, we say xxx is the parent of xxx.yyy which is the parent of xxx.yyy.zzz.
We can then use xxx.*to represent category xxx and all its child and grandchild categories.
105
Ghid de utilizare Yii
array(
......
'preload'=>array('log'),
'components'=>array(
......
'log'=>array(
'class'=>'CLogRouter',
'routes'=>array(
array(
'class'=>'CFileLogRoute',
'levels'=>'error',
'filter'=>'CLogFilter',
),
...other log routes...
),
),
),
)
Starting from version 1.0.7, Yii supports logging call stack information in the messages that are
logged by calling Yii::trace. This feature is disabled by default because it lowers performance. To
use this feature, simply define a constant named YII_TRACE_LEVEL at the beginning of the entry
script (before includingyii.php) to be an integer greater than 0. Yii will then append to every trace
message with the file name and line number of the call stacks belonging to application code. The
number YII_TRACE_LEVEL determines how many layers of each call stack should be recorded. This
information is particularly useful during development stage as it can help us identify the places that
trigger the trace messages.
3. Performance Profiling
Performance profiling is a special type of message logging. Performance profiling can be used to
measure the time needed for the specified code blocks and find out what the performance bottleneck
is.
To use performance profiling, we need to identify which code blocks need to be profiled. We mark
the beginning and the end of each code block by inserting the following methods:
Yii::beginProfile('blockID');
...code block being profiled...
Yii::endProfile('blockID');
106
Ghid de utilizare Yii
Note, code blocks need to be nested properly. That is, a code block cannot intersect with another. It
must be either at a parallel level or be completely enclosed by the other code block.
107
Ghid de utilizare Yii
// if post ID is invalid
throw new CHttpException(404,'The specified post cannot be found.');
2. Displaying Errors
When an error is forwarded to the CErrorHandler application component, it chooses an appropriate
view to display the error. If the error is meant to be displayed to end users, such as
a CHttpException, it will use a view named errorXXX, where XXX stands for the HTTP status code
(e.g. 400, 404, 500). If the error is an internal one and should only be displayed to developers, it will
use a view named exception. In the latter case, complete call stack as well as the error line
information will be displayed.
Info: When the application runs in production mode, all errors including those internal ones will be
displayed using view errorXXX. This is because the call stack of an error may contain sensitive
information. In this case, developers should rely on the error logs to determine what is the real cause
of an error.
CErrorHandler searches for the view file corresponding to a view in the following order:
1. WebRoot/themes/ThemeName/views/system: this is the system view directory under the
currently active theme.
2. WebRoot/protected/views/system: this is the default system view directory for an
application.
3. yii/framework/views: this is the standard system view directory provided by the Yii
framework.
Therefore, if we want to customize the error display, we can simply create error view files under the
system view directory of our application or theme. Each view file is a normal PHP script consisting of
mainly HTML code. For more details, please refer to the default view files under the
framework's view directory.
return array(
......
'components'=>array(
108
Ghid de utilizare Yii
'errorHandler'=>array(
'errorAction'=>'site/error',
),
),
);
In the action, we first retrieve the detailed error information from CErrorHandler::error. If it is not
empty, we render the error view together with the error information. The error information returned
fromCErrorHandler::error is an array with the following fields:
code: the HTTP status code (e.g. 403, 500);
type: the error type (e.g. CHttpException, PHP Error);
message: the error message;
file: the name of the PHP script file where the error occurs;
line: the line number of the code where the error occurs;
trace: the call stack of the error;
source: the context source code where the error occurs.
Tip: The reason we check if CErrorHandler::error is empty or not is because the error action may
be directly requested by an end user, in which case there is no error. Since we are passing
the$error array to the view, it will be automatically expanded to individual variables. As a result, in
the view we can access directly the variables such as $code, $type.
3. Message Logging
A message of level error will always be logged when an error occurs. If the error is caused by a
PHP warning or notice, the message will be logged with category php; if the error is caused by an
uncaught exception, the category would
be exception.ExceptionClassName (for CHttpException its statusCode will also be appended to
the category). One can thus exploit the logging feature to monitor errors happened during application
execution.
Web Service
109
Ghid de utilizare Yii
In the above, we declare the method getPrice to be a Web service API by marking it with the
tag @soap in its doc comment. We rely on doc comment to specify the data type of the input
parameters and return value. Additional APIs can be declared in the similar way.
2. Declaring Web Service Action
110
Ghid de utilizare Yii
Having defined the service provider, we need to make it available to clients. In particular, we want to
create a controller action to expose the service. This can be done easily by declaring
a CWebServiceAction action in a controller class. For our example, we will just put it
in StockController.
/**
* @param string the symbol of the stock
* @return float the stock price
* @soap
*/
public function getPrice($symbol)
{
//...return stock price for $symbol
}
}
That is all we need to create a Web service! If we try to access the action by
URLhttp://hostname/path/to/index.php?r=stock/quote, we will see a lot of XML content
which is actually the WSDL for the Web service we defined.
Tip: By default, CWebServiceAction assumes the current controller is the service provider. That is
why we define the getPrice method inside the StockController class.
$client=new SoapClient('http://hostname/path/to/index.php?r=stock/quote');
echo $client->getPrice('GOOGLE');
Run the above script in either Web or console mode, and we shall see 350 which is the price
for GOOGLE.
111
Ghid de utilizare Yii
4. Data Types
When declaring class methods and properties to be remotely accessible, we need to specify the
data types of the input and output parameters. The following primitive data types can be used:
112
Ghid de utilizare Yii
5. Class Mapping
In order to receive parameters of composite type from client, an application needs to declare the
mapping from WSDL types to the corresponding PHP classes. This is done by configuring
the classMap property ofCWebServiceAction.
113
Ghid de utilizare Yii
Internationalization
Internationalization (I18N) refers to the process of designing a software application so that it can be
adapted to various languages and regions without engineering changes. For Web applications, this
is of particular importance because the potential users may be from worldwide.
It provides the locale data for each possible language and variant.
It provides message and file translation service.
It provides locale-dependent date and time formatting.
It provides locale-dependent number formatting.
In the following subsections, we will elaborate each of the above aspects.
114
Ghid de utilizare Yii
The most needed I18N feature is perhaps translation, including message translation and view
translation. The former translates a text message to the desired language, while the latter translates
a whole file to the desired language.
A translation request consists of the object to be translated, the source language that the object is in,
and the target language that the object needs to be translated to. In Yii, the source language is
default to theapplication source language while the target language is default to the application
language. If the source and target languages are the same, translation will not occur.
Message Translation
Message translation is done by calling Yii::t(). The method translates the given message from source
languageto target language.
When translating a message, its category has to be specified since a message may be translated
differently under different categories (contexts). The category yii is reserved for messages used by
the Yii framework core code.
Messages can contain parameter placeholders which will be replaced with the actual parameter
values when calling Yii::t(). For example, the following message translation request would replace
the {alias} placeholder in the original message with the actual alias value.
Note: Messages to be translated must be constant strings. They should not contain variables that
would change message content (e.g. "Invalid {$message} content."). Use parameter
placeholders if a message needs to vary according to some parameters.
Translated messages are stored in a repository called message source. A message source is
represented as an instance of CMessageSource or its child class. When Yii::t() is invoked, it will look
for the message in the message source and return its translated version if it is found.
Yii comes with the following types of message sources. You may also extend CMessageSource to
create your own message source type.
CPhpMessageSource: the message translations are stored as key-value pairs in a PHP
array. The original message is the key and the translated message is the value. Each array
represents the translations for a particular category of messages and is stored in a separate
PHP script file whose name is the category name. The PHP translation files for the same
language are stored under the same directory named as the locale ID. And all these directories
are located under the directory specified by basePath.
CGettextMessageSource: the message translations are stored as GNU Gettext files.
CDbMessageSource: the message translations are stored in database tables. For more
details, see the API documentation for CDbMessageSource.
A message source is loaded as an application component. Yii pre-declares an application
component namedmessages to store messages that are used in user application. By default, the
115
Ghid de utilizare Yii
type of this message source isCPhpMessageSource and the base path for storing the PHP
translation files is protected/messages.
In summary, in order to use message translation, the following steps are needed:
Since version 1.0.2, Yii has added the support for choice format. Choice format refers to choosing a
translated according to a given number value. For example, in English the word 'book' may either
take a singular form or a plural form depending on the number of books, while in other languages,
the word may not have different form (such as Chinese) or may have more complex plural form rules
(such as Russian). Choice format solves this problem in a simple yet effective way.
To use choice format, a translated message must consist of a sequence of expression-message
pairs separated by |, as shown below:
'expr1#message1|expr2#message2|expr3#message3'
where exprN refers to a valid PHP expression which evaluates to a boolean value indicating whether
the corresponding message should be returned. Only the message corresponding to the first
expression that evaluates to true will be returned. An expression can contain a special variable
named n (note, it is not $n) which will take the number value passed as the first message parameter.
For example, assuming a translated message is:
and we are passing a number value 2 in the message parameter array when calling Yii::t(), we would
obtainmany books as the final translated message.
116
Ghid de utilizare Yii
File Translation
File translation is accomplished by calling CApplication::findLocalizedFile(). Given the path of a file
to be translated, the method will look for a file with the same name under the LocaleID subdirectory.
If found, the file path will be returned; otherwise, the original file path will be returned.
File translation is mainly used when rendering a view. When calling one of the render methods in a
controller or widget, the view files will be translated automatically. For example, if the target
language is zh_cn while thesource language is en_us, rendering a view named edit would resulting
in searching for the view fileprotected/views/ControllerID/zh_cn/edit.php. If the file is found,
this translated version will be used for rendering; otherwise, the
file protected/views/ControllerID/edit.php will be rendered instead.
File translation may also be used for other purposes, for example, displaying a translated image or
loading a locale-dependent data file.
117
Ghid de utilizare Yii
formatDecimal: this method formats the given number using the decimal pattern predefined
in the target locale data.
formatCurrency: this method formats the given number and currency code using the
currency pattern predefined in the target locale data.
formatPercentage: this method formats the given number using the percentage pattern
predefined in the target locale data.
Using Alternative Template Syntax
Yii allows developers to use their own favorite template syntax (e.g. Prado, Smarty) to write
controller or widget views. This is achieved by writing and installing a viewRenderer application
component. The view renderer intercepts the invocations ofCBaseController::renderFile, compiles
the view file with customized template syntax, and renders the compiling results.
Info: It is recommended to use customized template syntax only when writing views that are less
likely to be reused. Otherwise, people who are reusing the views would be forced to use the same
customized template syntax in their applications.
In the following, we introduce how to use CPradoViewRenderer, a view renderer that allows
developers to use the template syntax similar to that in Prado framework. For people who want to
develop their own view renderers, CPradoViewRenderer is a good reference.
1. Using CPradoViewRenderer
To use CPradoViewRenderer, we just need to configure the application as follows:
return array(
'components'=>array(
......,
'viewRenderer'=>array(
'class'=>'CPradoViewRenderer',
),
),
);
By default, CPradoViewRenderer will compile source view files and save the resulting PHP files
under theruntime directory. Only when the source view files are changed, will the PHP files be re-
generated. Therefore, using CPradoViewRenderer incurs very little performance degradation.
Tip: While CPradoViewRenderer mainly introduces some new template tags to make writing views
easier and faster, you can still write PHP code as usual in the source views.
In the following, we introduce the template tags that are supported by CPradoViewRenderer.
is translated into
Component Tags
Component tags are used to insert a widget in a view. It uses the following syntax:
where WidgetClass specifies the widget class name or class path alias, and property initial values
can be either quoted strings or PHP expressions enclosed within a pair of curly brackets. For
example,
would be translated as
Cache Tags
Cache tags are shortcuts to using fragment caching. Its syntax is as follows,
119
Ghid de utilizare Yii
</cache:fragmentID >
where fragmentID should be an identifier that uniquely identifies the content being cached, and the
property-value pairs are used to configure the fragment cache. For example,
<cache:profile duration={3600}>
// user profile information here
</cache:profile >
would be translated as
Clip Tags
Like cache tags, clip tags are shortcuts to
calling CBaseController::beginClip and CBaseController::endClip in a view. The syntax is as follows,
<clip:clipID>
// content for this clip
</clip:clipID >
where clipID is an identifier that uniquely identifies the clip content. The clip tags will be translated
as
Comment Tags
Comment tags are used to write view comments that should only be visible to developers. Comment
tags will be stripped off when the view is displayed to end users. The syntax for comment tags is as
follows,
<!---
view comments that will be stripped off
120
Ghid de utilizare Yii
--->
Console Applications
Console applications are mainly used by a Web application to perform offline work, such as code
generation, search index compiling, email sending, etc. Yii provides a framework for writing console
applications in an object-oriented and systematic way.
Yii represents each console task in terms of a command, and a console application instance is used
to dispatch a command line request to an appropriate command. The application instance is created
in an entry script. To execute a console task, we simply run the corresponding command on the
command line as follows,
defined('YII_DEBUG') or define('YII_DEBUG',true);
// include Yii bootstrap file
require_once('path/to/yii/framework/yii.php');
// create application instance and run
$configFile='path/to/config/file.php';
Yii::createConsoleApplication($configFile)->run();
We then create command classes which should extend from CConsoleCommand. Each command
class should be named as its command name appended with Command. For example, to define
an email command, we should write an EmailCommand class. All command class files should be
placed under the commandssubdirectory of the application base directory.
Tip: By configuring CConsoleApplication::commandMap, one can also have command classes in
different naming conventions and located in different directories.
121
Ghid de utilizare Yii
At any time in a command, we can access the console application instance via Yii::app(). Like a
Web application instance, console application can also be configured. For example, we can
configure a dbapplication component to access the database. The configuration is usually specified
as a PHP file and passed to the constructor of the console application class
(or createConsoleApplication in the entry script).
1. Using the yiic Tool
We have used the yiic tool to create our first application. The yiic tool is in fact implemented as a
console application whose entry script file is framework/yiic.php. Using yiic, we can accomplish
tasks such as creating a Web application skeleton, generating a controller class or model class,
generating code needed by CRUD operations, extracting messages to be translated, etc.
We can enhance yiic by adding our own customized commands. To do so, we should start with a
skeleton application created using yiic webapp command, as described in Creating First Yii
Application. The yiic webapp command will generate two files under
the protected directory: yiic and yiic.bat. They are thelocal version of the yiic tool created
specifically for the Web application.
We can then create our own commands under the protected/commands directory. Running the
local yiictool, we will see that our own commands appearing together with the standard ones. We
can also create our own commands to be used when yiic shell is used. To do so, just drop our
command class files under theprotected/commands/shell directory.
Security
1. Cross-site Scripting Prevention
Cross-site scripting (also known as XSS) occurs when a web application gathers malicious data from
a user. Often attackers will inject JavaScript, VBScript, ActiveX, HTML, or Flash into a vulnerable
application to fool other application users and gather data from them. For example, a poorly design
forum system may display user input in forum posts without any checking. An attacker can then
inject a piece of malicious JavaScript code into a post so that when other users read this post, the
JavaScript runs unexpectedly on their computers.
122
Ghid de utilizare Yii
One of the most important measures to prevent XSS attacks is to check user input before displaying
them. One can do HTML-encoding with the user input to achieve this goal. However, in some
situations, HTML-encoding may not be preferable because it disables all HTML tags.
Yii incorporates the work of HTMLPurifier and provides developers with a useful component
called CHtmlPurifierthat encapsulates HTMLPurifier. This component is capable of removing all
malicious code with a thoroughly audited, secure yet permissive whitelist and making sure the
filtered content is standard-compliant.
The CHtmlPurifier component can be used as either a widget or a filter. When used as a
widget, CHtmlPurifierwill purify contents displayed in its body in a view. For example,
return array(
'components'=>array(
'request'=>array(
'enableCsrfValidation'=>true,
),
),
);
123
Ghid de utilizare Yii
And to display a form, call CHtml::form instead of writing the HTML form tag directly.
The CHtml::form method will embed the necessary random value in a hidden field so that it can be
submitted for CSRF validation.
3. Cookie Attack Prevention
Protecting cookies from being attacked is of extreme importance, as session IDs are commonly
stored in cookies. If one gets hold of a session ID, he essentially owns all relevant session
information.
An application can use SSL to create a secure communication channel and only pass the
authentication cookie over an HTTPS connection. Attackers are thus unable to decipher the
contents in the transferred cookies.
Expire sessions appropriately, including all cookies and session tokens, to reduce the
likelihood of being attacked.
Prevent cross-site scripting which causes arbitrary code to run in a user's browser and
expose his cookies.
Validate cookie data and detect if they are altered.
Yii implements a cookie validation scheme that prevents cookies from being modified. In particular, it
does HMAC check for the cookie values if cookie validation is enable.
return array(
'components'=>array(
'request'=>array(
'enableCookieValidation'=>true,
),
),
);
To make use of the cookie validation scheme provided by Yii, we also need to access cookies
through the cookies collection, instead of directly through $_COOKIES:
124
Ghid de utilizare Yii
Yii::app()->request->cookies[$name]=$cookie;
Performance Tuning
Performance of Web applications is affected by many factors. Database access, file system
operations, network bandwidth are all potential affecting factors. Yii has tried in every aspect to
reduce the performance impact caused by the framework. But still, there are many places in the user
application that can be improved to boost performance.
If the application is using Active Record, we should turn on the schema caching to save the time of
parsing database schema. This can be done by configuring
the CDbConnection::schemaCachingDuration property to be a value greater than 0.
Besides these application-level caching techniques, we can also use server-level caching solutions
to boost the application performance. As a matter of fact, the APC caching we described earlier
belongs to this category. There are other server techniques, such as Zend
Optimizer, eAccelerator, Squid, to name a few.
5. Database Optimization
Fetching data from database is often the main performance bottleneck in a Web application.
Although using caching may alleviate the performance hit, it does not fully solve the problem. When
the database contains enormous data and the cached data is invalid, fetching the latest data could
be prohibitively expensive without proper database and query design.
Design index wisely in a database. Indexing can make SELECT queries much faster, but it may slow
downINSERT, UPDATE or DELETE queries.
For complex queries, it is recommended to create a database view for it instead of issuing the
queries inside the PHP code and asking DBMS to parse them repetitively.
Do not overuse Active Record. Although Active Record is good at modelling data in an OOP fashion,
it actually degrades performance due to the fact that it needs to create one or several objects to
represent each row of query result. For data intensive applications, using DAO or database APIs at
lower level could be a better choice.
Last but not least, use LIMIT in your SELECT queries. This avoids fetching overwhelming data from
database and exhausting the memory allocated to PHP.
6. Minimizing Script Files
Complex pages often need to include many external JavaScript and CSS files. Because each file
would cause one extra round trip to the server and back, we should minimize the number of script
files by merging them into fewer ones. We should also consider reducing the size of each script file
to reduce the network transmission time. There are many tools around to help on these two aspects.
For a page generated by Yii, chances are that some script files are rendered by components that we
do not want to modify (e.g. Yii core components, third-party components). In order to minimizing
these script files, we need two steps.
Note: The scriptMap feature described in the following has been available since version 1.0.3.
First, we declare the scripts to be minimized by configuring the scriptMap property of
the clientScript application component. This can be done either in the application configuration or in
code. For example,
$cs=Yii::app()->clientScript;
$cs->scriptMap=array(
126
Ghid de utilizare Yii
'jquery.js'=>'/js/all.js',
'jquery.ajaxqueue.js'=>'/js/all.js',
'jquery.metadata.js'=>'/js/all.js',
......
);
What the above code does is that it maps those JavaScript files to the URL /js/all.js. If any of
these JavaScript files need to be included by some components, Yii will include the URL (once)
instead of the individual script files.
Second, we need to use some tools to merge (and perhaps compress) the JavaScript files into a
single one and save it as js/all.js.
The same trick also applies to CSS files.
We can also improve page loading speed with the help of Google AJAX Libraries API. For example,
we can include jquery.js from Google servers instead of our own server. To do so, we first
configure the scriptMapas follows,
$cs=Yii::app()->clientScript;
$cs->scriptMap=array(
'jquery.js'=>false,
'jquery.ajaxqueue.js'=>false,
'jquery.metadata.js'=>false,
......
);
By mapping these script files to false, we prevent Yii from generating the code to include these files.
Instead, we write the following code in our pages to explicitly include the script files from Google,
<head>
<?php echo CGoogleApi::init(); ?>
127
Ghid de utilizare Yii
128