ghid utilizare yii ro

150
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. 1

Upload: eugen-pintea

Post on 03-Jul-2015

641 views

Category:

Documents


6 download

TRANSCRIPT

Page 1: Ghid Utilizare Yii Ro

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

Page 2: Ghid Utilizare Yii Ro

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

Page 3: Ghid Utilizare Yii Ro

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

Page 4: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

Pagina contact cu afisare erori la intrarea datelor

4

Page 5: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

Pagina contact cu afisare succes

Pagina de logare

5

Page 6: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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

Page 7: Ghid Utilizare Yii Ro

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'

7

Page 8: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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

system/ contine fisierele view pt erorile sistemului

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

8

Page 9: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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

9

Page 10: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

mkdir D:/wwwroot/myproject/protected/views/user

generate update.php

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.

10

Page 11: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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:

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

Page 12: Ghid Utilizare Yii Ro

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

Page 13: Ghid Utilizare Yii Ro

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

Page 14: Ghid Utilizare Yii Ro

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

Page 15: Ghid Utilizare Yii Ro

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

Page 16: Ghid Utilizare Yii Ro

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

Page 17: Ghid Utilizare Yii Ro

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

Page 18: Ghid Utilizare Yii Ro

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

Page 19: Ghid Utilizare Yii Ro

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

Page 20: Ghid Utilizare Yii Ro

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

Page 21: Ghid Utilizare Yii Ro

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

21

Page 22: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

asigura ca utilizatorul este autentificat inainte de a executa action-ul cerut; sau un filtru de

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:

22

Page 23: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

class PostController extends CController

{

......

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

23

Page 24: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

exemplu, intr-o pagina login, putem folosi un model form care va contine numele utilizatorului si

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

24

Page 25: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

......aici se defineste footer-ul......

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

25

Page 26: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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

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

Page 27: Ghid Utilizare Yii Ro

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)

{

$this->raiseEvent('onClicked', $event);

27

Page 28: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

}

$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

Page 29: Ghid Utilizare Yii Ro

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

Page 30: Ghid Utilizare Yii Ro

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

Page 31: Ghid Utilizare Yii Ro

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

Page 32: Ghid Utilizare Yii Ro

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.

32

Page 33: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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:

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

Page 34: Ghid Utilizare Yii Ro

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

Page 35: Ghid Utilizare Yii Ro

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

Page 36: Ghid Utilizare Yii Ro

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

Page 37: Ghid Utilizare Yii Ro

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

Page 38: Ghid Utilizare Yii Ro

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

Page 39: Ghid Utilizare Yii Ro

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

Page 40: Ghid Utilizare Yii Ro

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

Page 41: Ghid Utilizare Yii Ro

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

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

41

Page 42: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

)

Daca un model nu se potriveste cu vreun scenariu (spre exemplu este folosit doar intr-un scenariu,

sau toate scenariile impart acelasi set de atribute sigure) valoarea returnata poate fi simplificata sub

forma unui singur string:

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

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

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

42

Page 43: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

);

}

Prin urmare, prima regula va fi aplicata in toate scenariile, in timp ce urmatoarele doua reguli vor fi

aplicate doar in cazul scenariului register.

Nota: Validarea in functie de scenariu este disponibila incepand cu versiunea 1.0.1 a Yii.

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

$this->redirect(Yii::app()->user->returnUrl);

}

43

Page 44: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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

Page 45: Ghid Utilizare Yii Ro

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

Page 46: Ghid Utilizare Yii Ro

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

Page 47: Ghid Utilizare Yii Ro

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

Page 48: Ghid Utilizare Yii Ro

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

Page 49: Ghid Utilizare Yii Ro

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

Page 50: Ghid Utilizare Yii Ro

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

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

50

Page 51: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

$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

Page 52: Ghid Utilizare Yii Ro

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

}

Active Record52

