Sunteți pe pagina 1din 128

Ghid de utilizare Yii

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.

2. La ce foloseste Yii cel mai mult? 


Yii este o platforma de programare Web generica ce poate fi folosita pentru dezvoltarea unei game
foarte largi de aplicatii. Deoarece este foarte mica, si echipata cu solutii de caching sofisticate, este
folositoare in special pentru dezvoltarea aplicatiilor cu trafic foarte mare: portaluri, forumuri, sisteme
de genstiune continut (CMS), sisteme e-commerce, etc.

3. Cum se compara Yii cu alte platforme PHP? 


Ca cele mai multe platforme PHP, Yii este o platforma MVC.

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. Descarcam platforma Yii de pe site-ul yiiframework.com.


2. Dezarhivam fisierul editiei descarcate de Yii intr-un director accesibil pe Web.
Sfat: Platforma Yii nu trebuie neaparat instalata intr-un director accesibil de pe Web. O aplicatie Yii
are un fisier de intrare care este de obicei singurul fisier care trebuie sa fie expus utilizatorilor Web.
Celelalte scripturi PHP, inclusiv cele ale platformei Yii, ar trebui sa fie protejate fata de accesul din
Web din moment ce ar putea fi accesate si modificate de catre persoane neautorizate.

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.

Crearea primei aplicatii Yii


Pentru a intra in contact prima data cu Yii, descriem in aceasta sectiune cum sa cream prima
noastra aplicatie Yii. Vom folosi unealta (foarte puternica) yiic pe care o vom folosi pentru a crea
automat cod pentru anumite taskuri. Pentru convenienta, presupunem ca YiiRoot este directorul in
care a fost instalata platforma Yii, iar WebRoot este documentul radacina al serverului nostru Web.
Rulam yiic in linia de comanda in felul urmator:

% YiiRoot/framework/yiic webapp WebRoot/myproject

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

% php YiiRoot/framework/yiic.php webapp 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

Pagina contact cu afisare erori la intrarea datelor

4
Ghid de utilizare Yii

Pagina contact cu afisare succes

5
Ghid de utilizare Yii

Pagina de logare

Urmatoarea diagrama ne arata structura de directoare a aplicatiei. Trebuie vazuta


sectiunea Conventii pentru explicatii detaliate despre aceasta structura.

myproject/

index.php fisierul php de intrare in aplicatia Web

assets/ contine fisiere cu resurse accesibile din Web

css/ contine fisiere CSS

images/ contine imagini

themes/ contine teme

protected/ contine fisierele protejate ale aplicatiei

yiic scriptul yiic de linie de comanda

yiic.bat scriptul yiic de linie de comanda pt Windows

commands/ contine comenzi customizate pt 'yiic'

shell/ contine comenzi customizate pt 'yiic shell'

components/ contine componente utilizator reutilizabile

6
Ghid de utilizare Yii

MainMenu.php clasa widget-ului 'MainMenu'

Identity.php clasa 'Identity' folosita pentru autentificare

views/ contine fisiere view pentru widget-uri

mainMenu.php fisierul view pentru widget-ul 'MainMenu'

config/ contine fisiere de configurare

console.php configuratia aplicatiei consola

main.php configuratia aplicatiei Web

controllers/ contine fisierele cu clasele controller-elor

SiteController.php clasa controller-ului implicit

extensions/ contine extensii third-party

messages/ contine mesaje traduse

models/ contine fisiere cu clasele modelelor

LoginForm.php modelul de tip formular pentru action-ul 'login'

ContactForm.php modelul de tip formular pentru action-ul 'contact'

runtime/ contine fisiere generate temporar

views/ contine fisiere layout si view-urile controller-elor

layouts/ contine fisiere layout pt view-uri

main.php layout-ul implicit pt toate view-urile

site/ contine fisierele view pentru controller-ul 'site'

contact.php view-ul pentru action-ul 'contact'

index.php view-ul pentru action-ul 'index'

login.php view-ul pentru action-ul 'login'

system/ contine fisierele view pt erorile sistemului

7
Ghid de utilizare Yii

1. Conectarea la baza de date 


Majoritatea aplicatiilor Web folosesc baze de date, iar aplicatia noastra nu este o exceptie. Pentru a
folosi o baza de date, trebuie sa spunem aplicatiei cum sa se conecteze la ea. Putem face acest
lucru prin modificarea fisierului de configurare al
aplicatiei WebRoot/myproject/protected/config/main.php, in felul urmator:

return array(
......
'components'=>array(
......
'db'=>array(
'connectionString'=>'sqlite:protected/data/source.db',
),
),
......
);

In codul de mai sus, adaugam o intrare db la components. Acest lucru spune aplicatiei sa se


conecteze la baza de date SQLite WebRoot/myproject/protected/data/source.db atunci cand
este necesar.
Nota: Pentru a folosi o baza de date in Yii, trebuie sa activam extensia PHP PDO si extensia PDO
cu driver-ul specific pentru baza de date pe care vrem sa o folosim. Pentru aplicatia noastra, trebuie
ca extensiile php_pdo si php_pdo_sqlite sa fie activate.

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:

CREATE TABLE User (


id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
username VARCHAR(128) NOT NULL,
password VARCHAR(128) NOT NULL,
email VARCHAR(128) NOT NULL
);

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

Yii Interactive Tool v1.0

Please type 'help' for help. Type 'exit' to quit.

>> model User

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

>> crud User

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

In cele de mai sus, folosim comanda yiic shell pentru a interactiona cu aplicatia noastra.


Executam doua sub-comenzi: model User si crud User. model User genereaza clasa modelului
tabelei User. crud Userciteste modelul User si genereaza codul necesar pentru operatiile CRUD.
Nota: Putem intalni erori de genul "...could not find driver", chiar daca verificarea cerintelor necesare
a avut succes si arata ca avem activate extensia PDO si driverul PDO corespunzator pentru baza
noastra de date. Daca se intampla acest lucru, putem incerca sa rulam unealta yiic in felul urmator:

% php -c path/to/php.ini protected/yiic.php shell

unde path/to/php.ini reprezinta fisierul ini corect al PHP.

Acum ne putem bucura de rezultate folosind urmatorul URL:

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.

Daca ne logam ca administrator folosind admin/admin, putem vedea pagina utilizatorului admin in


URL-ul acesta:
10
Ghid de utilizare Yii

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!

Pagina utilizatorului admin

Pagina creare utilizator nou

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.

urmatoarea diagrama arata structura statica a unei aplicatii Yii:

Structura statica a aplicatiei Yii

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:

Fluxul tipic al aplicatiei Yii

13
Ghid de utilizare Yii

1. Un utilizator face o cerere prin URL-ul http://www.example.com/index.php?


r=post/show&id=1, iar serverul Web trateaza cererea prin executarea fisierul
bootstrap index.php.
2. Fisierul index.php creaza o instanta application si o ruleaza.
3. Aplicatia obtine informatii detaliate despre cererea utilizatorului de la o the detailed user
request information from componenta a aplicatiei cu numele request.
4. Aplicatia determina controller-ul si action-ul cu ajutorul componentei urlManager. In acest
exemplu, controller-ul este post si se refera la clasa PostController; action-ul este show, iar
semnificatia numelui este determinata de controller-ul in cauza.
5. Aplicatia creaza o instanta a controller-ului necesar pentru a trata mai departe cererea.
Controller-ul intelege ca show se refera la metoda cu numele actionShow din clasa controller-
ului. Apoi, aplicatia creaza si executa filtrele (ex. controlul accesului, benchmarking, etc)
asociate cu aceast action. Action-ul este executat daca este permis de catre filtre.
6. Action-ul citeste din baza de date un model Post al carui ID este 1.
7. Action-ul genereaza un view cu numele show si cu modelul Post.
8. View-ul citeste si afiseaza atributele modelului Post.
14
Ghid de utilizare Yii

9. View-ul executa cateva widget-uri.


10. Rezultatul generat de view este inclus intr-un layout.
11. Action-ul termina generarea view-ului si afiseaza rezultatul utilizatorului.

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',
)

De obicei retinem configuratia intr-un fisier PHP separat (ex. protected/config/main.php). Aici,


se returneaza array-ul de configurare dupa cum urmeaza:

return array(...);

Ca sa aplicam configuratia, transmitem numele fisierului PHP ca parametru al constructorului clasei


application, sau ca parametru al Yii::createWebApplication() in felul urmator (asa se face de obicei
in fisierul de intrare ):

$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.

2. Application Base Directory 


Application base directory se refera la directorul radacina care contine toate fisierele PHP care
trebuie ascunse fata de clienti. Implicit, acest director este denumit protected si se afla in acelasi
director cu fisierul php accesibil clientilor. Totusi, poate fi schimbat acest director prin
proprietatea basePath din configuratia aplicatiei.
Tot ce este in acest director special ar trebui protejat fata de orice client WEB. Cu Apache HTTP
serverprotectia se face foarte simplu printr-un fisier .htaccess pus in acest director. Continutul
fisierului .htaccesseste:

deny from all

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),
),
),
),
)

Adaugam elementul cache la array-ul components. Elementul cache retine clasa folosita de


componenta, clasa fiind CMemCache, iar proprietarea servers ar trebui initializata in acest fel.
ca sa accesam o componenta, folosim Yii::app()->ComponentID, unde ComponentID se refera la
ID-ul componentei (ex. Yii::app()->cache).
O componenta poate fi dezactivata atribuind lui enabled valoarea false. Daca incercam sa accesam
o componenta dezactivata, atunci primim null.
Sfat: Implicit, componentele sunt create la cerere. Ca rezultat, componenta nu va fi creata daca nu
este accesata in timpul unei cereri client. Ca rezultat, performanta per ansamblu nu va scadea, chiar
daca aplicatia are o configuratie cu foarte multe componente. Unele componente (ex. CLogRouter)
poate ar trebui totusi sa fie create indiferent daca sunt accesate sau nu. Daca se doreste aces lucru,
atunci ID-urile lor trebuie mentionate in lista memorata in proprietatea preload.

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

 assetManager: CAssetManager - gestioneaza publicarea fisierelor private de tip asset


(implicit acestea exista in directorul Asset).
 authManager: CAuthManager - gestioneaza role-based access control (RBAC - control
acces bazat pe roluri).
 cache: CCache - asigura cache de date. Este de retinut ca trebuie specificata clasa care se
va ocupa cu acest lucru (ex. CMemCache, CDbCache). Altfel, atunci cand se va accesa
componenta se va primi null.
 clientScript: CClientScript - gestioneaza scripturile client (javascript si CSS).
 coreMessages: CPhpMessageSource - gestioneaza mesajele nucleu traduse folosite de
platforma Yii.
 db: CDbConnection - asigura conexiunea la baza de date. Este de retinut ca trebuie sa
configuram proprietatea connectionString pentru a putea folosi aceasta componenta.
 errorHandler: CErrorHandler - trateaza exceptii si erori PHP.
 messages: CPhpMessageSource - gestioneaza mesaje traduse folosite de Yii.
 request: CHttpRequest - furnizeaza informatii despre cererile client.
 securityManager: CSecurityManager - asigura servicii de securitate, precum hashing si
encryption.
 session: CHttpSession - functionalitati la nivel de sesiune.
 statePersister: CStatePersister - ofera o zona de date persistenta la nivel global al aplicatiei
intre cererile client.
 urlManager: CUrlManager - creare si analizare URL.
 user: CWebUser - reprezinta informatiile despre identitatea utilizatorului curent.
 themeManager: CThemeManager - gestiune teme.
5. Ciclul de viata al aplicatiei 
Atunci cand se trateaza o cerere client, aplicatia va trece prin urmatoarele stadii:

1. Seteaza tratarea de erori si autoloader-ul de clase;


2. Inregistreaza componentele nucleu ale aplicatiei;
3. Incarca configuratia aplicatiei;
4. Initializeaza aplicatia cu CApplication::init()
 Incarca componentele statice ale aplicatiei;
5. Activeaza evenimentul onBeginRequest;
6. Proceseaza cererea client:
 Analizeaza cererea client;
 Creaza controller-ul necesar;
 Ruleaza controller-ul;
7. Activeaza evenimentul onEndRequest;

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.

class SiteController extends CController


{
}

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

Cand CWebApplication analizeaza o cerere de la client, atunci este creata o instanta a unui


controller. Se primeste, prin ruta, ID-ul controller-ului, iar aplicatia va folosi urmatoarele reguli pentru
a determina ce clasa controller este si unde este localizat fisierul fizic al clasei.
 Daca este specificat CWebApplication::catchAllRequest, va fi creat un controller pe baza
acestei proprietati, iar ID-ul de controller primit de la client va fi ignorat. Aceasta este situatia
cand dorim sa aducem aplicatia in modul de mentenanta, offline sau invizibila in Web in spatele
unei pagini statice.
 Daca ID-ul este gasit in CWebApplication::controllerMap, configuratia controller-ului care
este precizata acolo va fi folosita pentru a crea instanta controller-ului.
 Daca ID-ul este in formatul 'cale/catre/xyz', numele clasei controller-ului se
presupune ca esteXyzControllersi fisierul fizic al clasei
esteprotected/controllers/cale/catre/XyzController.php. De exemplu, un ID de
controlleradmin.userar conduce la clasaUserControllersi la fisierul
fizicprotected/controllers/admin/UserController.php`. Daca fisierul fizic al clasei nu exista,
atunci se genereaza CHttpException 404.
In cazul in care modulele sunt folosite (posibil incepand cu versiunea 1.0.3), procesul de mai sus
este un pic diferit. In particular, aplicatia va verifica daca ID-ul se refera la un controller din interiorul
unui modul, si daca da, atunci va fi creata intai instanta modulului respectiv, iar apoi va fi creata
instanta controller-ului.
3. Action 
Dupa cum am mentionat anterior, un action poate fi definit ca metoda al carei nume incepe cu
cuvantulaction. O modalitate mai avansata de a defini o clasa action este prin a cere controller-ului
sa instantieze clasa action atunci cand este ceruta. Aceasta permite reutilizarea usoara in alte
proiecte a clasei action, clasa action fiind independenta in felul acesta de aplicatia curenta.
Pentru a defini o noua clasa action, facem urmatoarele:

class UpdateAction extends CAction


{
public function run()
{
// aici intra logica action
}
}

Pentru ca acest action sa fie vizibil de catre controller, suprascriem metoda actions() din clasa
controller-ului:

class PostController extends CController


{
public function actions()

20
Ghid de utilizare Yii

{
return array(
'edit'=>'application.controllers.post.UpdateAction',
);
}
}

Mai sus, am folosit alias-ul application.controllers.post.UpdateAction pentru a specifica


faptul ca fisierul fizic al clasei action este protected/controllers/post/UpdateAction.php.
Daca concepem action-urile fiind clase, putem sa organizam aplicatia modular. De exemplu,
urmatoarea structura de directoare poate fi folosita pentru a organiza codul pentru controllere:

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:

public function filterAccessControl($filterChain)


{
...
// se apeleaza $filterChain->run() pentru a continua filtrarea si executia
action-ului
}

$filterChain este o instanta a clasei CFilterChain si reprezinta o lista de filtre asociate cu action-ul


cerut. In interiorul filtrului, putem apela $filterChain->run() pentru a continua filtrarea si executia
action-ului.
Dar un filtru poate sa fie si o instanta a clasei CFilter sau a unei clase derivate. Urmatorul cod
defineste o clasa noua de filtru:

class PerformanceFilter extends CFilter


{
protected function preFilter($filterChain)
{
// cod executat inainte de executia action-ului
return true; // daca action-ul nu trebuie executat trebuie returnat false
}

protected function postFilter($filterChain)


{
// cod de executat dupa ce action-ul este executat
}
}

Pentru a aplica filtre action-urilor, trebuie sa suprascriem metoda CController::filters().


Metoda ar trebui sa returneze un array cu configuratia filtrului. De exemplu:

class PostController extends CController


{

22
Ghid de utilizare Yii

......
public function filters()
{
return array(
'postOnly + edit, create',
array(
'application.filters.PerformanceFilter - edit, create',
'unit'=>'microsecunde',
),
);
}
}

Codul de mai sus specifica doua filtre: postOnly si PerformanceFilter. Filtrul postOnly este


definit ca metoda (metoda filtru corspunzatoare este definita deja in CController);
filtrul PerformanceFilter este definit printr-o clasa.
Aliasul application.filters.PerformanceFilter ne spune calea unde gasim fisierul fizic al
clasei: protected/filters/PerformanceFilter. Filtrul PerformanceFilter are nevoie de un
array pentru a isi initializa valorile proprietatilor. Aici, proprietatea unit din
filtrul PerformanceFilter va fi initializata cu'microsecunde'.
Folosind operatorii plus si minus, putem specifica la ce action-uri ar trebui aplicat (sau nu) un filtru.
In codul de mai sus, filtrul postOnly va fi aplicat action-urilor edit si create, in timp ce
filtrul PerformanceFilter ar trebui aplicat la toate action-urile CU EXCEPTIA action-
urilor edit si create. Daca nu apare nici plus nici minus in configuratia filtrului, atunci filtrul va fi
aplicat tuturor action-urilor.

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:

......aici se defineste header-ul......


<?php echo $content; ?>
......aici se defineste footer-ul......

24
Ghid de utilizare Yii

$content contine rezultatul generat pentru un view.


Layout este implicit aplicat atunci cand se apeleaza render(). Implicit, fisierul
viewprotected/views/layouts/main.php este folosit ca layout. Poate fi schimbat prin modificarea
ori aCWebApplication::layout ori a CController::layout. Pentru a genera un view fara sa ii aplicam un
layout, folosimrenderPartial().
2. Widget 
Un widget este o instanta a clasei CWidget sau a unei clase derivate. Este o componenta creata in
special pentru scopuri de prezentare. Widget-urile sunt incluse de obicei intr-un fisier view pentru a
genera unele interfete utilizator complexe, dar de sine statatoare. De exemplu, widget-ul calendar
poate fi folosit pentru a genera un calendar complex. Widget-urile ajuta la o mai buna separare si
reutilizare a codului din interfata utilizator.
Pentru a folosi un widget intr-un fisier view, facem urmatoarele:

<?php $this->beginWidget('path.to.WidgetClass'); ?>


...continut body care poate fi capturat de catre widget...
<?php $this->endWidget(); ?>

sau

<?php $this->widget('cale.catre.WidgetClass'); ?>

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.

Pentru a defini un nou widget, derivam CWidget si suprascriem metodele init() si run():

25
Ghid de utilizare Yii

class MyWidget extends CWidget


{
public function init()
{
// aceasta metoda este apelata de catre CController::beginWidget()
}

public function run()


{
// aceasta metoda este apelata de catre CController::endWidget()
}
}

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

$width=$component->textWidth; // get the textWidth property


$component->enableCaching=true; // set the enableCaching property

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:

public function getTextWidth()


{
return $this->_textWidth;
}

public function setTextWidth($value)


{
$this->_textWidth=$value;
}

Codul de mai sus defineste o proprietate textWidth (numele este case-insensitive). Cand se citeste


proprietatea, getTextWidth() este invocata si valoarea returnata devine valoarea proprietatii. In
mod similar, cand se atribuie o valoare proprietatii, setTextWidth() este invocata. Daca metoda
setter nu este definita, atunci proprietatea va fi read-only, iar incercarea de a o scrie va avea ca
efect activarea unei exceptii. Folosind metodele getter si setter pentru a defini o proprietate avem
avantajul ca putem executa logica aditionala atunci cand se citeste sau se scrie o proprietate (ex.
cand se face validare de date, cand se activeaza evenimente).
Nota: Este o mica diferenta intre o proprietate definita prin metodele getter si setter si o variabila a
unei clase. Numele proprietatii este case-insensitive, in timp ce numele variabilei este case-
sensitive.
2. Evenimentele unei componente 
Evenimentele sunt proprietati speciale ale caror valori pot fi nume de metode (denumite  event
handlers, metode care trateaza evenimente). Atasarea/Atribuirea unei metoda la un eveniment va
avea ca efect apelarea metodei ori de cate ori si oriunde evenimentul este activat. De aceea,
comportamentul unei componente poate fi modificat intr-un fel neanticipat in perioada de dezvoltare
a componentei.
Un eveniment al unei componente este definit printr-o metoda al carei nume incepe cu on. Ca si
proprietatile definite cu metode getter/setter, numele evenimentelor sunt case-insensitive. Codul
urmator defineste un eveniment onClicked:

public function onClicked($event)


{

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)
{
......
}

$event este parametrul care descrie evenimentul (provine din apelul raiseEvent()).


Parametrul $event este o instanta a clasei CEvent sau a unei clase derivate. Trebuie sa contina cel
putin informatiile despre cine a activat evenimentul.
Daca apelam onClicked() in acest moment, evenimentul onClicked va fi activat (in interiorul
luionClicked()), iar event handler-ul atasat va fi invocat automat.
Un eveniment poate fi atasat mai multor handlere. Cand un eveniment este activat, handler-ele vor fi
invocate in ordinea in care au fost atasate evenimentului. Daca un handler decide sa intrerupa
invocarea hadler-elor urmatoare, poate sa seteze $event->handled cu valoarea true.
3. Behaviour 
Incepand cu versiunea 1.0.2, o componenta are adaugat suport pentru mixin si ii poate fi atasata un
behavior (=comportament), sau mai multe behaviours. Un behaviour este un obiect ale carui metode
pot fi 'mostenite' de catre componenta atasata in scopul de a castiga functionalitate noua in loc de
specializare (mostenirea normala de clase). O componenta poate fi atasata impreuna cu mai multe
behaviours, obtinand astfel 'mostenire multipla'.
Clasele behavior trebuie sa implementeze interfata [IBehavior]. Cele mai multe behaviours extind
clasa de bazaCBehavior. Daca o clasa behaviour are nevoie sa fie atasata unui model, poate
extinde si clasaCModelBehavior sau CActiveRecordBehavior care implementeaza features specifice
modelelor.
Pentru a folosi behavior, trebuie sa fie atasat unei componente inainte de toate apeland metoda
clasei behaviour [attach()|IBehavior::attach]. Dupa aceea putem apela o metoda behavior prin
componenta:

28
Ghid de utilizare Yii

// $name identifica in mod unic behavior al unei componente


$behavior->attach($name,$component);
// test() este o metoda a $behavior
$component->test();

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

// dar acum va functiona corect


$component->enableBehavior($name);
$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/

ForumModule.php fisierul clasei modulului

components/ contine componente utilizator reutilizabile

views/ contine fisierele view ale widget-urilor

controllers/ contine fisierele claselor controller-elor

DefaultController.php fisierul clasei controller-ului implicit

extensions/ contine extensii third-party

models/ contine fisierele cu clasele modelelor

views/ contine fisierele de layout si view-urile controller-


elor

layouts/ contine fisierele layout pt view-uri

default/ contine fisierele view pentru DefaultController

index.php view-ul index

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

Yii Interactive Tool v1.0

Please type 'help' for help. Type 'exit' to quit.

>> module forum

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;

Un action de controller din interiorul unui modul poate fi accesat


prin rutamoduleID/controllerID/actionID. De exemplu, presupunand ca modulul forum de mai
sus are un controller cu numele PostController, putem folosi ruta forum/post/create pentru a
ne referi la action-ulcreate din acest controller. URL-ul corespunzator pentru aceasta ruta va
fihttp://www.example.com/index.php?r=forum/post/create.
Sfat: Daca un controller este intr-un subdirector din controllers, inca putem folosi formatul ruteide
mai sus. De exemplu, presupunand ca PostController este sub forum/controllers/admin,
putem sa apelam action-ul create folosind ruta forum/admin/post/create.
3. Module imbricate 
Modulele pot fi imbricate. Adica, un modul poate contine alt modul. Le puntem denumi modulul
parinte simodulul copil. Modulele copil trebuie sa fie pus in directorul modules al modulului parinte.
Pentru a accesa un action al unui controller dintr-un modul copil, trebuie sa folosim
rutaparentModuleID/childModuleID/controllerID/actionID.
Path Alias si Namespace
Yii foloseste foarte mult path alias-uri (scurtaturi de cale). Un path alias este asociat unui director
sau unui fisier fizic. Alias-ul este specificat in sintaxa cu puncte, la fel cu formatul namespace
adoptat in lume:

RootAlias.cale.catre.destinatie

RootAlias este alias-ul unui director existent. Prin apelarea YiiBase::setPathOfAlias(), putem defini


noi alias-uri de cale. Pentru convenienta, Yii predefineste urmatoarele alias-uri radacina:
 system: se refera la directorul platformei Yii;
 application: se refera la directorul de baza al aplicatiei;
 webroot: se refera la directorul care contine fisierul cu scriptul de intrare. Acest alias este
disponibil incepand cu versiunea 1.0.3.
In plus, daca aplicatia foloseste module, un alias radacina este de asemenea predefinit pentru
fiecare ID de modul si se refera la calea modulului respectiv. Acest feature este disponibil incepand
cu versiunea 1.0.3.
Folosind YiiBase::getPathOfAlias(), un alias poate fi tradus in path-ul corespunzator. De
exemplu,system.web.CController ar fi tradus in yii/framework/web/CController.
Folosind alias-uri, este foarte convenabil sa importam definitiile unei clase. De exemplu, daca vrem
sa includem definitia clasei CController, putem face in felul urmator:

32
Ghid de utilizare Yii

Yii::import('system.web.CController');

Metoda import este diferita de functiile PHP include si require in sensul ca este mult mai eficienta.


Definitia clasei nu este de fapt inclusa decat din momentul in care este folosita prima data.
Importarea unor namespace-uri de mai multe ori este de asemenea mult mai rapida decat functiile
PHP include_once si require_once.
Sfat: Cand facem referirea la o clasa definita de platforma Yii, nu trebuie sa o importam sau sa o
includem. Toate clasele nucleu ale Yii sunt importate deja.

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.

 WebRoot/protected: acesta este application base directory. Contine toate fisierele PHP si


fisierele de date care trebuie securizate fata de exterior. Poate fi schimbat
prin CWebApplication::basePath. Yii contine un alias implicit avand numele application care
este asociat cu aceasta cale. Accesul la acest director, inclusiv orice subdirector, ar trebui sa fie
interzis tuturor utilizatorilor Web.
 WebRoot/protected/runtime: acest director contine toate fisierele private temporare
generate in timpul rularii aplicatiei. Acest director poate fi schimbat
prin CApplication::runtimePath. Acest director trebuie sa dea drepturi de scriere procesului
serverului Web.
 WebRoot/protected/extensions: acest director contine toate extensiile third-party. Acest
director poate fi schimbat prin CApplication::extensionPath.
 WebRoot/protected/modules: acest director contine toate modulele aplicatiei, fiecare
reprezentand un subdirector.
 WebRoot/protected/controllers: acest director contine toate fisierele cu clasele
controller-elor. Acest director poate fi schimbat prin CWebApplication::controllerPath.
 WebRoot/protected/views: acest director contine toate fisierele view, inclusiv view-urile
controller-elor, view-urile layout si view-urile sistem. Acest director poate fi schimbat
prin CWebApplication::viewPath.
 WebRoot/protected/views/ControllerID: acest director contine fisierele view pentru un
singur controller (identificat prin ID-ul sau, ControllerID). Acest director poate fi schimbat
prinCController::getViewPath.
 WebRoot/protected/views/layouts: acest director contine toate fisierele view care contin
layout-uri. Acest director poate fi schimbat prin CWebApplication::layoutPath.

35
Ghid de utilizare Yii

 WebRoot/protected/views/system: acest director contine toate fisierele view ale


sistemului. View-urile sistemului sunt template-uri folosite pentru afisarea exceptiilor si erorilor.
Acest director poate fi schimbat prin CWebApplication::systemViewPath.
 WebRoot/assets: acest director contine fisiere asset publicate. Un fisier asset este un fisier
privat care poate fi publicat pentru a deveni accesibil utilizatorilor Web. Acest director trebuie sa
dea drept de scriere procesului serverului Web. Acest director poate fi schimbat
prin CAssetManager::basePath.
 WebRoot/themes: acest director contine diverse teme in care poate fi prezentata aplicatia
Yii. Fiecare subdirector reprezinta o singura tema al carui nume este numele subdirectorului
respectiv. Acest director poate fi schimbat prin CThemeManager::basePath.
Fluxul de dezvoltare
In acest moment avem definite conceptele fundamentale ale Yii. Acum putem arata fluxul obisnuit de
dezvoltare atunci cand se creaza o aplicatie web cu ajutorul Yii. Fluxul presupune ca am facut
analiza cerintelor aplicatiei si designul ei.

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.

De obicei, sunt necesari urmatorii pasi cand ne confruntam cu formulare in Yii:

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.

1. Definirea clasei modelului 


Mai jos, cream un model LoginForm folosit pentru a colecta input-urile de la utilizator dintr-o pagina
de logare. Pentru ca informatiile despre logare sunt folosite doar pentru a autentifica utilizatorul, nu
trebuie sa le salvam in baza de date. Si deci vom crea LoginForm ca fiind un model de formular.

class LoginForm extends CFormModel


{
public $username;
public $password;
public $rememberMe=false;
}

Am declarat trei atribute in LoginForm: $username, $password si $rememberMe. Sunt folosite pentru


a pastra username-ul si parola introduse de catre utilizator (plus optiunea remember me). Pentru
optiunea$rememberMe, valoarea implicita este false, care inseamna ca optiunea va fi initial afisata
ne-bifata.

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.

2. Declarare reguli de validare 


O data ce utilizatorul trimite datele din formular si modelul este populat, trebuie sa ne asiguram ca
input-urile sunt valide inainte de a ne folosi de ele. Facem acest lucru prin executarea validarii fata
de un set de reguli. Specificam regulile de validare in metoda rules(), care ar trebui sa returneze
un array cu configuratia regulilor.

class LoginForm extends CFormModel


{
public $username;
public $password;
public $rememberMe=false;

public function rules()


{
return array(
array('username, password', 'required'),
array('password', 'authenticate'),
);
}

public function authenticate($attribute,$params)


{
if(!$this->hasErrors()) // vrem sa permitem autentificarea doar cand nu
sunt erori
{
$identity=new UserIdentity($this->username,$this->password);
if($identity->authenticate())
{
$duration=$this->rememberMe ? 3600*24*30 : 0; // 30 de zile
Yii::app()->user->login($identity,$duration);
}
else
$this->addError('password','Incorrect password.');
}
}
}

38
Ghid de utilizare Yii

In codul de mai sus, username si password sunt obligatorii, iar password va fi validata de catre


metodaauthenticate().
Fiecare regula returnata de catre metoda rules() trebuie sa fie in formatul urmator:

array('AttributeList', 'Validator', 'on'=>'ScenarioList', ...optiuni aditionale)

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

In al treilea caz, Validator poate fi un alias predefinit catre o clasa validator. In exemplul de mai


sus, numelerequired este un alias catre CRequiredValidator, care asigura ca valoarea atributului
va contine ceva. Mai jos avem o lista completa de alias-uri predefinite de validatori:
 captcha: alias pentru of CCaptchaValidator, asigura ca atributul este acelasi cu codul de
verificare afisat intr-un CAPTCHA.
 compare: alias pentru CCompareValidator, asigura ca atributul este egal cu un alt atribut sau
o constanta.
 email: alias pentru CEmailValidator, asigura ca astributul este o adresa de email valida.
 default: alias pentru CDefaultValueValidator, defineste o valoare implicita atributelor
specificate.

39
Ghid de utilizare Yii

 file: alias pentru CFileValidator, asigura ca atributul contine un nume pentru un fisier


pentru care se face upload.
 filter: alias pentru CFilterValidator, transforma atributul cu un filtru.
 in: alias pentru CRangeValidator, asigura ca atributul este intr-o lista predefinita de valori.
 length: alias pentru CStringValidator, asigura ca lungimea valorii atributului este intr-un
anumit interval.
 match: alias pentru CRegularExpressionValidator, asigura ca valoarea atributului se
potriveste cu o expresie regulata.
 numerical: alias pentru CNumberValidator, asigura ca valoarea atributului este este un
numar valid.
 required: alias pentru CRequiredValidator, asigura ca atributul va contine ceva.
 type: alias pentru CTypeValidator, asigura ca atributul este de un anumit tip de date.
 unique: alias pentru CUniqueValidator, asigura ca valoarea atributului este unica intr-o
coloana a unei tabele din baza de date.
 url: alias pentru CUrlValidator, asigura ca valoarea atributului este un URL valid.
Mai jos, prezentam exemple folosind validatori predefiniti:

// username este obligatoriu


array('username', 'required'),

// username trebuie sa aiba intre 3 si 12 caractere


array('username', 'length', 'min'=>3, 'max'=>12),

// in scenariul de inregistrare utilizator, password trebuie sa fie la fel cu


password2
array('password', 'compare', 'compareAttribute'=>'password2', 'on'=>'register'),
// in scenariul de logare, password trebuie sa fie analizat de metoda authenticate()
array('password', 'authenticate', 'on'=>'login'),

3. Securizarea asignarilor de atribute 


Nota: Asignarea de atribute in functie de scenariu este disponibila incepand cu versiunea 1.0.2 a 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

Ultima instructiune este o asignare masiva care asigneaza fiecare intrare


din $_POST['LoginForm'] la atributul corespunzator din model in scenariul login (specificat in al
doilea parametru). Codul de mai sus este echivalent cu codul de mai jos:

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:

public function safeAttributes()


{
return array(
parent::safeAttributes(),
'login' => 'username, password',
);
}

Mai precis, valoarea returnata de metoda safeAttributes ar trebui sa fie de forma urmatoare:

array(
// aceste atribute pot fi asignate masiv in orice scenariu
// care nu este specificat mai jos
'attr1, attr2, ...',
*

// aceste atribute pot fi asignate masiv doar in scenariul 1


'scenario1' => 'attr2, attr3, ...',
*

// aceste atribute pot fi asignate masiv doar in scenariul 2

41
Ghid de utilizare Yii

'scenario2' => 'attr1, attr3, ...',


)

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:

'attr1, attr2, ...'

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

Putem declara regulie de validare in clasa modelului formularului in felul urmator:

public function rules()


{
return array(
array('username, password', 'required'),

42
Ghid de utilizare Yii

array('password_repeat', 'required', 'on'=>'register'),


array('password', 'compare', 'on'=>'register'),
);
}

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.

5. Obtinerea regulilor de validare 


Putem folosi CModel::hasErrors() pentru a verifica daca au fost erori de validare. Daca au fost erori,
putem folosi CModel::getErrors() pentru a obtine mesajele de eroare. Ambele metode pot fi folosite
pentru toate atributele sau pentru un singur atribut.
6. Label-uri de atribute 
Cand proiectam un formular, de obicei trebuie sa afisam un label label pentru fiecare camp input.
Label-ul explica utilizatorului ce fel de informatie trebuie introdusa in campul input. Deci putem
adauga manual un label intr-un view, ar fi mult mai flexibil si convenabil sa specificam label-ul in
modelul formularului.

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:

public function actionLogin()


{
$form=new LoginForm;
if(isset($_POST['LoginForm']))
{
// collects user input data
$form->attributes=$_POST['LoginForm'];
// valideaza input-urile si se face redirect spre pagina anterioara,
// daca are succes validarea
if($form->validate())

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

Nota: Pentru a permite $_POST['LoginForm'] sa ne dea un array in loc de un string, aderam la o


conventie atunci cand denumim campurile input din view. In particular, pentru un camp input care
corespunde cu atributul a1 din clasa modelului C, vom numi acest input C[a1]. De exemplu, vom
folosi LoginForm[username] pentru a numi campul input corespunzator cu atributul username.
Ce mai ramane de facut este sa cream view-ul login care ar trebui sa contina un formular HTML
care sa contina campurile input necesare.

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'=>''));

Altfel, ar fi nevoie de scrierea de javascript peste tot.

Mai jos, folosim CHtml pentru a crea formularul de logare. Presupunem ca


variabila $user reprezinta instantaLoginForm.

<div class="yiiForm">
<?php echo CHtml::form(); ?>

<?php echo CHtml::errorSummary($user); ?>

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

Codul de mai sus genereaza un formular mai dinamic. De exemplu, CHtml::activeLabel() genereaza


un label asociat cu atributul specificat al modelului. Daca atributul are o eroare la intrare, clasa CSS
a label-ului va fierror, ceea ce va schimba aspectul label-ului cu stiluri CSS corespunzatoare. In
mod similar,CHtml::activeTextField() genereaza un camp input text pentru atributul specificat al
modelului si ii schimba clasa CSS in cazul unei erori de intrare.
Daca folosim fisierul cu stiluri CSS form.css pus la dispozitie de unealta yiic, formularul generat
va arata in felul urmator:
Pagina login

Pagina login cu erori

Colectare input-uri tabulare


Uneori vrem sa colectam date de la utilizator in mod automat. Adica, utilizatorul poate introduce
informatiile pentru mai multe instante de modele si sa le trimita pe toate o data. Denumim aceasta
modalitate input tabulardeoarece campurile input sunt de obicei prezentate intr-un tabel HTML.
Pentru a folosi un input tabular, trebuie intai sa cream si sa populam un array de instante cu
modelele respective, in functie ce trebuie sa facem, inserare sau actualizare de date. Apoi, trebuie
sa extragem datele primite de la utilizator din variabila $_POST si sa asignam aceste date fiecarui
model. O diferenta mica fata de asignarea in cazul unui singur model, este ca extragem datele de
intrare folosind $_POST['ModelClass'][$i] in loc de $_POST['ModelClass'].

46
Ghid de utilizare Yii

public function actionBatchUpdate()


{
// extragem elementele de actualizat automat
// presupunem ca fiecare element este al clasei modelului 'Item'
$items=$this->getItemsToUpdate();
if(isset($_POST['Item']))
{
$valid=true;
foreach($items as $i=>$item)
{
if(isset($_POST['Item'][$i]))
$item->attributes=$_POST['Item'][$i];
$valid=$valid && $item->validate();
}
if($valid) // toate elementele sunt valide
// ...se executa ceva aici
}
// afisam view-ul pentru colectarea input-ului tabular
$this->render('batchUpdate',array('items'=>$items));
}

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>

<?php echo CHtml::submitButton('Save'); ?>


</form>

47
Ghid de utilizare Yii

</div><!-- yiiForm -->

Trebuie notat ca folosim "name[$i]" in loc de "name" cand apelam CHtml::activeTextField.


Daca este vreo eroare de validare, campurile input corespunzatoare vor fi evidentiate automat, in
acelasi fel ca in cazul input-urilor unui singur model, caz descris mai devreme.

Lucrul cu baze de date


Yii pune la dispozitie un suport puternic pentru programarea cu bazele de date. Fiind creat pe baza
extensiei PHP Data Objects (PDO), Yii DAO (Data Access Objects) asigura accesul la diverse
DBMS (sisteme de gestiune de baze de date) intr-o singura interfata. Aplicatiile craete folosind Yii
DAO nu trebuie modificate atunci cand se doreste schimbarea sistemului DBMS. Mai mult, Yii AR
(Active Record), o abordare ORM foarte cunoscuta, simplifica si mai mult programarea cu bazele de
date. Prin reprezentarea unei tabele ca fiind o clasa, si prin reprezentarea unui rand din aceasta
tabela ca fiind o instanta a clasei, Yii AR elimina task-ul repetitiv de a scrie acele instructiuni SQL
care in majoritatea cazurilor sunt doar instructiuni CRUD (create, read, update si delete).

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.

Data Access Objects (DAO)


DAO (Obiecte accesare date) pune la dispozitie un API generic pentru accesul datelor stocate in
diverse DBMS (Sisteme de Management Baze de Date). Ca urmare, DBMS-ul poate fi schimbat
oricand cu altul, fara sa fie nevoie sa schimbam codul nostru in care folosim DAO sa accesam
datele.

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:

 CDbConnection: reprezinta conexiunea la o baza de date.


 CDbCommand: reprezinta o instructiune SQL de executat.
 CDbDataReader: reprezinta un flux doar de citire de randuri dintr-un set de rezultate.
 CDbTransaction: reprezinta o tranzactie DB.
In cele ce urmeaza, explicam folosirea Yii DAO in diverse scenarii.

48
Ghid de utilizare Yii

1. Stabilirea conexiunii la baza de date 


Pentru a stabili o conexiune, cream o instanta CDbConnection si o activam. Avem nevoie de un
DSN (nume pentru sursa de date) pentru a specifica informatiile necesare pentru conectarea la baza
de date. Pot fi necesare si un nume si o parola pentru stabilirea conexiunii. Va fi generata o exceptie
in cazul in care apare o eroare la stabilirea conexiunii (ex. DSN gresit sau nume/parola gresite).

$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

2. Executarea instructiunilor SQL 


O data ce este stabilita o conexiune DB, instructiunile SQL pot fi executate folosind CDbCommand.
Cream o instanta CDbCommand prin apelarea CDbConnection::createCommand() cu instructiunea
SQL specificata:

$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.

$rowCount=$command->execute(); // executa SQL


$dataReader=$command->query(); // executa o cerere SQL
$rows=$command->queryAll(); // o cerere care returneaza toate randurile
rezultate
$row=$command->queryRow(); // o cerere care returneaza primul rand dintre
rezultate
$column=$command->queryColumn(); // o cerere care returneaza prima coloana din
rezultate
$value=$command->queryScalar(); // o cerere care returneaza primul camp din primul
rand

3. Extragerea rezultatelor cererii 


Dupa ce CDbCommand::query() genereaza instanta CDbDataReader, putem extrage randurile cu
datele rezultate prin apelarea repetata a CDbDataReader::read(). Putem de asemenea
folosi CDbDataReader intr-unforeach pentru a extrage fiecare rand in parte.

$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

// extragem toate randurile o data intr-un singur array


$rows=$dataReader->readAll();

Nota: Spre deosebire de query(), toate metodele queryXXX() returneaza date direct. De


exemplu,queryRow() returneaza un array care reprezinta primul rand din rezultatele cererii.

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

5. Conectarea de parametri (Binding) 


Pentru a evita [atacurile SQL injection] (http://en.wikipedia.org/wiki/SQL_injection) si pentru a
imbunatati performanta executiilor cererilor SQL folosite des, putem "prepara" o instructiune SQL cu
placeholder-e de parametri optionali care vor fi inlocuiti cu parametrii reali in timpul procesului de
conectare de parametri.

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

// o cerere SQL cu 2 placeholdere ":username" si ":email"


$sql="INSERT INTO users(username, email) VALUES(:username,:email)";
$command=$connection->createCommand($sql);

// inlocuim placeholder-ul ":username" cu valoarea reala username


$command->bindParam(":username",$username,PDO::PARAM_STR);

// inlocuim placeholder-ul ":email" cu valoarea reala email


$command->bindParam(":email",$email,PDO::PARAM_STR);
$command->execute();

// inseram un alt rand cu un nou set de parametri


$command->bindParam(":username",$username2,PDO::PARAM_STR);
$command->bindParam(":email",$email2,PDO::PARAM_STR);
$command->execute();

Metodele bindParam() si bindValue() sunt foarte asemanatoare. Singura diferenta este


ca bindParam()conecteaza un parametru cu o referinta a unei variabile PHP, in timp
ce bindValue() conecteaza un parametru cu o valoare. Pentru parametri care reprezinta blocuri mai
de date memorate, este de preferat sa folosimbindParam() din considerente de perfomanta.
Pentru mai multe detalii despre conectarea de parametrii, trebuie vazuta sectiunea din documentatia
PHP.
6. Conectarea coloanelor (Binding) 
Cand extragem rezultatele cererii, putem sa conectam si coloane la variabile PHP pentru a fi
populate automat cu ultimele date, de fiecare data cand se extrage un rand nou.

$sql="SELECT username, email FROM users";


$dataReader=$connection->createCommand($sql)->query();
// conectam prima coloana (username) cu variabila $username
$dataReader->bindColumn(1,$username);
// conectam a doua coloana (email) cu variabila $email
$dataReader->bindColumn(2,$email);
while($dataReader->read()!==false)
{
// $username si $email contin username si email din randul curent
}

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.

CREATE TABLE Post (


id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
title VARCHAR(128) NOT NULL,
content TEXT NOT NULL,
createTime INTEGER NOT NULL
);

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.

1. Stabilirea unei conexiuni DB 


AR se bazeaza pe o conexiune DB pentru a executa operatiile SQL. Implicit, componenta  db asigura
instanta clasei CDbConnection care este folosita pentru conexiunea DB, cel putin asa se presupune.
Urmatoarea configuratie de aplicatie arata un exemplu:

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

Sfat: Pentru ca AR se bazeaza pe metadatele despre tabele pentru a determina informatiile despre


coloane, citirea acestor metadate si analiza lor vor lua mereu timp. Daca schema bazei de date nu
va fi schimbata prea curand, ar trebui sa activam caching-ul de scheme prin configurarea
proprietatii CDbConnection::schemaCachingDuration cu o valoare mai mare decat 0.

Suportul pentru AR este limitat de catre DBMS. In acest moment, au suport doar urmatoarele
DBMS:

 MySQL 4.1 sau mai nou


 PostgreSQL 7.3 sau mai nou
 SQLite 2 sau 3
Daca vrem sa folosim o alta componenta decat db, sau daca vrem sa lucram cu mai multe baze de
date folosind AR, atunci ar trebui sa suprascriem CActiveRecord::getDbConnection().
Clasa CActiveRecord este clasa de baza pentru toate clasele AR.
Sfat: Exista doua posibilitati in lucrul cu mai multe baze de date in AR. Daca schemele bazelor de
date sunt diferite, atunci putem crea clase de baza AR diferite cu implementari diferite
alegetDbConnection(). Daca schemele bazelor de date sunt la fel, atunci schimbarea dinamica a
variabilei CActiveRecord::db este o idee mult mai buna.

2. Definirea clasei AR 


Pentru a accesa o tabela din baza de date, mai intai trebuie sa definim o clasa AR prin derivarea
claseiCActiveRecord. Fiecare clasa AR reprezinta o singura tabela, iar o instanta a clasei AR
reprezinta un rand din acea tabela. Urmatorul exemplu este codul minim necesar pentru o clasa AR
care reprezimta tabela Post:

class Post extends CActiveRecord


{
public static function model($className=__CLASS__)
{
return parent::model($className);
}

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

Desi nu declaram niciodata in mod explicit proprietatea title din clasa Post, putem accesa


aceasta proprietate pentru ca title este o coloana din tabela Post, iar CActiveRecord ne face
accesibila aceasta coloana ca proprietate cu ajutorul metodei PHP __get(). O exceptie va fi
generata daca incercam sa accesam o coloana inexistenta din tabela.
Info: Pentru o vizibilitate mai mare, este cel mai eficient sa urmam regula camel case cand
denumim tabelele (si coloanele lor) din baza de date. In particular, numele de tabele sunt formate
prin capitalizarea (prima litera este mare) fiecarui cuvant din numele tabelei, si alaturarea fiecarui
cuvant fara sa punem spatiu; numele coloanelor sunt asemanatoare numelor tabelelor, cu singura
diferenta ca prima litera trebuie sa fie litera mica. De exemplu, folosim Post pentru a denumi tabela
care memoreaza post-urile; vom folosi createTime pentru a denumi coloana tabelei care este cheie
primara. Denumind astfel tabelele si coloanele, facem tabelele sa arate exact ca tipurile de clase si
coloanele sa arate exact ca variabilele. De notat ca folosirea camel case poate crea unele
inconveniente cu unele DBMS-uri, ca MySQL, pentru ca s-ar putea comporta diferit in sisteme de
operare diferite.

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:

class Post extends CActiveRecord


{
public $title='Va rugam introduceti un titlu';
......
}

$post=new Post;
echo $post->title; // Aceasta instructiune va afisa: Va rugam introduceti un titlu

Incepand cu versiunea 1.0.2 a Yii, unui atribut i se poate asigna o valoare de


tip CDbExpression inainte ca inregistrarea sa fie salvata (fie insert fie update) in baza de date. De
exemplu, pentru a salva timestamp-ul returnat de functia MySQL NOW(), putem folosi urmatorul cod:

$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.

// find pentru a cauta primul rand care indeplineste conditia specificata


$post=Post::model()->find($condition,$params);
// find pentru a cauta randul cu cheia primara specificata
$post=Post::model()->findByPk($postID,$condition,$params);

56
Ghid de utilizare Yii

// find pentru a cauta randul cu valorile atributelor specificate


$post=Post::model()->findByAttributes($attributes,$condition,$params);
// find pentru a cauta primul rand folosind instructiunea SQL specificata
$post=Post::model()->findBySql($sql,$params);

In cele de mai sus, apelam metoda find cu Post::model(). Sa ne aducem aminte ca metoda


staticamodel() este necesara pentru fiecare clasa AR. Metoda returneaza o instanta AR care este
folosita pentru a accesa metodele clasei (asemanator cu metodele unei clase statice) in contextul
unui obiect.
Daca metoda find gaseste un rand care indeplineste conditiile cererii, atunci va intoarce o
instanta Post ale carei proprietati contin valorile coloanelor corespunzatoare din randul din tabela.
Putem acum citi valorile incarcate exact la fel cum putem citi valorile proprietatilor unui obiect, de
exemplu, echo $post->title;.
Metoda find va intoarce null daca nu este gasit nici un rand din baza de date care indeplineste
conditiile.
Cand apelam find, folosim $condition si $params pentru a transmite conditiile. $condition poate
fi un string in care se retine clauza WHERE dintr-o instructiune SQL. $params este un array de
parametri ale caror valori ar trebui sa fie conectate corespunzator la placeholder-ele
din $condition. De exemplu,

// find pentru a gasi randul cu postID=10


$post=Post::model()->find('postID=:postID', array(':postID'=>10));

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

Trebuie sa retinem ca atunci cand folosim CDbCriteria pentru conditii, parametrul $params nu mai


este necesar din moment ce poate fi specificat in CDbCriteria, asa cum am aratat mai sus.
O alta modalitate in ce priveste CDbCriteria este sa transmitem un array catre metoda find. Fiecare
pereche key-value din array va corespune unei perechi nume proprietate-valoare. Putem sa
rescriem exemplul de mai sus astfel:

$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.

// find all pentru a cauta randurile care indeplinesc conditia


$posts=Post::model()->findAll($condition,$params);
// find all pentru a cauta randurile care au cheile primare specificate
$posts=Post::model()->findAllByPk($postIDs,$condition,$params);
// find all pentru a cauta randurile care au valorile specificate
$posts=Post::model()->findAllByAttributes($attributes,$condition,$params);
// find all pentru a cauta randurile care rows using the specified SQL statement
$posts=Post::model()->findAllBySql($sql,$params);

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:

// afla cate randuri indeplinesc conditia specificata


$n=Post::model()->count($condition,$params);
// afla numarul de randuri folosind instructiunea SQL specificata
$n=Post::model()->countBySql($sql,$params);
// verifica daca este cel putin un rand care indeplineste conditia specificata
$exists=Post::model()->exists($condition,$params);

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

$post->save(); // salveaza modificarile in baza de date

Dupa cum se vede, folosim aceeasi metoda save() pentru a executa si inserarea si actualizarea.


Daca o instanta AR este creata folosind operatorul new, atunci save() va insera un nou rand in
tabela. Daca instanta AR este rezultatul unei metode find sau findAll, atunci save() va actualiza
randul existent din tabela. De fapt, putem folosi CActiveRecord::isNewRecord pentru a verifica daca
instanta AR este noua sau nu.
Este de asemenea posibil sa actualizam unul sau mai multe randuri dintr-o tabela fara sa incarcam
inainte. AR pune la dispozitie urmatoarele metode pentru acest scop:

// actualizeaza randurile care indeplinesc conditia specificata


Post::model()->updateAll($attributes,$condition,$params);
// actualizeaza randurile care se potrivesc cu conditia specificata si cu cheile
primare specificate
Post::model()->updateByPk($pk,$attributes,$condition,$params);
// actualizeaza contorizarea coloanelor din randurile care indeplinesc conditia
specificata
Post::model()->updateCounters($counters,$condition,$params);

In cele de mai sus, $attributes este un array cu valori de coloane indexate dupa numele


coloanelor;$counters este un array cu valori de incrementare indexate dupa numele coloanelor;
iar $condition si$params sunt descrie in subsectiunea anterioara.
6. Stergerea inregistrarilor 
Putem de asemenea sterge un rand din tabela daca o instanta AR a fost populata cu acest rand.

$post=Post::model()->findByPk(10); // presupunem ca este un post cu ID=10


$post->delete(); // sterge randul din tabela

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:

// sterge randurile care indeplinesc conditia specificata


Post::model()->deleteAll($condition,$params);
// sterge randurile care se potrivesc cu cheile primare si cu conditia specificata
Post::model()->deleteByPk($pk,$condition,$params);

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.

// se presupune ca $_POST['Post'] este un array de valori de coloane indexat dupa


numele coloanelor
$post->attributes=$_POST['Post'];
$post->save();

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

Active Record relational

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

Info: Suportul pentru constrangeri cu chei foreign depinde de DBMS.


SQLite nu are suport pentru astfel de constrangeri. Dar putem totusi declara constrangerile atunci
cand cream tabelele. AR poate exploata aceste declaratii pentru a aduce un suport pentru cererile
relationale.

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

CREATE TABLE Foo


(
id INTEGER NOT NULL PRIMARY KEY
);
CREATE TABLE bar
(
id INTEGER NOT NULL PRIMARY KEY,
fooID INTEGER
COMMENT 'CONSTRAINT FOREIGN KEY (fooID) REFERENCES Foo(id)'
);

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'=>array('RelationType', 'ClassName', 'ForeignKey', ...optiuni aditionale)

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.

class Post extends CActiveRecord


{
public function relations()
{
return array(
'author'=>array(self::BELONGS_TO, 'User', 'authorID'),
'categories'=>array(self::MANY_MANY, 'Category', 'PostCategory(postID,
categoryID)'),
);
}
}

class User extends CActiveRecord


{
public function relations()
{
return array(
'posts'=>array(self::HAS_MANY, 'Post', 'authorID'),
'profile'=>array(self::HAS_ONE, 'Profile', 'ownerID'),
);
}
}

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

cu care s-a facut legatura. De exemplu, daca $author reprezinta o instanta AR User, putem


folosi $author->posts pentru a accesa instantele sale Post.
2. Executarea cererilor relationale 
Cel mai simplu mod de executie al unei cereri relationale este prin citirea proprietatii relationale dintr-
o instanta AR. Daca proprietatea nu este accesata anterior, va fi initiata o cerere relationala care
aplica join celor doua tabele si filtreaza dupa cheia primara a instantei AR curente. Rezultatul cererii
va fi salvat in proprietate ca instanta (sau instante) ale clasei (claselor) AR respective. Aceasta
abordare este cunoscuta sub termenul delazy loading (incarcare pt puturosi:D), aceasta insemnand
ca cererea relationala este executata atunci cand obiectele respective sunt accesate initial.
Exemplul de mai jos arata cum sa folosim aceasta abordare:

// extragem post-ul cu ID=10


$post=Post::model()->findByPk(10);
// extragem autorul post-ului: o cerere relationala va fi executata aici
$author=$post->author;

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:

class User extends CActiveRecord


{
public function relations()
{
return array(

67
Ghid de utilizare Yii

'posts'=>array(self::HAS_MANY, 'Post', 'authorID'


'order'=>'??.createTime DESC',
'with'=>'categories'),
'profile'=>array(self::HAS_ONE, 'Profile', 'ownerID'),
);
}
}

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.

4. Optiuni pentru cereri relationale dinamice 


Incepand cu versiunea 1.0.2, Putem folosi optiuni pentru cereri relationale dinamice si in with() si in
optiuneawith. Optiunile dinamice vor suprascrie optiunile existente specificate in metoda relations().
De exemplu, in cazul modelului User de mai sus, daca vrem sa folosim abordarea eager loading
pentru a incarca toate post-urile care apartin unui autor, ascending order (optiunea order din
specificatia relatiei este setata cu ordine desecendenta), putem face in felul urmator:

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

Cand aplicatia ruleaza, componenta cache poate fi accesata prin Yii::app()->cache.


Yii pune la dispozitie mai multe componente cache, pentru a asigura memorarea datelor pe mai
multe medii de stocare, in functie de necesitati. De exemplu,
componenta CMemCache incapsuleaza extensia PHP memcache si foloseste memoria RAM ca
mediu de stocare; componenta CApcCache incapsuleaza extensia APC din PHP; iar
componenta CDbCache memoreaza datele intr-o baza de date. Mai jos sunt componentele cache
disponibile in acest moment:
 CMemCache: foloseste PHP extensia memcache.
 CApcCache: foloseste extensia APC din PHP.
 CXCache: foloseste extensia XCache din PHP. Note, this has been available since version
1.0.1.
 CDbCache: foloseste o tabela din baza de date pentru a memora datele din cache. Va crea
si va folosi o baza de date SQLite3 care va exista in directorul runtime. Putem specifica explicit
o baza de date prin setarea proprietatii connectionID.
Sfat: Pentru ca toate aceste componente cache sunt derivate din aceeasi clasa de baza CCache,
putem folosi oricand un tip diferit de cache fara sa modificam codul care foloseste cache-ul.

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:

// pastram valoarea in cache pentru cel mult 30 de secunde


Yii::app()->cache->set($id, $value, 30);

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.

Reprezentam o dependenta ca instanta a clasei CCacheDependency sau a unei clase derivate.


Transmitem instanta dependentei impreuna cu datele care trebuie memorate in cache atunci cand
apelam set().

// valoarea va expira in 30 de secunde


// poate fi invalidata mai devreme daca fisierul dependent este modificat
Yii::app()->cache->set($id, $value, 30, new CFileCacheDependency('NumeFisier'));

In acest moment, daca extragem $value din cache prin apelarea get(), dependenta va fi evaluata si


daca este modificata, atunci vom primi o valoare false, ceea ce indica faptul ca datele trebuie
regenerate.
Mai jos avem un sumar ale dependentelor cache posibile:

 CFileCacheDependency: dependenta este modificata daca timpul ultimei modificari s-a


schimbat.
 CDirectoryCacheDependency: dependenta este modificata daca s-a modificat cel putin un
fisier din director sau din subdirectoarele acestuia.
 CDbCacheDependency: dependenta este modificata daca rezultatul cererii SQL este
modificata.
 CGlobalStateCacheDependency: dependenta este modificata daca valoarea starii globale
specificate este modificata. O stare globala este o variabila care este persistenta intre cererile si
sesiunile unei aplicatii. Variabila este definita prin CApplication::setGlobalState().
 CChainedCacheDependency: dependenta este modificata daca este modificata oricare
dintre dependentele dintr-un chain.
Cache de fragmente
71
Ghid de utilizare Yii

Caching-ul de fragmente se refera la memorarea in cache a unui fragment dintr-o pagina. De


exemplu, daca o pagina afiseaza un sumar al vanzarilor anuale intr-o tabela, putem sa memoram
aceasta tabela in cache pentru a elimina timpul de generare al tabelei la fiecare cerere utilizator.

Pentru a folosi caching-ul de fragmente, putem sa


apelam CController::beginCache() siCController::endCache() intr-un fisier view al controller-ului.
Cele doua metode marcheaza inceputul si sfarsitul fragmentului din pagina care trebuie memorat in
cache. Ca si caching-ul de date, avem nevoie de un ID pentru a identifica fragmentul care va fi
memorat in cache.

...alt continut HTML...


<?php if($this->beginCache($id)) { ?>
...continut de adaugat in cache...
<?php $this->endCache(); } ?>
...alt continut HTML...

In codul de mai sus, daca beginCache() returneaza false, continutul memorat in cache va fi automat


inserat in acel loc; altfel, continutul dintre instructiunile if va fi executat si va fi adaugat in cache
atunci cand este invocata endCache().

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:

...alt continut HTML...


<?php if($this->beginCache($id, array('duration'=>3600))) { ?>
...continut de memorat in cache...
<?php $this->endCache(); } ?>
...alt continut HTML...

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:

...alt continut HTML...


<?php if($this->beginCache($id, array('dependency'=>array(
'class'=>'system.caching.dependencies.CDbCacheDependency',
'sql'=>'SELECT MAX(lastModified) FROM Post')))) { ?>
...continut de memorat in cache...
<?php $this->endCache(); } ?>
...alt continut HTML...

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:

...alt continut HTML...


<?php if($this->beginCache($id, array('requestTypes'=>array('GET')))) { ?>
...continut de memorat in cache...
<?php $this->endCache(); } ?>
...alt continut HTML...

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.

...alt continut HTML...


<?php if($this->beginCache($id1)) { ?>
...continutul exterior de memorat in cache...
<?php if($this->beginCache($id2)) { ?>
...continutul interior de memorat in cache...
<?php $this->endCache(); } ?>
...continutul exterior de memorat in cache...
<?php $this->endCache(); } ?>
...alt continut HTML...

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:

public function filters()


{
return array(
array(
'system.web.widgets.COutputCache',
'duration'=>100,
'varyByParam'=>array('id'),
),
);
}

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.

Apelam CController::renderDynamic() pentru a insera continut dinamic in locul dorit.

...alt continut HTML...


<?php if($this->beginCache($id)) { ?>
...fragmentul de memorat in cache...
<?php $this->renderDynamic($callback); ?>
...fragmentul de memorat in cache...
<?php $this->endCache(); } ?>
...alt continut HTML...

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:

1. Descarcarea extensiei din depozitul de extensii de pe site-ul Yii.


2. Dezarhivarea extensiei in subdirectorul extensions/xyz dindirectorul de baza al aplicatiei,
unde xyz este numele extensiei.
3. Importarea, configurarea si folosirea extensiei.
Fiecare extensie are un nume care o identifica unic fata de celelalte extensii. Daca extensia are
numele xyz, putem folosi oricand alias-ul de cale application.extensions.xyz pentru a localiza
directorul de baza care contine toate fisierele extensiei xyz.
Fiecare extensie are cerinte specifice in ce priveste importarea, configurarea si folosirea. In cele ce
urmeaza, facem un sumar al scenariilor obisnuite de folosire, urmand categorisirea descrisa in
sectiunea Generalitati despre extensii.
1. Componenta aplicatie 
Pentru a folosi o componenta de aplicatie, trebuie sa modificam fisierul de configurare a
aplicatiei prin adaugarea unei noi intrari la proprietatea components, in felul urmator:

return array(
// 'preload'=>array('xyz',...),
'components'=>array(
'xyz'=>array(
'class'=>'application.extensions.xyz.XyzClass',
'property1'=>'value1',
'property2'=>'value2',
),
// alte configuratii de componente
),
);

Apoi, putem accesa componenta oriunde in cod, folosind Yii::app()->xyz. Componenta la fi


creata prin abordarea lazy (adica, ea va fi creata atunci cand este accesata prima data). Putem
specifica incarcarea ei in proprietatea preload, pentru a fi creata automat o data cu aplicatia.

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:

// widget care nu are nevoie de continut body


<?php $this->widget('application.extensions.xyz.XyzClass', array(
'property1'=>'value1',
'property2'=>'value2')); ?>

// widget care poate contine un body


<?php $this->beginWidget('application.extensions.xyz.XyzClass', array(
'property1'=>'value1',
'property2'=>'value2')); ?>

...continutul body al widget-ului...

<?php $this->endWidget(); ?>

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:

class TestController extends CController


{
public function actions()
{
return array(
'xyz'=>array(
'class'=>'application.extensions.xyz.XyzClass',
'property1'=>'value1',
'property2'=>'value2',
),
// alte action-uri
);
}
}

Apoi, action-ul poate fi accesat prin routa test/xyz.


4. Filtru 
Filtrele sunt de asemenea folosite de catre un controller. In principal asigura posibilitatea de a
executa un cod inainte si dupa procesarea unei cereri din partea utilizatorului web atunci cand este

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:

class TestController extends CController


{
public function filters()
{
return array(
array(
'application.extensions.xyz.XyzClass',
'property1'=>'value1',
'property2'=>'value2',
),
// alte filtre
);
}
}

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

Apoi, un action a din controller poate fi accesat prin ruta xyz/a.


6. Validator 
Un validator este folosit in special intr-o clasa model (una care este derivata fie din CFormModel fie
dinCActiveRecord). Daca avem o clasa validator XyzClass (care apartine extensiei xyz), putem s-o
folosim prin suprascrierea metodei CModel::rules din clasa modelului nostru:

79
Ghid de utilizare Yii

class MyModel extends CActiveRecord // sau CFormModel


{
public function rules()
{
return array(
array(
'attr1, attr2',
'application.extensions.xyz.XyzClass',
'property1'=>'value1',
'property2'=>'value2',
),
// alte reguli de validare
);
}
}

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

Apoi, putem folosi unealta yiic care va avea incorporata noua comanda xyz.


Nota: O aplicatie de consola de obicei foloseste un fisier de configurare care este diferit decat cel
folosit de o aplicatie Web. Daca o aplicatie este creata folosind comanda yiic webapp, atunci
fisierul de configurare pentru aplicatia de
consola protected/yiic esteprotected/config/console.php, in timp ce fisierul de configurare
pentru aplicatia Web esteprotected/config/main.php.

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.

class MyTabView extends CTabView


{
public function init()
{
if($this->cssFile===null)
{
$file=dirname(__FILE__).DIRECTORY_SEPARATOR.'tabview.css';
$this->cssFile=Yii::app()->getAssetManager()->publish($file);
}
parent::init();
}
}

In codul de mai sus, suprascriem metoda CWidget::init si atribuim


proprietatii CTabView::cssFile URL-ul cu noul stil CSS. Punem apoi fisierul cu noul stil CSS in
acelasi director care contine fisierul clasei MyTabView pentru a putea fi impachetate intr-o extensie.
Deoarece fisierul cu stilul CSS nu este accesibil utilizatorilor Web, trebuie sa il publicam ca fiind un
asset.
Pentru a crea un nou widget de la zero, in principal trebuie sa implementam doua
metode: CWidget::init siCWidget::run. Prima metoda este apelata atunci cand folosim $this-
>beginWidget pentru a insera un widget intr-un view. A doua metoda este apelata atunci cand
apelam $this->endWidget. Daca vrem sa capturam si sa procesam continutul afisat intre aceste
doua apeluri de metode, putem porni output buffering inCWidget::init si in CWidget::run sa extragem
output-ul memorat pentru procesare ulterioara.
Cand folosim un widget, de obicei trebuie sa includem CSS-ul, javascript-ul sau alte fisiere in pagina
care foloseste widget-ul. Denumim aceste fisiere assets pentru ca ele stau impreuna cu fisierul
clasei widget-ului si nu sunt accesibile in mod normal utilizatorilor Web. Pentru a face accesibile
aceste fisiere, trebuie sa le publicam folosind CWebApplication::assetManager, dupa cum am aratat
in snippet-ul de mai sus. In plus, daca vrem sa includem un fisier CSS sau javascript in pagina
curenta, trebuie sa inregistram fisierul folosindCClientScript:
82
Ghid de utilizare Yii

class MyWidget extends CWidget


{
protected function registerClientScript()
{
// ...publicam fisiere CSS sau JavaScript aici...
$cs=Yii::app()->clientScript;
$cs->registerCssFile($cssFile);
$cs->registerScriptFile($jsFile);
}
}

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.

class MyFilter extends CFilter


{
protected function preFilter($filterChain)
{
// lucruri de aplicat inainte ca action-ul sa fie executat
return true; // false daca action-ul NU ar trebui sa fie executat
}

protected function postFilter($filterChain)


{
// lucruri de aplicat dupa ce action-ul a fost executat
}
}

Parametrul $filterChain este de tip CFilterChain care contine informatii despre action-ul care este


filtrat in acest moment.
5. Controller 
83
Ghid de utilizare Yii

Un controller distribuit ca extensie ar trebui sa fie derivat din clasa CExtController, in loc


de CController. Motivul principal este ca CController presupune ca fisierele view ale controller-ului
sunt localizate inapplication.views.ControllerID, in timp ce CExtController presupune ca
fisierele view sunt localizate in interiorul directorului views care este un subdirector al directorului
care contine fisierul clasei controller-ului. De aceea, este mai usor sa redistribuim controller-ul din
moment ce fisierele sale view stau impreuna cu fisierul clasei controller-ului.
6. Validator 
Un validator ar trebui sa fie derivat din CValidator si sa implementeze
metoda CValidator::validateAttribute.

class MyValidator extends CValidator


{
protected function validateAttribute($model,$attribute)
{
$value=$model->$attribute;
if($value has error)
$model->addError($attribute,$errorMessage);
}
}

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.

class MyCommand extends CConsoleCommand


{
public function run($args)
{
// $args gives an array of the command-line arguments for this command
}

public function getHelp()


{
return 'Folosire: cum folosim aceasta 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.

Folosirea bibliotecilor 3rd-Party


Yii este proiectat foarte atent pentru a permite integrarea usoara a bibliotecilor third-party, pentru a
extinde functionalitatea Yii. Atunci cand folosim biblioteci third-party intr-un proiect, intampinam de
obicei probleme in legatura cu includerea fisierelor si denumirea claselor. Deoarece toate clasele Yii
sunt prefixate cu litera C, este putin probabil sa apara vreun conflict de denumire; si pentru ca Yii se
bazeaza pe SPL autoload pentru a executa includerea fisierelor claselor, se poate descurca usor cu
alte biblioteci daca si ele folosesc acelasi feature de autoloading sau PHP include path pentru a
include fisierele claselor.
Mai jos folosim un exemplu pentru a arata cum sa folosim
componenta Zend_Search_Lucene din platforma Zend in interiorul unei aplicatii Yii.
Mai intai, vom extrage fisierul platformei Zend intr-un director sub protected/vendors,
presupunand caprotected este directorul de baza al aplicatiei. Verificam daca exista
fisierulprotected/vendors/Zend/Search/Lucene.php.
Apoi, la inceputul fisierului cu clasa controller-ului, inseram urmatoarele linii:

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

where $this refers to the controller instance; $route specifies the route of the request;


and $params is a list of GET parameters to be appended to the URL.
By default, URLs created by createUrl is in the so-called get format. For example,
given $route='post/read'and $params=array('id'=>100), we would obtain the following URL:

/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

To change the URL format, we should configure the urlManager application component so


that createUrl can automatically switch to the new format and the application can properly
understand the new URLs:

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:

'pattern1'=>array('route1', 'urlSuffix'=>'.xml', 'caseSensitive'=>false)

Using Named Parameters


A rule can be associated with a few GET parameters. These GET parameters appear in the rule's
pattern as special tokens in the following format:
87
Ghid de utilizare Yii

&lt;ParamName:ParamPattern&gt;

where ParamName specifies the name of a GET parameter, and the optional ParamPattern specifies


the regular expression that should be used to match the value of the GET parameter. In case
when ParamPatternis omitted, it means the parameter should match any characters except the
slash /. When creating a URL, these parameter tokens will be replaced with the corresponding
parameter values; when parsing a URL, the corresponding GET parameters will be populated with
the parsed results.
Let's use some examples to explain how URL rules work. We assume that our rule set consists of
three rules:

array(
'posts'=>'post/list',
'post/<id:\d+>'=>'post/read',
'post/<year:\d{4}>/<title>'=>'post/read',
)

 Calling $this->createUrl('post/list') generates /index.php/posts. The first rule is


applied.
 Calling $this->createUrl('post/read',array('id'=>100)) generates /index.php/po
st/100. The second rule is applied.
 Calling $this->createUrl('post/read',array('year'=>2008,'title'=>'a sample
post'))generates /index.php/post/2008/a%20sample%20post. The third rule is applied.
 Calling $this->createUrl('post/read') generates /index.php/post/read. None of the
rules is applied.
In summary, when using createUrl to generate a URL, the route and the GET parameters passed to
the method are used to decide which URL rule to be applied. If every parameter associated with a
rule can be found in the GET parameters passed to createUrl, and if the route of the rule also
matches the route parameter, the rule will be used to generate the URL.
If the GET parameters passed to createUrl are more than those required by a rule, the additional
parameters will appear in the query string. For example, if we call $this-
>createUrl('post/read',array('id'=>100,'year'=>2008)), we would
obtain /index.php/post/100?year=2008. In order to make these additional parameters appear in
the path info part, we should append /* to the rule. Therefore, with the rule post/<id:\d+>/*, we
can obtain the URL as/index.php/post/100/year/2008.
As we mentioned, the other purpose of URL rules is to parse the requesting URLs. Naturally, this is
an inverse process of URL creation. For example, when a user requests for /index.php/post/100,
the second rule in the above example will apply, which resolves in the route post/read and the GET
parameterarray('id'=>100) (accessible via $_GET).

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

# if a directory or a file exists, use it directly

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_FILENAME} !-d

# otherwise forward it to index.php

RewriteRule . index.php

We then configure the showScriptName property of the urlManager component to be false.


Now if we call $this->createUrl('post/read',array('id'=>100)), we would obtain the
URL /post/100. More importantly, this URL can be properly recognized by our Web application.

90
Ghid de utilizare Yii

Faking URL Suffix


We may also add some suffix to our URLs. For example, we can have /post/100.html instead
of /post/100. This makes it look more like a URL to a static Web page. To do so, simply configure
the urlManager component by setting its urlSuffix property to the suffix you like.

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.

Centrul sistemului de autentificare al Yii este componenta de aplicatie user, un obiect care


implementeaza interfata [IWebUser]. Componenta user reprezinta informatiile persistente despre
identitatea utilizatorului curent. Putem accesa aceasta componenta de oriunde din cod
prin Yii::app()->user.
Folosind componenta user, putem verifica daca un utilizator este logat sau nu
prin CWebUser::isGuest; putem sa logam un user prin metoda login si sa il delogam prin
metoda logout; putem sa verificam daca utilizatorul are dreptul sa execute anumite operatii prin
apelul metodei CWebUser::checkAccess; de asemenea, putem sa obtinem identificatorul unic si alte
informatii persistente despre identitatea utilizatorului.

1. Definirea clasei de identitate 


Pentru a autentifica un utilizator, definim o clasa de identitate care contine codul efectiv de
autentificare. Clasa de identitate ar trebui sa implementeze interfata [IUserIdentity]/ Mai multe clase
pot implementa abordari diferite de autentificare (ex. OpenID, LDAP). Un start bun ar fi sa derivam
clasa CUserIdentity care este clasa de baza pentru autentificarea pe baza unui nume si a unei
parole.
Cel mai important lucru in definirea unei clase de identitate este implementarea metodei
[IUserIdentity::authenticate]. O clasa de identitate poate sa declare de asemenea informatii
aditionale despre identitate care trebuie sa fie persistente in timpul sesiunii utilizatorului.

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.

class UserIdentity extends CUserIdentity


{
private $_id;
public function authenticate()
{
$record=User::model()->findByAttributes(array('username'=>$this->username));
if($record===null)
$this->errorCode=self::ERROR_USERNAME_INVALID;
else if($record->password!==md5($this->password))
$this->errorCode=self::ERROR_PASSWORD_INVALID;
else
{
$this->_id=$record->id;
$this->setState('title', $record->title);
$this->errorCode=self::ERROR_NONE;
}
return !$this->errorCode;
}

public function getId()


{
return $this->_id;
}
}

Informatiile memorate (prin apelul metodei CBaseUserIdentity::setState) vor fi trimise


catre CWebUser care le memoreaza mai departe intr-un mediu de stocare persistent, ca de exemplu
sesiunea utilizatorului. Aceste informatii pot fi accesate ca proprietati ale clasei CWebUser. De
exemplu, putem obtine informatia title a utilizatorului curent prin Yii::app()->user-
>title (Acest lucru este disponibil incepand cu versiunea 1.0.3. In versiunile anterioare, trebuie sa
folosim Yii::app()->user->getState('title')).
Info: Implicit, CWebUser foloseste sesiunea utilizatorului ca mediu de stocare persistent pentru
informatiile referitoare la identitatea utilizatorului. Daca logarea bazata pe cookie-uri este activata
(prin setarea CWebUser::allowAutoLogin cu valoarea true), informatiile despre identitatea
utilizatorului pot fi salvate si in cookie-uri. Totusi trebuie sa ne asiguram ca nu memoram in cookie-
uri informatii care ar trebui sa fie ascunse (ex. parola).
2. Login si Logout 

92
Ghid de utilizare Yii

Folosind calasa de identitate si componenta user, putem implementa usor action-urile de logare si
delogare.

// Logheaza un utilizator cu numele si parola date


$identity=new UserIdentity($username,$password);
if($identity->authenticate())
Yii::app()->user->login($identity);
else
echo $identity->errorMessage;
......
// Delogheaza utilizatorului curent
Yii::app()->user->logout();

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.

// tinem userul logat timp de 7 zile.


// trebuie sa ne asiguram ca proprietatea allowAutoLogin este setata cu true in
componenta user.
Yii::app()->user->login($identity,3600*24*7);

3. Filtrul de control al accesului 


Filtrul de control al accesului este o schema preliminara de autorizare care verifica daca utilizatorul
curent poate executa action-ul cerut. Autorizarea se foloseste de numele utilizatorului, IP-ul clientului
si tipul cererii. Este pus la dispozitie ca un filtru cu numele "accessControl".
Sfat: Filtrul de control al accesului este suficient pentru scenariile simple. Pentru un control al
accesului complex, va trebui probabil sa folositi RBAC (acces bazat pe roluri) care va fi explicat mai
jos.
Pentru a controla accesul la action-urile unui controller, instalam filtrul de control al accesului prin
suprascrierea CController::filters (vedeti Filter pentru mia multe detalii despre instalarea filtrelor).

class PostController extends CController


{
......
public function filters()
{

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.

class PostController extends CController


{
......
public function accessRules()
{
return array(
array('deny',
'actions'=>array('create', 'edit'),
'users'=>array('?'),
),
array('allow',
'actions'=>array('delete'),
'roles'=>array('admin'),
),
array('deny',
'actions'=>array('delete'),
'users'=>array('*'),
),
);
}
}

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.

O regula de acces poate sa se potriveasca cu urmatorii parametri de context:

 actions: precizeaza lista de action-uri pentru care se aplica regula.


 users: precizeaza utilizatorii pentru care se aplica regula. Este folosit numele utilizatorului
curent. Trei caractere speciale pot fi folosite aici:
 *: orice utilizator, anonim sau autentificat.
 ?: utilizatorii anonimi.
 @: utilizatorii autentificati.
 roles: precizeaza caror roluri li se aplica regula curenta. Se foloseste controlul accesului
bazat pe roluricare va fi descris in sub-sectiunea urmatoare. In particular, regula este aplicata
dacaCWebUser::checkAccess returneaza true pentru unul dintre roluri. De notat este faptul ca
ar trebui sa folosim rolurile in regulile allow pentru ca, prin definitie, un rol reprezinta o
permisiune de a face ceva.
 ips: precizeaza caror adrese IP li se va aplica regula.
 verbs: precizeaza caror tipuri de cereri (ex. GET, POST) li se va aplica regula.
 expression: precizeaza o expresie PHP a carei valoare indica daca aceasta regula se va
aplica sau nu. In aceasta expresie putem folosi variabila $user care se refera la Yii::app()-
>user. Aceasta optiune este disponibila incepand cu versiunea 1.0.3.

Tratarea rezultatelor de autorizare


Cand autorizarea esueaza, adica utilizatorul nu are dreptul sa executa action-ul specificat, se poate
intampla unul dintre urmatoarele doua scenarii:

 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

 Otherwise an HTTP exception will be displayed with error code 401.


When configuring the loginUrl property, one can provide a relative or absolute URL. One can also
provide an array which will be used to generate a URL by calling CWebApplication::createUrl. The
first array element should specify the route to the login controller action, and the rest name-value
pairs are GET parameters. For example,

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

4. Role-Based Access Control 


Role-Based Access Control (RBAC) provides a simple yet powerful centralized access control.
Please refer to the Wiki article for more details about comparing RBAC with other more traditional
access control schemes.
Yii implements a hierarchical RBAC scheme via its authManager application component. In the
following ,we first introduce the main concepts used in this scheme; we then describe how to define
authorization data; at the end we show how to make use of the authorization data to perform access
checking.

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
}

Configuring Authorization Manager


Before we set off to define an authorization hierarchy and perform access checking, we need to
configure theauthManager application component. Yii provides two types of authorization
managers: CPhpAuthManager andCDbAuthManager. The former uses a PHP script file to store
authorization data, while the latter stores authorization data in database. When we configure
the authManager application component, we need to specify which component class to use and
what are the initial property values for the component. For example,

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',
),
),
);

We can then access the authManager application component using Yii::app()->authManager.

Defining Authorization Hierarchy


Defining authorization hierarchy involves three steps: defining authorization items, establishing
relationships between authorization items, and assigning roles to application users.
The authManager application component provides a whole set of APIs to accomplish these tasks.
To define an authorization item, call one of the following methods, depending on the type of the item:

 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
}

Using Default Roles


Note: The default role feature has been available since version 1.0.3
Many Web applications need some very special roles that would be assigned to every or most of the
system users. For example, we may want to assign some privileges to all authenticated users. It
poses a lot of maintenance trouble if we explicitly specify and store these role assignments. We can
exploit default roles to solve this problem.
A default role is a role that is implicitly assigned to every user, including both authenticated and
guest. We do not need to explicitly assign it to a user. When CWebUser::checkAccess, default roles
will be checked first as if they are assigned to the user.
Default roles must be declared in the CAuthManager::defaultRoles property. For example, the
following configuration declares two roles to be default roles: authenticated and guest.

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'

Below is an example of directory organization for an application with two themes basic and fancy.

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

In the application configuration, if we configure

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.

Yii::log($message, $level, $category);


Yii::trace($message, $category);

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:

 CDbLogRoute: saves messages in a database table.


 CEmailLogRoute: sends messages to specified email addresses.
 CFileLogRoute: saves messages in a file under the application runtime directory.
 CWebLogRoute: displays messages at the end of the current Web page.
 CProfileLogRoute: displays profiling messages at the end of the current Web page.
Info: Message routing occurs at the end of the current request cycle when the onEndRequest event
is raised. To explicitly terminate the processing of the current request,
call CApplication::end()instead of die() or exit(), because CApplication::end() will raise
the onEndRequest event so that the messages can be properly logged.

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.

Logging Context Information


Starting from version 1.0.6, we can specify to log additional context information, such as PHP
predefined variables (e.g. $_GET, $_SERVER), session ID, user name, etc. This is accomplished by
specifying theCLogRoute::filter property of a log route to be a suitable log filter.
The framework comes with the convenient CLogFilter that may be used as the needed log filter in
most cases. By default, CLogFilter will log a message with variables like $_GET, $_SERVER which
often contains valuable system context information. CLogFilter can also be configured to prefix each
logged message with session ID, username, etc., which may greatly simplifying the global search
when we are checking the numerous logged messages.
The following configuration shows how to enable logging context information. Note that each log
route may have its own log filter. And by default, a log route does not have a log filter.

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

where blockID is an ID that uniquely identifies the code block.

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.

To show profiling result, we need to install a CLogRouter application component with


a CProfileLogRoute log route. This is the same as we do with normal message routing.
The CProfileLogRoute route will display the performance results at the end of the current page.

Profiling SQL Executions


Profiling is especially useful when working with database since SQL executions are often the main
performance bottleneck of an application. While we can manually
insert beginProfile and endProfile statements at appropriate places to measure the time spent
in each SQL execution, starting from version 1.0.6, Yii provides a more systematic approach to solve
this problem.
By setting CDbConnection::enableProfiling to be true in the application configuration, every SQL
statement being executed will be profiled. The results can be readily displayed using the
aforementionedCProfileLogRoute, which can show us how much time is spent in executing what
SQL statement. We can also call CDbConnection::getStats() to retrieve the total number SQL
statements executed and their total execution time.
Error Handling
Yii provides a complete error handling framework based on the PHP 5 exception mechanism. When
the application is created to handle an incoming user request, it registers its handleError method to
handle PHP warnings and notices; and it registers its handleException method to handle uncaught
PHP exceptions. Consequently, if a PHP warning/notice or an uncaught exception occurs during the
application execution, one of the error handlers will take over the control and start the necessary
error handling procedure.
Tip: The registration of error handlers is done in the application's constructor by calling PHP
functions set_exception_handler and set_error_handler. If you do not want Yii to handle the errors
and exceptions, you may define
constant YII_ENABLE_ERROR_HANDLER andYII_ENABLE_EXCEPTION_HANDLER to be false in
the entry script.
By default, errorHandler (or exceptionHandler) will raise an onError event (or onException event). If
the error (or exception) is not handled by any event handler, it will call for help from
the errorHandler application component.
1. Raising Exceptions 
Raising exceptions in Yii is not different from raising a normal PHP exception. One uses the
following syntax to raise an exception when needed:

throw new ExceptionClass('ExceptionMessage');

107
Ghid de utilizare Yii

Yii defines two exception classes: CException and CHttpException. The former is a generic


exception class, while the latter represents an exception that should be displayed to end users. The
latter also carries astatusCode property representing an HTTP status code. The class of an
exception determines how it should be displayed, as we will explain next.
Tip: Raising a CHttpException exception is a simple way of reporting errors caused by user
misoperation. For example, if the user provides an invalid post ID in the URL, we can simply do the
following to show a 404 error (page not found):

// 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.

Handling Errors Using an Action


Starting from version 1.0.6, Yii allows using a controller action to handle the error display work. To
do so, we should configure the error handler in the application configuration as follows:

return array(
......
'components'=>array(

108
Ghid de utilizare Yii

'errorHandler'=>array(
'errorAction'=>'site/error',
),
),
);

In the above, we configure the CErrorHandler::errorAction property to be the


route site/error which refers to the error action in SiteController. We may use a different
route if needed.
We can write the error action like the following:

public function actionError()


{
if($error=Yii::app()->errorHandler->error)
$this->render('error', $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

Web service is a software system designed to support interoperable machine-to-machine interaction


over a network. In the context of Web applications, it usually refers to a set of APIs that can be
accessed over the Internet and executed on a remote system hosting the requested service. For
example, a Flex-based client may invoke a function implemented on the server side running a PHP-
based Web application. Web service relies on SOAP as its foundation layer of the communication
protocol stack.
Yii provides CWebService and CWebServiceAction to simplify the work of implementing Web
service in a Web application. The APIs are grouped into classes, called service providers. Yii will
generate for each class aWSDL specification which describes what APIs are available and how they
should be invoked by client. When an API is invoked by a client, Yii will instantiate the corresponding
service provider and call the requested API to fulfill the request.
Note: CWebService relies on the PHP SOAP extension. Make sure you have enabled it before
trying the examples displayed in this section.

1. Defining Service Provider 


As we mentioned above, a service provider is a class defining the methods that can be remotely
invoked. Yii relies on doc comment and class reflection to identify which methods can be remotely
invoked and what are their parameters and return value.
Let's start with a simple stock quoting service. This service allows a client to request for the quote of
the specified stock. We define the service provider as follows. Note that we define the provider
classStockController by extending CController. This is not required. We will explain why we do so
shortly.

class StockController extends CController


{
/**
* @param string the symbol of the stock
* @return float the stock price
* @soap
*/
public function getPrice($symbol)
{
$prices=array('IBM'=>100, 'GOOGLE'=>350);
return isset($prices[$symbol])?$prices[$symbol]:0;
//...return stock price for $symbol
}
}

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.

class StockController extends CController


{
public function actions()
{
return array(
'quote'=>array(
'class'=>'CWebServiceAction',
),
);
}

/**
* @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.

3. Consuming Web Service 


To complete the example, let's create a client to consume the Web service we just created. The
example client is written in PHP, but it could be in other languages, such as Java, C#, Flex, etc.

$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:

 str/string: maps to xsd:string;


 int/integer: maps to xsd:int;
 float/double: maps to xsd:float;
 bool/boolean: maps to xsd:boolean;
 date: maps to xsd:date;
 time: maps to xsd:time;
 datetime: maps to xsd:dateTime;
 array: maps to xsd:string;
 object: maps to xsd:struct;
 mixed: maps to xsd:anyType.
If a type is not any of the above primitive types, it is considered as a composite type consisting of
properties. A composite type is represented in terms of a class, and its properties are the class'
public member variables marked with @soap in their doc comments.
We can also use array type by appending [] to the end of a primitive or composite type. This would
specify an array of the specified type.
Below is an example defining the getPosts Web API which returns an array of Post objects.

class PostController extends CController


{
/**
* @return Post[] a list of posts
* @soap
*/
public function getPosts()
{
return Post::model()->findAll();
}
}

class Post extends CActiveRecord


{
/**
* @var integer post ID
* @soap
*/
public $id;
/**

112
Ghid de utilizare Yii

* @var string post title


* @soap
*/
public $title;

public static function model($className=__CLASS__)


{
return parent::model($className);
}
}

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.

class PostController extends CController


{
public function actions()
{
return array(
'service'=>array(
'class'=>'CWebServiceAction',
'classMap'=>array(
'Post'=>'Post', // or simply 'Post'
),
),
);
}
......
}

6. Intercepting Remote Method Invocation 


By implementing the [IWebServiceProvider] interface, a sevice provider can intercept remote method
invocations. In [IWebServiceProvider::beforeWebMethod], the provider may retrieve the
current CWebServiceinstance and obtain the the name of the method currently being requested
via CWebService::methodName. It can return false if the remote method should not be invoked for
some reason (e.g. unauthorized access).

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.

Yii provides support for I18N in several aspects.

 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.

1. Locale and Language 


Locale is a set of parameters that defines the user's language, country and any special variant
preferences that the user wants to see in their user interface. It is usually identified by an ID
consisting of a language ID and a region ID. For example, the ID en_US stands for the locale of
English and United States. For consistency, all locale IDs in Yii are canonicalized to the format
of LanguageID or LanguageID_RegionID in lower case (e.g.en, en_us).
Locale data is represented as a CLocale instance. It provides locale-dependent information,
including currency symbols, number symbols, currency formats, number formats, date and time
formats, and date-related names. Since the language information is already implied in the locale ID,
it is not provided by CLocale. For the same reason, we often interchangeably using the term locale
and language.
Given a locale ID, one can get the corresponding CLocale instance
by CLocale::getInstance($localeID)or CApplication::getLocale($localeID).
Info: Yii comes with locale data for nearly every language and region. The data is obtained
fromCommon Locale Data Repository (CLDR). For each locale, only a subset of the CLDR data is
provided as the original data contains much rarely used information.
For an Yii application, we differentiate its target language from source language. The target
language is the language (locale) of the users that the application is targeted at, while the source
language refers to the language (locale) that the application source files are written in.
Internationalization occurs only when the two languages are different.
One can configure target language in the application configuration, or change it dynamically before
any internationalization occurs.
Tip: Sometimes, we may want to set the target language as the language preferred by a user
(specified in user's browser preference). To do so, we can retrieve the user preferred language ID
using CHttpRequest::preferredLanguage.
2. Translation 

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.

Yii::t('yii', 'Path alias "{alias}" is redefined.',


array('{alias}'=>$alias))

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:

1. Call Yii::t() at appropriate places;


2. Create PHP translation files as protected/messages/LocaleID/CategoryName.php. Each
file simply returns an array of message translations. Note, this assumes you are using the
defaultCPhpMessageSource to store the translated messages.
3. Configure CApplication::sourceLanguage and CApplication::language.
Tip: The yiic tool in Yii can be used to manage message translations when CPhpMessageSourceis
used as the message source. Its message command can automatically extract messages to be
translated from selected source files and merge them with existing translations if necessary.
Starting from version 1.0.10, when using CPhpMessageSource to manage message source,
messages for an extension class (e.g. a widget, a module) can be specially managed and used. In
particular, if a message belongs to an extension whose class name is Xyz, then the message
category can be specified in the format ofXyz.categoryName. The corresponding message file will
be assumed to beBasePath/messages/LanguageID/categoryName.php, where BasePath refers
to the directory that contains the extension class file. And when using Yii::t() to translate an
extension message, the following format should be used, instead:

Yii::t('Xyz.categoryName', 'message to be translated')

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:

'n==1#one book|n>1#many books'

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

As a shortcut notation, if an expression is a number, it will be treated as n==Number. Therefore, the


above translated message can be also be written as:

'1#one book|n>1#many books'

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.

3. Date and Time Formatting 


Date and time are often in different formats in different countries or regions. The task of date and
time formatting is thus to generate a date or time string that fits for the specified locale. Yii
provides CDateFormatterfor this purpose.
Each CDateFormatter instance is associated with a target locale. To get the formatter associated
with the target locale of the whole application, we can simply access the dateFormatter property of
the application.
The CDateFormatter class mainly provides two methods to format a UNIX timestamp.
 format: this method formats the given UNIX timestamp into a string according to a
customized pattern (e.g.$dateFormatter->format('yyyy-MM-dd',$timestamp)).
 formatDateTime: this method formats the given UNIX timestamp into a string according to a
pattern predefined in the target locale data (e.g. short format of date, long format of time).
4. Number Formatting 
Like data and time, numbers may also be formatted differently in different countries or regions.
Number formatting includes decimal formatting, currency formatting and percentage formatting. Yii
providesCNumberFormatter for these tasks.
To get the number formatter associated with the target locale of the whole application, we can
access thenumberFormatter property of the application.
The following methods are provided by CNumberFormatter to format an integer or double value.
 format: this method formats the given number into a string according to a customized pattern
(e.g.$numberFormatter->format('#,##0.00',$number)).

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.

Short PHP Tags


Short PHP tags are shortcuts to writing PHP expressions and statements in a view. The expression
tag <%= expression %> is translated into <?php echo expression ?>; while the statement tag <%
statement %>to <?php statement ?>. For example,
118
Ghid de utilizare Yii

<%= CHtml::textField($name,'value'); %>


<% foreach($models as $model): %>

is translated into

<?php echo CHtml::textField($name,'value'); ?>


<?php foreach($models as $model): ?>

Component Tags
Component tags are used to insert a widget in a view. It uses the following syntax:

<com:WidgetClass property1=value1 property2=value2 ...>


// body content for the widget
</com:WidgetClass>

// a widget without body content


<com:WidgetClass property1=value1 property2=value2 .../>

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,

<com:CCaptcha captchaAction="captcha" showRefreshButton={false} />

would be translated as

<?php $this->widget('CCaptcha', array(


'captchaAction'=>'captcha',
'showRefreshButton'=>false)); ?>

Note: The value for showRefreshButton is specified as {false} instead of "false" because the


latter means a string instead of a boolean.

Cache Tags
Cache tags are shortcuts to using fragment caching. Its syntax is as follows,

<cache:fragmentID property1=value1 property2=value2 ...>


// content being cached

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

<?php if($this->cache('profile', array('duration'=>3600))): ?>


// user profile information here
<?php $this->endCache(); endif; ?>

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

<?php $this->beginClip('clipID'); ?>


// content for this clip
<?php $this->endClip(); ?>

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,

php entryScript.php CommandName Param0 Param1 ...

where CommandName refers to the command name which is case-insensitive,


and Param0, Param1 and so on are parameters to be passed to the command instance.
The entry script for a console application is usually written like the following, similar to that in a Web
application,

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.

Writing a command class mainly involves implementing the CConsoleCommand::run method.


Command line parameters are passed as an array to this method. Below is an example:

121
Ghid de utilizare Yii

class EmailCommand extends CConsoleCommand


{
public function run($args)
{
$receiver=$args[0];
// send email to $receiver
}
}

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,

<?php $this->beginWidget('CHtmlPurifier'); ?>


...display user-entered content here...
<?php $this->endWidget(); ?>

2. Cross-site Request Forgery Prevention 


Cross-Site Request Forgery (CSRF) attacks occur when a malicious web site causes a user's web
browser to perform an unwanted action on a trusted site. For example, a malicious web site has a
page that contains an image tag whose src points to a banking
site: http://bank.example/withdraw?transfer=10000&to=someone. If a user who has a login
cookie for the banking site happens to visit this malicous page, the action of transferring 10000
dollars to someone will be executed. Contrary to cross-site, which exploits the trust a user has for a
particular site, CSRF exploits the trust that a site has for a particular user.
To prevent CSRF attacks, it is important to abide to the rule that  GET requests should only be
allowed to retrieve data rather than modify any data on the server. And for POST requests, they
should include some random value which can be recognized by the server to ensure the form is
submitted from and the result is sent back to the same origin.
Yii implements a CSRF prevention scheme to help defeat POST-based attacks. It is based on storing
a random value in a cookie and comparing this value with the value submitted via the POST request.
By default, the CSRF prevention is disabled. To enable it, configure the CHttpRequest application
component in the application configuration as follows,

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.

There are several countermeasures to prevent cookies from being attacked.

 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.

Cookie validation is disabled by default. To enable it, configure the CHttpRequest application


component in theapplication configuration as follows,

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:

// retrieve the cookie with the specified name


$cookie=Yii::app()->request->cookies[$name];
$value=$cookie->value;
......
// send a cookie
$cookie=new CHttpCookie($name,$value);

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.

1. Enabling APC Extension 


Enabling the PHP APC extension is perhaps the easiest way to improve the overall performance of
an application. The extension caches and optimizes PHP intermediate code and avoids the time
spent in parsing PHP scripts for every incoming request.
2. Disabling Debug Mode 
Disabling debug mode is another easy way to improve performance. An Yii application runs in debug
mode if the constant YII_DEBUG is defined as true. Debug mode is useful during development stage,
but it would impact performance because some components cause extra burden in debug mode. For
example, the message logger may record additional debug information for every message being
logged.
3. Using yiilite.php 
When the PHP APC extension is enabled, we can replace yii.php with a different Yii bootstrap file
namedyiilite.php to further boost the performance of an Yii-powered application.
The file yiilite.php comes with every Yii release. It is the result of merging some commonly used
Yii class files. Both comments and trace statements are stripped from the merged file. Therefore,
using yiilite.phpwould reduce the number of files being included and avoid execution of trace
statements.
Note, using yiilite.php without APC may actually reduce performance,
because yiilite.php contains some classes that are not necessarily used in every request and
would take extra parsing time. It is also observed that using yiilite.php is slower with some
server configurations, even when APC is turned on. The best way to judge whether to
use yiilite.php or not is to run a benchmark using the included hello worlddemo.
4. Using Caching Techniques 
As described in the Caching section, Yii provides several caching solutions that may improve the
performance of a Web application significantly. If the generation of some data takes long time, we
can use the data cachingapproach to reduce the data generation frequency; If a portion of page
remains relatively static, we can use thefragment caching approach to reduce its rendering
frequency; If a whole page remains relative static, we can use the page caching approach to save
the rendering cost for the whole page.
125
Ghid de utilizare Yii

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

<?php echo CHtml::script(


CGoogleApi::load('jquery','1.3.2') . "\n" .
CGoogleApi::load('jquery.ajaxqueue.js') . "\n" .
CGoogleApi::load('jquery.metadata.js')
); ?>
......
</head>

127
Ghid de utilizare Yii

128

S-ar putea să vă placă și