Page 53: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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(

'db'=>array(

'class'=>'system.db.CDbConnection',

53

Page 54: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

'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

Page 55: Ghid Utilizare Yii Ro

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.

$post=new Post;

55

Page 56: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

$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

Page 57: Ghid Utilizare Yii Ro

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

Page 58: Ghid Utilizare Yii Ro

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.

58

Page 59: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

$post=Post::model()->findByPk(10);

$post->title='Titlul nou';

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

59

Page 60: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

// sterge randurile care se potrivesc cu cheile primare si cu conditia

specificata

Post::model()->deleteByPk($pk,$condition,$params);

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

60

Page 61: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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

presupunand ca ele instantele apartin aceleiasi clase AR. O cale mai simpla, totusi, este sa

apelam CActiveRecord::equals().

Info: Spre deosebire de implementarile AR din alte framework-uri, Yii are suport pentru chei primare

compuse in instantele sale AR. O cheie primara compusa este formata din doua sau mai multe

coloane. Valoarea cheii primare este reprezentata in Yii ca un array.

Proprietatea primaryKeydefineste valoarea cheii primare a unei instante AR.

9. Customizare CActiveRecord pune la dispozitie cateva metode care pot fi suprascrise in clasele derivate pentru a

schimba fluxul de lucru.

beforeValidate si afterValidate: acestea sunt apelate inainte si dupa validare.

beforeSave si afterSave: acestea sunt apelate inainte si dupa salvarea unei instante AR.

beforeDelete si afterDelete: acestea sunt apelate inainte si dupa ce o instanta AR este stearsa.

afterConstruct: aceasta este apelata pentru fiecare instanta AR creata cu operatorul new.

afterFind: aceasta este apelata pentru fiecare instanta AR creata ca rezultat al cererii.

10. Folosirea tranzactiilor cu AR Fiecare instanta AR contine o proprietate cu numele dbConnection care este o

instanta CDbConnection. De aceea, putem folosi tranzactii, feature pus la dispozitie de Yii DAO in

cazul in care se doreste folosirea lor cu AR.

$model=Post::model();

$transaction=$model->dbConnection->beginTransaction();

try

{

// find si save sunt doi pasi care pot fi intrerupti de o alta cerere

// de aceea, folosirea unei tranzactii asigura consistenta si integritate

datelor

$post=$model->findByPk(10);

$post->title='Titlu nou post';

$post->save();

$transaction->commit();

}

catch(Exception $e)

{

$transaction->rollBack();

}

61

Page 62: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

Active Record relational

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

Page 63: Ghid Utilizare Yii Ro

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

Page 64: Ghid Utilizare Yii Ro

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

ssName 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

Page 65: Ghid Utilizare Yii Ro

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

Page 66: Ghid Utilizare Yii Ro

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

Page 67: Ghid Utilizare Yii Ro

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

Page 68: Ghid Utilizare Yii Ro

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

Page 69: Ghid Utilizare Yii Ro

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

Page 70: Ghid Utilizare Yii Ro

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

Page 71: Ghid Utilizare Yii Ro

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.

71

Page 72: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

Cache de fragmente

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

Page 73: Ghid Utilizare Yii Ro

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

Page 74: Ghid Utilizare Yii Ro

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

Page 75: Ghid Utilizare Yii Ro

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

Page 76: Ghid Utilizare Yii Ro

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

Page 77: Ghid Utilizare Yii Ro

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.

2. Widget 77

Page 78: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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

Page 79: Ghid Utilizare Yii Ro

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

Page 80: Ghid Utilizare Yii Ro

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

Page 81: Ghid Utilizare Yii Ro

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

Page 82: Ghid Utilizare Yii Ro

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

Page 83: Ghid Utilizare Yii Ro

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

Page 84: Ghid Utilizare Yii Ro

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

Page 85: Ghid Utilizare Yii Ro

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

Page 86: Ghid Utilizare Yii Ro

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

Page 87: Ghid Utilizare Yii Ro

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

Page 88: Ghid Utilizare Yii Ro

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

88

Page 89: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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

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

Page 90: Ghid Utilizare Yii Ro

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

90

Page 91: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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.

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

Page 92: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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

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

92

Page 93: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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

{

......

93

Page 94: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

public function filters()

{

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

94

Page 95: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

rezultatul autorizarii. Daca aceasta regula este o regula allow, action-ul poate fi executat; daca

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

95

Page 96: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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.

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

96

Page 97: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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.

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(

97

Page 98: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

'class'=>'CDbConnection',

'connectionString'=>'sqlite:path/to/file.db',

),

'authManager'=>array(

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

98

Page 99: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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

$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

99

Page 100: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

by the createPostoperation. We then call CWebUser::checkAccess to perform the access

checking:

if(Yii::app()->user->checkAccess('createPost'))

{

// create post

}

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

100

Page 101: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

defines two roles, "authenticated" and "guest", which effectively apply to authenticated users and

guest users, respectively.

$bizRule='return !Yii::app()->user->isGuest;';

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

101

Page 102: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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.

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/

102

Page 103: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

main.php

site/

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.

103

Page 104: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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

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

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

104

Page 105: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

'emails'=>'[email protected]',

),

),

),

),

)

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

105

Page 106: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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.

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:

106

Page 107: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

Yii::beginProfile('blockID');

...code block being profiled...

Yii::endProfile('blockID');

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

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 

107

Page 108: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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

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.

108

Page 109: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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(

'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

109

Page 110: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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

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

}

110

Page 111: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

}

In the above, we declare the method getPrice to be a Web service API by marking it with the

tag @soap in its doc comment. We rely on doc comment to specify the data type of the input

parameters and return value. Additional APIs can be declared in the similar way.

2. Declaring Web Service Action 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.111

Page 112: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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

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

{

112

Page 113: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

/**

* @var integer post ID

* @soap

*/

public $id;

/**

* @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'

),

),

);

}

......

}

113

Page 114: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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

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.

114

Page 115: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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

Page 116: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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

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

116

Page 117: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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.

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 

117

Page 118: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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

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.

118

Page 119: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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,

<%= 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)); ?>

119

Page 120: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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

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

120

Page 121: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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

--->

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.

121

Page 122: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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:

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

122

Page 123: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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.

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

Page 124: Ghid Utilizare Yii Ro

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;

124

Page 125: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

......

// send a cookie

$cookie=new CHttpCookie($name,$value);

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

125

Page 126: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

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.

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,

126

Page 127: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

$cs=Yii::app()->clientScript;

$cs->scriptMap=array(

'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

Page 128: Ghid Utilizare Yii Ro

Ghid de utilizare Yii

128