Creeaza.com - informatii profesionale despre


Evidentiem nevoile sociale din educatie - Referate profesionale unice
Acasa » scoala » informatica
Realizarea unui magazin virtual

Realizarea unui magazin virtual


Realizarea unui magazin virtual

1   Introducere. Structura 2   start.php. MySite. FrontSite 3   MyLogin. FrontLogin 4   MyDb. MyTable 5   Copiii clasei MyTable 6   MyPage. MainPage 7   register.php. MyForm. MultiStepForm. RegisterForm 8   product.php 9   orderView.php. Procesarea comenzii 10   Interfata de administrare - x

Acest magazin virtual este fictiv, si are in principal un scop didactic, insa contine toate elementele de baza ale unei aplicatii de comert electronic reale: produse afisate pe categorii, inregistrare utilizatori, cos de cumparaturi (shopping cart), adaugarea produselor in cos, procesarea comenzilor, interfata de administrare, etc.

Pentru a extrage cunostintele din aceasta aplicatie, trebuie sa analizezi, sa testezi, sa modifici, si sa intelegeti codul sursa al diferitelor clase/fisiere ale aplicatiei.



Acest proces se numeste si 'debug', si se poate face cu instructiunile die() - pentru oprirea executiei intr-un loc al scriptului, print_r() pentru afisarea array-urilor, sau var_dump(), astfel incat sa poti urmari firul executiei, si valoarea diferitelor variabile in diferite puncte ale executiei. Acest proces se desfasoara mai eficient cu un program specializat, numit debugger. Debuggerul este de obicei integrat intr-un editor. Editoare PHP ce au debugger sunt: phpDesigner si Zend Studio. Un debugger are capacitatea de a urmari pas cu pas firul executiei, cu posibilitatea de a intra sau nu in interiorul anumitor functii/metode. Daca firul executiei este prea lung, si te intereseaza doar anumite puncte ale programului, se pot seta 'breakpoints', puncte in care debuggerul se opreste. La fiecare instructiune la care debuggerul se opreste, se pot vedea toate variabilele curente, sau doar anumite variabile (facilitatea 'watches'). Debuggerul cu care lucreaza phpDesigner, si care poate fi folosit pentru anumite scopuri si separat, este Xdebug (www.xdebug.org).

Capitolul nu va explica tot codul sursa, insa vor fi explicate elementele principale si modul in care acestea se leaga. Restul poate fi dedus din analiza sursei.

Structura

Structura proiectului este urmatoarea: laptops

  • logs
  • sessions
  • site
    • common
    • css
    • img
    • js
    • lib
    • products
    • templates
    • .fisiere .php .
    • x
      • css
      • lib
      • templates
      • fisiere .php

Directorul aplicatiei este laptops. Directorul logs va contine logurile de erori. Directorul sessions va contine fisierele de sesiune. Aceste doua directoare nu trebuie sa fie in interiorul Document Root. Document Root, radacina aplicatiei web, este directorul site (echivalent cu public_html sau www din online). De exemplu, pentru cererea www.example.com/index.php , index.php va fi cautat in directorul site.

Aplicatia este deci in interiorul folderului site.

css va contine fisierul/fisierele css, js va contine eventuale fisiere javascript, img contine imaginile websiteului, insa NU imaginile produselor, ce vor fi tinute separat, in products.
Paginile website-ului, sunt fisiere .php direct in folderul site. Ex: index.php, about.php, orderView.php, etc.

Insa, codul sursa din interiorul acestor scripturi este destul de redus cantitativ, el fiind distribuit in clasele sau alte fisiere din interiorul directoarelor lib si common.

Scopul urmarit este pastrarea majoritarii codului sursa in clase (sau functii), astfel incat, sa pot organiza propriile librarii, si sa refolosesc cat mai mult cod de la un proiect la altul. In general, in lib se pastreaza codul sursa specific proiectului actual, iar in common sunt librarii ce pot fi folosite si la alte proiecte.

Un motiv in plus pentru care fisierele .php din site au cod putin, este ca partea de afisare este pastrata integral in sabloane, fisierele din folderul templates. Rolul scripturilor ramane deci de a apela metodele potrivite din clasele potrivite pentru a indeplini sarcinile necesare, si de a trimite mai departe rezultatele sabloanelor, prin intermediul variabilelor.

Aplicatia poate fi de fapt impartita in doua aplicatii: aplicatia principala (pe care o denumim pe scurt front, si aplicatia de administrare ce este in interiorul folderului x, si o denumim simplu x. Aceasta denumire o folosim de fapt in clasele specifice unei aplicatii sau celeilalte: FrontSite vs XSite, FrontLogin, vs XLogin, etc.

2   start.php. MySite. FrontSite
Fiecare script din aplicatie include fisierul start.php. Acest fisier are rolul de a oferi prin functia __autoload() locatiile in care se afla clasele proiectului. Astfel incat, la folosirea unei clase, __autoload() se ocupa de gasirea acesteia. In plus, se includ si eventuale alte librarii organizate in functii (situate de obicei in common). userPage.php este pagina utilizatorului. Incepe similar cu orderDetails.php, si anume extrage prin getFullData() datele complete ale utilizatorului, si le transmite apoi sablonului userPage.tpl.php prin variabila de sablon $userDetails.

Apoi, folosind metodele getListByUser($uid) si getTotalByUser($uid) din clasa Orders, extrag comenzile (trimise) pentru utilizatorul respectiv, si totalul lor.

Folosesc apoi in sablon $userDetails ce contine datele complete (si deja formatate, in cod html) ale utilizatorului, apoi $ordersData, si $ordersTotal pentru afisarea comenzilor acestui utilizator si totalul acestor comenzi.

Orice calcul, oricat de simplu (cum ar fi totalul comenzilor) il realizez in interiorul claselor specializate (Orders in acest caz) si nu in sabloane, astfel incat sa tin cat pot de separat, logica de prezentare de logica aplicatiei.

userPage.php si orderDetails.php se completeaza astfel reciproc: ambele ofera detalii complete despre utilizator, in aceeasi forma, insa in userPage.php se afiseaza lista completa a comenzilor pentru utilizator, iar in orderDetails.php pot vedea detaliile complete ale unei comenzi.

Pe de alta parte, in acest fisier se creeaza obiectul $site, din clasa FrontSite (respectiv XSite) a carei metoda - start() - va fi apelata la inceputul fiecarui script.

Intr-un fisier inclus, de exempu start.php din interiorul folderului lib, referirea la alte fisiere se va face relativ la fisierul parinte, nu la fisierul inclus. De exemplu, daca in index.php includ lib/start.php, in start.php ma refer la alte fisiere cu calea relativa la index.php. Daca vreau sa includ alt fisier din lib, folosesc calea lib/f-url.php.

Folosesc acelasi start.php pentru aplicatia principala (front) cat si pentru administrare (x). Astfel, trebuie sa le diferentiez pentru a gasi caile corecte catre librarii, si pentru a initializa FrontSite sau XSite. Daca includ din index.php , din aplicatia principala, lib/start.php, atunci lib/start.php va fi disponibil, pentru ca relativ la index.php indica un fisier existent. Daca insa din x/index.php voi include ../lib/start.php, atunci lib/start.php nu exista, pentru ca relativ la x/index.php nu am un start.php in directorul lib. Astfel, fac diferenta intre locul din care includ, si folosesc si variabila $rel, pentru a include corect clasele aplicatiei din lib si common din interiorul scripturilor din x. Dupa cum se observa, in x, folosesc atat fisiere din site/lib cat si din site/x/lib.

MySite

Principalul scop al acestei clase este de a initia aplicatia, prin: configurarea aplicatiei (doConfig()), managementul erorilor (handleErrors()), conexiunea la baza de date ( dbConnect()), si initializarea obiectului $login, (din clasa MyLogin sau o clasa parinte) ce va porni sesiunea si eventual se va ocupa de login-ul efectiv si autorizare.

Aceasta clasa este abstracta, avand metoda abstracta doConfig() deci o vom extinde prin clasa FrontSite, astfel incat sa punem la dispozitia aplicatiei proprietatea config avand toate datele de configurare.

Clasa MySite nu se ocupa in mod direct nici de conexiunea la baza de date, nici de managementul erorilor, nici de managementul sesiunii (login) sau de astfel de activitati specializate. In schimb, deleaga aceste activitati catre clasele specializate. Conexiunea se realizeaza printr-un obiect $db din clasa MyDb, managementul erorilor este realizat prin clasa MyErrorHandler, iar login-ul prin clasa MyLogin. Clasa MySite implementeaza interfata ArrayAccess, astfel incat sa pot accesa valorile din proprietatea $config, prin intermediul obiectului ($site) folosind sintaxa pentru array - [].

O metoda utila din aceasta clasa este isOnline(), metoda ce verifica daca aplicatia este rulata online (in productie) sau offline. In functie de acest lucru, cativa parametri de configurare se schimba, si in plus proprietatea $debug este implicit 1 offline. Aceasta proprietate este folosita de clasele de management al erorilor pentru a regla modul de afisare a erorilor.

FrontSite

FrontSite este clasa ce extinde MySite, si initializeaza paginile aplicatiei. Aici au fost suprascrise metodele doConfig() pentru configurare, si checkLogin() ce creeaza un obiect de tip FrontLogin, ce va fi apoi pastrat ca proprietate a obiectelor FrontSite, proprietate numita $login.

3   MyLogin. FrontLogin
MyLogin este clasa cu urmatoarele atributii:

  • pornirea sesiunii
  • stergerea sesiunii
  • administrarea formularului de login
  • procesarea formularului de login: autentificarea si login-ul efectiv

Un obiect din clasa MyLogin (respectiv FrontLogin sau XLogin) este creat in metoda start() a clasei MySite.

In constructorul clasei MyLogin, setez proprietati ale clasei ce reprezinta optiuni ale sesiunii (si cookie-ului de sesiune) ($this->name - numele sesiunii, $this->seconds - durata sesiunii, etc) si apoi pornesc sesiunea cu ajutorul metodei sessionStart().


In plus, daca utilizatorul nu este logat, realizez un obiect din clasa LoginForm, ce reprezinta un obiect de tip MyForm, util pentru crearea formularelor, si il setez ca proprietate obiectului curent.

Apelez apoi metoda make() ce actioneaza doar daca formularul a fost apasat, si in acest caz, realizeaza o validare a formularului. Daca validarea nu reuseste, retinem eroarea in proprietatea $this->error. Altfel, incercam autentificarea folosind metoda authenticate() ce verifica existenta unui utilizator cu parola respectiva in baza de date. Daca autentificarea esueaza, salvam eroarea tot in proprietatea $this->error.


Daca autentificarea reuseste, apelam metoda login() folosind ca parametru datele extrase pentru acel utilizator ($udata), si setam variabilele de sesiune. Variabilele de sesiune pe care le setam vor fi obtinute de fapt prin clasa FrontLogin, ce extinde MyLogin. Metoda getMainVar() va returna numele variabilei principale pe care o setam in sesiune, variabila pe care o folosim si in metoda isLogged() pentru a verifica daca utilizatorul curent este logat. Metoda getVarsToSet() va furniza restul variabilelor de care e posibil sa avem nevoie in sesiune. Bineinteles, numele acestor variabile, trebuie sa corespunda cu numele unor coloane din tabelul users, astfel incat sa poata fi extrasa corect informatia.


Metoda logout() va realiza stergerea sesiunii, si redirectarea catre o anumita pagina, dupa ce utilizatorul va fi delogat.

4   MyDb. MyTable

MyDb

MyDb este o clasa ce contine ca principale metode connect() si query() si ca principala proprietate o instanta a clasei MySQLi. Scopul acestei clase este de a trata erorile la conectare sau la executarea unui query, si de a pune in evidenta o strategie OOP (un design pattern) ce forteaza o clasa sa fie instantiata o singura data, iar instanta respectiva sa poata fi accesata de oriunde din aplicatie (oarecum similar cu o variabila globala).

Acest design pattern se numeste Singleton si este obtinut in primul rand prin declararea constructorului ca fiind private, si printr-o metoda statica, pe care o numim de exemplu getInstance() ce este responsabila de crearea obiectului acestei clase. In plus, se foloseste proprietatea statica $instance, in care este tinut singurul obiect creat din aceasta clasa. Instantierea (din exteriorul clasei) se va face prin metoda getInstance() ce returneaza obiectul existent, sau daca nu exista, il creeaza. Astfel, in loc sa folosim variabila globala $db, voi obtine de oriunde din aplicatie un obiect al clasei MyDb folosind MyDb::getInstance()


MyTable

MyTable este o clasa ce reprezinta un tabel in baza de date, si pune la dispozitia programatorului 'scurtaturi' catre operatiile cele mai frecvent intalnite in lucrul cu MySQL din php:

  • operatiile de insert si update - metoda doData() genereaza si executa un query pentru insert sau update, avand ca principal parametru ($data) un array cu chei - numele coloanelor , si cu valori - valorile de inserat sau actualizat
  • extragerea coloanei - cheie primara - in cazul cel mai frecvent intalnit, in care cheia primara este compusa dintr-o singura coloana, in metoda setPkName(), executata inca din constructorul clasei, este determinata cheia primara a acestui tabel si pastrata in proprietatea $pkName
  • delete - metoda delete() ce primeste ca argument o valoare a unei coloane de tip cheie primara, va sterge din tabel randul respectiv
  • extragerea datelor dintr-un singur rand, pe baza unei conditii. Acest rand este returnat intr-un array de metoda fetchSingle() pe baza conditiei $condition, primita ca parametru
  • extragerea datelor din mai multe randuri, si returnarea acestora intr-un tablou ce contine la randul lui cate un tablou pentru fiecare rand. Metoda este fetchAll() si primeste ca parametru query-ul ce se executa pentru extragerea datelor.
  • extragerea datelor unui rand pe baza valorii unei chei primare - fetch()
  • extragerea unui array format din valorile a doua coloane - ce contine ca si chei valorile primei coloane, iar ca valori valorile celei de-a doua coloane. Aceasta metoda e utila in general cand avem nevoie de un array, o lista, (de exemplu de categorii) ce are ca si chei valoarile cheii primare pentru fiecare element din lista.
  • etc

Proprietatile acestei clase sunt $db (proprietate statica ce contine un obiect din clasa MyDb), $tableName (contine numele tabelului din baza de date), $pkName (initializata in constructor cu numele coloanei cheie primara) si $pkValue ce poate tine la un moment dat valoarea cheii primare pentru un anumit rand

5   Copiii clasei MyTable

Pentru tabelele din baza de date, ce reprezinta concepte/entitati de sine statatoare, ex: produse (products), comenzi (orders), produse comandate (op), voi avea daca este nevoie, clase ce extind clasa MyTable si implementeaza functionalitati specifice entitatilor respective. Este util uneori sa suprascriu metode din clasa MyTable, cum ar fi delete() sau doData(), metode ce in general vor avea si un apel catre metodele clasei de baza

De exemplu, metoda delete() din clasa Products suprascrie metoda delete() din MyTable, pentru ca inaintea stergerii randului din tabel ce reprezinta produsul respectiv, mai sunt cateva lucruri de facut. In primul rand, trebuie sa verificam daca acel produs nu exista intr-o comanda, deci verificam existenta unui rand in op (produse comandate). Daca produsul exista intr-o comanda, adica exista un rand in op cu acel productId, produsul nu poate fi sters pentru ca am afecta integritatea bazei de date, avand chei straine (valori pentru productId) ce indica randuri inexistente in tabelul products. Daca aceasta eroare NU este intalnita, extragem numele pozelor pentru produsul respectiv (picSmall si picLarge) pentru a sterge fisierele. Si abia apoi, apelam metoda parinte ce va sterge un rand din tabelul products.

Metoda doData() este util sa o suprascriem (de fapt sa o completam), cand mai avem nevoie de prelucrari asupra datelor ce vor fi adaugate/actualizate in tabel. De exemmplu, in doData() din Users, modific valoarea din tabloul $data cu cheia 'pass', astfel incat sa contina sha1($data['pass']), adica voi adauga hash-ul parolei in baza de date si nu parola insasi. Similar, la tabelele ce au o coloana dateAdded (in care pastrez data adaugarii randului), in doData voi completa automat coloana respectiva cu data curenta.

6   MyPage. MainPage

Clasa MyPage se foloseste de clasa Template (al carei obiect este tinut in proprietatea $oTemplate) pentru a construi forma finala a paginilor. Fiind abstracta, cu metoda output() abstracta, ea trebuie extinsa. Clasa MainPage o extinde si furnizeaza o metoda output() ce compune paginile finale.

Metoda fetch() are doi parametri: numele sablonului si variabilele sablonului, si va returna stringul rezultat prin executarea sablonului respectiv (vezi capitolul anterior).
Metode ca setTitle(), adCss() sau adJs() seteaza proprietati ale acestei clase, proprietati ce vor fi folosite in final de metoda output() pentru construirea paginii.

Metoda output() a clasei MainPage obtine principalele 'piese' necesare constructiei finale a paginii, prin apelarea metodelor sale, ce folosesc sabloanele pentru fiecare piesa in parte: getHeader(), getLeftCol() Metoda output() primeste ca prim parametru sablonul continutului paginii curente, si variabilele transmise acestui sablon.

Prin instructiunea $varsMainSite['content'] = $this->fetch($tpName, $vars); obtinem continutul paginii curente. Acest continut, impreuna cu celelalte piese, sunt transmise ca variabile sablonului mainsite.tpl.php ce 'imbraca' continutul paginii curente cu header.tpl.php, leftCol.tpl.php, etc.

Apoi, rezultatul astfel obtinut, este transmis ca variabila numita $body catre sablonul outer.tpl.php ce va 'imbraca' continutul obtinut pana acum, rezultand astfel forma finala a paginii. In concluzie, pagina este formata din trei straturi, ce se adauga la exterior, 'imbracand' rezultatul anterior:

  1. $content - obtinut din sablonul $tpName
  2. $outputMainSite (sau $body) - obtinut din salbonul mainsite.tpl.php (ce imbraca $content)
  3. $outputOuter (pagina finala) obtinuta din sablonul outer.tpl.php


7   register.php. MyForm. MultiStepForm. RegisterForm

MyForm

Realizarea unui formular, si operatiile intalnite in lucrul cu formulare sunt in responsabilitatea obiectelor de tip MyForm sau a claselor ce extind aceasta clasa.

Clasa MyForm contine in primul rand metode pentru generarea elementelor de formular: text(), password(), hidden(), radio(), ck(), etc. Apoi metodele begin() si end() ce vor returna codul html pentru inceputul si sfarsitul formularului. Proprietatea hiddenFormName, setata automat de forma hidden_$formName, este folosita de metoda pushed() pentru a verifica daca in cererea scriptului curent butonul formularului a fost apasat.

Apoi, logica generala a formularelor (daca s-a apasat valideaza, daca nu sunt erori proceseaza) este adapostita de metoda make(). Metoda process() nu este implementata in MyForm, dar poate fi implementata intr-o clasa ce extinde MyForm. Similar si functia validate(), poate fi implementata intr-o clasa copil, si in functie de valorile din $formData, va popula array-ul $errors cu mesaje de eroare ce vor fi afisate prin apelarea metodei outputErrors().

$formData este o proprietate foarte importanta a clasei MyForm. Este un array, identic cu $_GET daca formularul a fost apasat si metoda de transmitere este 'get' sau cu $_POST daca formularul este apasat si metoda de transmitere este 'post'. Inca din constructor, daca formularul a fost apasat, preluam datele din $_GET sau $_POST si le pastram in proprietatea (array) $formData. Astfel, daca in cererea curenta desi butonul formularului a fost apasat suntem nevoiti sa reafisam formularul, pentru ca are erori, proprietatea $formData ne va ajuta sa 'reumplem' formularul cu informatia completata de utilizator inainte sa apese butonul submit (in formularul cererii anterioare). De exemplu, in metoda text(), prin instructiunea $elementData = $this->formData[$name] preluam valoarea corespunzatoare campului curent din $formData, si o folosim pentru apoi la atributul value al tagului input, pentru a reafisa informatia in acel camp.

MultiStepForm, RegisterForm si register.php

MultiStepForm este o clasa ce faciliteaza realizarea formularelor pe mai multe pagini - in mai multi pasi. Ea primeste in constructor un obiect de tipul MyForm, pe care-l va folosi in administrarea formularului, si un array ce contine pasii respectivi.

In subclasele clasei MultiStepForm (in cazul nostru in RegisterForm) pot defini conditiile si ordinea in care se parcurg pasii formularului prin intermediul suprascrierii metodelor getNextStep() si getPrevStep().

Clasa MultiStepForm 'vede' formularul pe mai multe pagini ca un singur formular, pastrat in proprietatea $form de tipul MyForm, astfel ca in $this->form->formData se vor pastra toate datele completate pana atunci, indiferent ca au fost pe o singura pagina sau pe mai multe pagini.

Fiecare pas (formular pe o anumita pagina) are un camp de tip hidden prin care se identifica. De exemplu, la pasul1, voi avea <input type='hidden' name='step' value='1' />
Clasa MultiStepForm (sau subclasa ei mai precis) valideaza, proceseaza, sau ofera butoane 'inapoi' sau 'inainte' in functie de pasul la care este utilizatorul. Pasul respectiv este determinat din constructor prin metoda findOutCurrentStep() ce cauta in $form->formData elementul cu cheia 'step' si ii extrage valoarea. In proprietatea $steps, de tip $array , pastram pasii definiti. Folosind metodele getNextStep() si getPrevStep() determinam pasul anterior sau pasul urmator, insa aceste metode nu schimba efectiv pasul curent. Metodele ce schimba pasul sunt goNext() si goPrev() ce se folosesc de metodele getNextStep si getPrevStep() si apoi de metoda setCurrentStep() pentru a schimba efectiv pasul.

Actiunea se intampla in metoda make() ce se executa doar daca formularul a fost apasat. Aceeasi logica, se valideaza, de data asta validarea se va face prin metoda validate() a clasei RegisterForm (ce extinde MultiStepForm) iar erorile se salveaza in obiectul $form. Daca nu sunt erori, se apeleaza metoda saveState() ce salveaza datele formularului in variabile de sesiune, si apoi se apeleaza metoda fetchGoStep() ce determina pasul urmator in functie de butonul apasat: goNext sau goPrev.

Este de remarcat ca in aceeasi cerere (si in aceeasi executie a scriptului .php), pot fi doi pasi simultan, nu unul singur. De exemplu cand trec de la pasul1 la pasul2 (apas butonul inainte), in prima faza pasul va fi tot 1. Insa dupa ce se valideaza datele, in metoda make(), metoda fetchGoStep() va schimba pasul. Astfel, in metoda setCurrentStep() va fi setata proprietatea $this->currentStep cu pasul respectiv, se va inainta in array-ul $this->steps, si, in plus, se va seta un camp hidden cu noul pas, ce va fi scris in momentul cand se construieste formularul, in metoda $form->begin().

Metodele getNextButton() si getPrevButton() se ocupa de realizarea butoanelor 'inainte' si 'inapoi'. Ele se ocupa si de limite, astfel ca, la primul pas nu voi avea buton 'inapoi', iar la ultimul pas voi avea un buton 'process' a carui apasare va determina metoda fetchGoStep() sa returneze false, si metoda make() sa apeleze metoda process(). Metoda process() este suprascrisa in RegisterForm, insa este executata si metoda originala prin parent::process(). In aceasta metoda se folosesc datele de formular pentru adaugarea in baza de date, si se seteaza proprietatea process cu true.


In sablon, avem la dispozitie obiectul $form cu ajutorul caruia scriem formularul, si erorile obtinute prin validare (si salvate in $form->errors), si obiectul $m din clasa RegisterForm cu ajutorul caruia obtinem butonul inainte si inapoi, si starea formularului - procesat sau nu. $step este determinat din pagina principala (register.php) prin metoda $m->getCurrentStep().

8   product.php
Pagina produsului, product.php are doua scopuri:

  1. prezentarea produsului
  2. formular pentru adaugarea produsului in cosul de cumparaturi, adica in comanda curenta.

Pentru prezentarea produsului, realizam un obiect din clasa Products, si apelam metoda fetch() (mostenita din clasa MyTable) ce primeste ca parametru valoarea cheii primare, si returneaza informatiile randului extras intr-un array. Acel array este mai departe transmis sablonului, in variabila $data. Sablonul preia informatiile din $data si afiseaza produsul. Pe de alta parte, realizam un obiect de tip QtyForm ce extinde MyForm, obiect tinut in variabila $form, cu ajutorul caruia realizam formularul pentru cantitate. Acest formular are un singur camp text, pentru cantitate, si un buton, de tip imagine. Apelam la fiecare incarcare a scriptului metoda make() din a obiectului $form, metoda mostenita din MyForm. Metoda implementeaza binecunoscuta logica a formularelor: daca s-a apasat valideaza, daca s-a validat proceseaza. Validarea se realizeaza in QtyForm, si in cazul in care nu reuseste populeaza $form->errors cu mesajul erorii. In sablon, folosim $form->outputErrors() ce va afisa eroarea respectiva. Metoda outputErrors() din MyForm este astfel construita incat sa returneze sirul empty in caz ca nu sunt erori. Astfel, suntem scutiti de un if in interiorul sabloanelor.


In cazul in care nu sunt erori, metoda make() din MyForm va apela metoda process() care este suprascrisa in clasa QtyForm. Aceasta metoda creeaza un obiect de tip Orders, ce primeste la constructie un obiect de tip MyLogin, astfel incat sa extragem orderId-ul comenzii curente pentru utilizatorul logat. Apelam apoi metoda addOp($this->formData) ce apeleaza la randul ei metoda add($productId, $qty) pentru un obiect din clasa Op ce se ocupa de adaugarea produsului respectiv in tabelul op (ordered products, sau produse comandate) pentru comanda curenta. Daca exista deja un produs cu acel productId pentru comanda respectiva, metoda add() ii va actualiza cantitatea prin apelarea metodei updateQty().

In proprietatea $formData a obiectului QtyForm avem atat cantitatea produsului de adaugat, cat si productId-ul, pentru ca in formular, am adauga un camp hidden pentru productId, astfel incat sa-l retransmitem la reincarcarea paginii, prin get.

Metoda process redirecteaza apoi la pagina orderView.php - pagina de vizualizare a 'cosului de cumparaturi' sau a comenzii curente.

9   orderView.php. Procesarea comenzii

Scriptul orderView.php are trei scopuri principale:

  1. Afisarea continutului comenzii curente (pentru utilizatorul logat)
  2. Posibilitatea actualizarii cantitatilor pentru produsele din 'cos' -> prin apasarea butonului Actualizeaza
  3. Trimiterea comenzii

Astfel, afisarea comenzii este 'amestecata' cu un formular ce preia cantitatile fiecarui produs in parte.

Afisarea comenzii

Clasele Orders si Op lucreaza impreuna pentru a extrage informatiile comenzii curente, informatii ce sunt pastrate in variabila de sablon $odata. In site, obiectele din clasa Orders sunt initializate cu un parametru $login (tip MyLogin) in constructor, iar daca acest parametru este furnizat, verific in metoda checkLogin() daca utilizatorul este logat. In caz ca utilizatorul nu este logat, arunc o exceptie. Aceasta exceptie nu este prinsa in orderView.php deci se transforma in eroare fatala in cazul in care pagina este accesata de un utilizator nelogat.

Metoda countCurrentOp() din clasa Orders, ce deleaga responsabilitatea catre countCurrent() din clasa Op va numara produsele comandate pentru comanda curenta, si le va returna apoi in variabila $countOp din script. In sablon, folosim aceeasi variabila $countOp, si daca nu sunt produse in comanda curenta afisam mesajul corespunzator.

In script, (orderView.php) daca exista produse comandate, apelez $order->get Current OrderData() ce va transfera responsabilitatea catre $this->op->getOpData($orderId);

Metoda getOpData($orderId) apartine clasei Op, si va selecta din tabelul op produsele comenzii respective (cu valoarea din $orderId). In aceasta metoda, se fac calculele necesare afisarii sub forma de proforma: se calculeaza valoarea de pe fiecare rand, (pret * cantitate), se calculeaza tva-ul de pe fiecare rand, si apoi se calculeaza total valoare, total tva si total factura. Aceasta metoda returneaza un array cu 2 elemente. In primul element (avand cheia opData) este lista de produse, cu informatiile necesare fiecarui produs, iar in cel de-al doilea element (avand cheia total) avem, tot intr-un array, cele trei totaluri. Astfel, aceste informatii se intorc in $odata, iar scriptul principal orderView.php sau sablonul (orderView.tpl.php) are deja informatiile si calculele gata facute, avand responsabilitatea doar sa le afiseze.

Actualizarea cantitatilor

Formularul cantitatilor pentru fiecare produs in parte este generat cu ajutorul clasei OrderForm, ce extinde MyForm. In acest formular, campurile text sunt putin deosebite fata de cele intalnite pana acum, in sensul in care numele unui camp este de exemplu qty[9] sau qty[11] unde 9 si 11 reprezinta valori ale productId-ului produsului respectiv din lista de produse comandate. Astfel, in $_GET, $_POST si respectiv $formData, voi avea ca element cu cheia 'qty' un array in loc sa am o valoare. Array-ul va avea ca si chei productId-urile si ca valori cantitatile produselor respective.

Inainte sa apasam butonul formularului, metoda loadData() a clasei OrderForm, ce primeste ca argument datele comenzii curente incarcate anterior din baza de date, $odata, va construi array-ul $formData asa cum ar fi fost construit in mod natural din $_GET si $_POST, si va seta proprietatea $formData pentru obiectul curent (descendent al clasei MyForm). In metoda text() din MyForm, la instructiunea } elseif (strpos($name, '[') !== FALSE) { verific daca am astfel de campuri text ce transmit un sir de valori, si preiau numele campului si valoarea cheii dintr-o constructie gen qty[9], astfel incat sa verific valoarea $formData['qty'][9] si sa extrag de fapt valoarea completata in campul respectiv, valoarea scrisa in $elementData. Astfel, cantitatile extrase din baza de date, se completeaza automat in campurile corespunzatoare.

In metoda validate() din clasa OrderForm verific daca vreuna din valorile array-ului $formData['qty'] se evalueaza la false, deci daca una din cantitati este necompletata. Daca avem o eroare, o setez campului corespunzator, in array-ul $errors, iar campul corespunzator are, asa cum am precizat, un nume de forma qty[$productId].

Metoda process(), apelata daca formularul nu are erori, va actiona diferit, in functie de butonul ce se apasa. Daca se apasa butonul btnUpdate, se trece prin fiecare element al array-ului $formData['qty'], si se incearca setarea cantitatii pentru produsul respecti (cheia este $productId). Aici se foloseste obiectul $order din clasa Orders, setat anterior in script cu metoda setOrder(). Daca setarea cantitatii esueaza, genereaza o exceptie, care este ignorata. Astfel, in cazul unei probleme, cantitatea pur si simplu nu se actualizeaza. Informatia actualizata pentru aceasta comanda va fi apoi preluata in $odata si reafisata in formular prin loadData().

Procesarea comenzii

In metoda process() in caz ca nu se apasa butonul btnUpdate, procesam comanda. Procesarea comenzii se face prin metoda placeOrder() a clasei Orders, ce schimba valoarea comenzii, si tva-ul, calculate si extrase din $odata. Apoi, se schimba si coloana orderSent in 1, iar dateAdded primeste data curenta, astfel incat sa stim ca aceasta comanda este finala, si cand a fost trimisa.

10   Interfata de administrare - x

10.1   start.php. XSite.php. XLogin.php

10.2   MyCrud.php - create, read, update, delete

10.3   cats.php - CatsCrud

10.4   products.php - ProductsCrud

10.5   orders.php
10.6   users.php
10.7   orderDetails.php
10.8   userPage.php

10.1   start.php. XSite.php. XLogin.php

Scripturile din interfata de administrare (numita x pe scurt), impart cu scripturile din site-ul principal functionalitatea din clasele/functiile deja existente. In plus, sunt cateva clase, folosite doar aici, ce stau in x/lib/.

Asa cum am vazut anterior, se foloseste acelasi fisier start.php, care insa va initializa in variabila $site un obiect din clasa XSite. XSite este o extensie a clasei FrontSite care insa creeaza un obiect de tipul XLogin. Parametrii primiti la crearea obiectului XLogin duc la crearea unei sesiuni diferite (cu nume diferit si cale diferita) fata de sesiunea din site-ul principal. Astfel, un utilizator va avea doua sesiuni complet diferite, independente, una pentru front, cealalta pentru x. Login-ul din administrare nu va influenta login-ul din site, si invers. In plus, in metoda checkLogin() din XSite, metoda ce este apelata in start() din MySite, apelez authorize(), metoda delegata obiectului MyLogin ce redirecteaza utilizatorul la pagina de autentificare in cazul in care acesta nu este logat. Autorizarea este insa ignorata daca este apelata metoda $site->skipAuth(true) (vezi x/authPage.php). Deci implicit, toate paginile din x sunt protejate prin login.

Clasa XLogin, nu face decat sa schimbe tabelul in care se autentifica utilizatorii, variabila de sesiune principala, si variabilele aditionale setate in sesiune.

10.2   MyCrud.php - create, read, update, delete

CRUD este un concept des intalnita in dezvoltarea aplicatiilor cu baze de date, si reprezinta initialele de la: Create, Read, Update, Delete. Sunt de fapt cele 4 operatii de baza ce pot fi executate asupra unui tabel din baza de date, sau asupra unei informatii stocate sub o anumita forma. Pentru o corespondenta exacta cu operatiile din SQL, 'create' este de fapt 'insert' iar 'read' este 'select'.

In general, in aplicatiile web, si mai ales in interfetele de administrare, implementarea acestui set de 4 operatii este o operatie des intalnita datorita nevoii de a administra informatiile din baza de date. Doar in cazurile simple operatiile se refera la un singur tabel. In cazurile ceva mai complexe, administrarea datelor se face simultan din doua sau mai multe tabele, legate intre ele.

In cazul aplicatiei noastre, intalnim un 'crud simplu' in cazul tabelelor cats si products si am intalni un 'crud mai putin simplu' in cazul actualizarii datelor din tabelul users date legate de billingPerson sau billingCompany.

Pentru variantele simple, am realizat o clasa, numita MyCrud.php, ce automatizeaza si 'ascunde' in interiorul ei o mare parte din codul necesar celor 4 operatii. Clasa nu automatizeaza complet codul necesar celor 4 operatii astfel incat sa ramana o anume flexibilitate la afisare, si la implementarea efectiva operatiilor, si nu in ultimul rand pentru a pastra un nivel rezonabil de complexitate al codului.

Obiectele de tip MyCrud, primesc ca principal parametru la creare, un obiect de tip MyForm (sau derivat din MyForm) ce devine apoi proprietatea $form a obiectului MyCrud.

Obiectul MyForm, trebuie sa aiba la randul lui ca proprietate ($table) un obiect de tip MyTable. Atat $form, cat si $table se vor referi la aceeasi entitate, de exemplu la tabelul de categorii, cats. Obiectul MyCrud are proprietatea $action ce poate fi: insert, update, delete sau display. Acest parametru este setat prin POST sau GET. In functie de asta, va apela una din metodele corespunzatoare fiecarei actiuni.

Toate metodele pregatesc informatiile finale necesare in proprietatea (array) templateVars. Apoi, variabilele de sablon, obtinute de la obiectul crud prin getTemplateVars(), impreuna cu numele sablonului obtinut prin getTemplate() sunt transmise in script ca parametru metodei output() a clasei XPage pentru afisarea efectiva. In mod implicit, fiecare actiune are sablonul propriu, setat prin setTemplates(). Actiunea delete() nu are insa in general nevoie de sabloane, actiunile insert si update impart acelasi sablon, de forma crud_numeTabel_form.tpl.php iar sablonul de afisare poate fi refolosit, si este crud_display.tpl.php.

Insert si Update

Atat metodele insert() cat si update() pregatesc formularul prin metoda buildForm() apoi adauga obiectul $form in lista variabilelor de transmis sablonului, apoi apeleaza metoda make(). Metoda buildForm() atat pentru insert() cat si pentru update():

  • 'populeaza' campurile de tip select (prin proprietatea $initData) daca astfel de campuri corespund cu coloane de tip enum
  • 'leaga' proprietatea $formData din clasa MyCrud de proprietatea $formData din MyForm - pentru un acces mai scurt
  • genereaza pentru formular un camp hidden, numit action, ce va transmite mai departe actiunea cu care s-a inceput, insert sau update


In cazul metodei update(), in plus, in buildForm(), obtin valoarea cheii primare (din get, post, sau din formData) si prin intermediul metodei prepareFormForUpdate() extrag datele din baza de date - din tabelul asociat obiectului crud - si setez proprietatea $formData cu aceste date, si implicit proprietatea $formData a obiectului MyForm, folosita apoi pentru popularea formularului.

Metoda make() se ocupa mai departe de administrarea formularului cand a fost apasat butonul submit. Se apeleaza metoda validate() a obiectului $form (primit de MyCrud la constructor), metoda ce este suprascrisa intr-o subclasa din MyForm, si este populata astfel proprietatea $errors a obiectului $form cu eventualele erori. Pentru ca in sablon transmit direct obiectul $form, voi afisa eventualele erori cu $form->outputErrors(). Daca nu sunt erori, se apeleaza metoda process() care la randul ei apeleaza insert2() sau update2(), metode ce realizeaza efectiv insert-ul sau update-ul in baza de date, apeland metoda doData() a obiectului de tip MyTable. Daca $table este dintr-o subclasa a clasei MyTable, metoda doData() poate fi suprascrisa, si astfel folosita si de obiectul crud.

Display (Afisarea)

Metoda display() se ocupa de transmiterea catre sablonul crud a variabilei displayData, un array ce contine cate un array de informatii pentru fiecare rand extras din tabelul bazei de date.

Extragerea se face cu ajutorul metodei fetchAll() a obiectului $table, proprietate a obiectului curent crud, pe baza unui query ce poate fi setat din afara clasei prin intermediul metodei setDisplaySql(). Metoda prepareForDisplay() are rolul de a fi suprascrisa la nevoie, intr-o subclasa a MyCrud, astfel incat sa modifice informatiile afisate, din displayData.

Metoda updateByDisplayCols va sterge din displayData, din fiecare array, elementele ce nu se afla in proprietatea $this->displayCols, daca aceasta este setata. In acelasi timp, in proprietatea pkValues se tin separat valorile cheii primare pentru fiecare rand.
In metoda display() se adauga apoi pentru fiecare array elemente cu cheile deleteLk si editLk ce repezinta linkuri catre paginile de edit si delete pentru randul curent. Se foloseste functia addParam din f-url.php ce adauga ca parametru in query string numele cheii primare avand valoarea cheii pentru fiecare rand. In mod implicit, sablonul metodei display() este crud_display.tpl.php.

Delete

Metoda delete() cheama metoda delete a obiectului MyTable (sau derivat dintr-o clasa MyTable) si redirecteaza la pagina de afisare (Display)

10.3   cats.php - CatsCrud

Implementarea sistemului MyCrud.php in administrarea categoriilor (tabelul cats), se face in urmatoarele 3 fisiere, din folderul x:

  • cats.php
  • lib/CatsForm.php
  • templates/crud_cats_form.tpl.php

In cats.php creez un obiect din clasa CatsForm (ce extinde MyForm), apoi un obiect de tip Cats (ce extinde MyTable). Obiectul $table, de tipul Cats, este apoi setat ca proprietate obiectului $form. In final, creez obiectul $crud din clasa MyCrud, setez sql-ul pentru afisare, coloanele pe care vreau sa le afisez si apelez metoda go() ce analizeaza datele din get/post si apeleaza mai departe metoda potrivita. Implicit va fi apelata metoda display() si va fi folosit sablonul corespunzator, crud_display.tpl.php. Sablonul si variabilele de sablon sunt apoi transmise metodei output a clasei XPage, astfel incat continutul sa fie integrat in pagina de administrare.


In cazul unui update sau insert, este folosita clasa CatsForm. In CatsForm ma ocup de implementarea metodei validate() ce verifica datele din $formData (datele transmise prin get sau post de catre formular) si le valideaza. Validarea se poate face inclusiv relativ la datele deja existente in tabelul cats, pentru ca obiectul de tip CatsForm are in proprietatea $table un obiect de tip Cats. Astfel, impiedic introducerea a doua categorii cu acelasi nume. In caz de erori, populez ca de obicei proprietatea $errors ce este apoi folosita de obiectul $form, in sablonul crud_cats_form.tpl.php pentru afisarea erorilor.

Operatia delete() nu mai are nevoie de lib/CatsForm.php sau de templates/crud_cats _form.tpl.php. Metoda delete() din MyCrud trimite aici catre metoda delete() din Cats. Stergerea unei categorii este ceva mai complicata, pentru ca trebuie sa stearga automat toate produsele din categoria respectiva. Altfel, produsele raman intr-o categorie inexistenta. Aceasta metoda cauta deci toate produsele din categoria ce trebuie stearsa si incearca stergerea lor, prin aplicarea metodei delete() a obiectelor de tip Products. Daca stergerea vreunui produs esueaza, atunci categoria nu va fi stearsa, pentru ca executia va sari in blocul catch, unde va fi afisata eroarea.

10.4   products.php - ProductsCrud

MyCrud pentru tabelul products foloseste fisierele:

  • products.php
  • lib/ProductsCrud.php
  • lib/ProductsForm.php
  • templates/crud_products_form.tpl.php

products.php este similar cu cats.php, doar ca aici folosesc un query ce face join din tabelul products si cats, astfel incat, display() va afisa atat produsul cat si numele categoriei din care face parte. Sablonul crud_products_form.tpl.php este similar cu crud_cats_form.tpl.php, primeste obiectul $form cu ajutorul caruia afisam campurile formularului si eventualele erori. Insa de data asta extind clasa MyCrud, in care suprascriu prepareForDisplay(), pentru a modifica $displayData, astfel incat sa afisez pozele in browser in loc de numele lor.


Produsele sunt legate de categorii, prin urmare formularul pentru adaugarea/modificarea produselor va contine un select list cu numele categoriilor, avand ca valori catId-ul pentru fiecare categorie. Pentru a popula select list-ul cu categorii, in constructorul clasei ProductsForm extrag categoriile din baza de date si folosesc setInitData pentru a seta array-ul primit de selectul cu numele catId. In constructor, apelez metoda prepareForUpload(), metoda ce seteaza atributul enctype al formularului, si metoda post.

In metoda validate(), a clasei ProductsForm, in afara validarii campurilor obisnuite, am de facut o validarea pentru upload-ul fisierelor - pozele produselor. Pentru ca primesc din MyCrud, din metoda make() numele actiunii curente ca parametru catre metoda validate(), pot folosi $action pentru a face deosebirea intre insert si update. Astfel, la insert prin parametrul $required cer prezenta unor fisiere incarcate, in timp ce la update incarcarea fisierelor este optionala. Clasa MyUpload, prin metodele addFile() si doUpload() se ocupa de incarcarea fisierelor, si verifica daca au fost erori, prin hasErrors() si apoi prin outputError() trimit erorile catre proprietatea $errors din obiectul curent din clasa ProductsForm. Daca nu sunt erori, extrag numele fisierelor incarcate cu getUploadedName($numeCamp) si le folosesc ca valori pentru insert-ul sau update-ul in baza de date.

10.5   orders.php
orders.php este pagina de cautare si afisare a comenzilor. In general, cand sunt de afisat informatii dintr-un tabel cu multe randuri, sau e nevoie de cautarea in informatiile respective dupa anumite criterii, nu se face afisarea simpla ci se incepe cu un formular de cautare. In general, formularele de cautare transmit datele prin get, pentru ca nu modifica informatie in baza de date, si astfel se poate naviga usor inainte si inapoi fata de pagina de rezultate, in plus pagina de rezultate se poate salva in bookmarks.

In cazul comenzilor, daca nu caut comanda dupa utilizator (caz in care folosesc Cauta utilizator), probabil voi cauta comanda dupa perioada. Acest formular, cauta comenzi, imi ofera o data de inceput si o data de sfarsit, interval in care trebuie sa se incadreze valoarea coloanei dateAdded din orders. MySQL are posibilitaea sa compare datele cu operatorii <, >, <= si >=, insa bineinteles datele trebuie sa fie in acelasi format, formatul bazei de date: YYYY-mm-dd.

In plus, daca in baza de date am tipul datetime, deci pastrez ora, minutul si secunda, e necesar ca valorile cu care compar coloana dateAdded, deci valorile din formular, sa aiba si ele forma completa. Astfel, pentru a obtine intervalul complet am completat ora 00:00:00 pentru data de inceput, astfel incat sa 'prind' orice date din baza de date pentru data respectiva, si ora 23:59:59 pentru data de sfarsit. Daca introduc de exemplu 02.09.2009 la data de inceput si 25.10.2009 la data de sfarsit, cautarea se va face intre 02.09.2009 00:00:00 si 25.10.2009 23:59:59.


Functia smartDate(), prezenta in site/common/f-date.php, va converti o data din format romanesc in formatul bazei de date. Insa, permite scurtaturi, de exemplu daca se completeaza doar ziua se introduce automat luna curenta si anul curent. Daca se completeaza ziua si luna se introduce automat anul curent.


Functia db2Display() va realiza aproximativ procesul invers, modifica datele din format de baze de date intr-un format usor de citit.


Datele extrase, in variabila $data, precum si totalul valorii comenzilor gasite, vor fi transmise catre sablonul orders.tpl.php si afisate, impreuna cu formularul de cautare.

10.6   users.php
users.php foloseste un formular simplu, un input text, pentru cautarea utilizatorilor. Query-ul va cauta textul din formular atat in prenume, nume, cat si in numele de utilizator. In aceasta simpla listare nu ma intereseaza datele de facturare (din billingCompany sau billingPerson).


In users.tpl.php afisez formularul de cautare, si apoi datele utilizatorilor obtinute in $data. Fiecare rand are un link catre userPage.php, in care transmit valoarea cheii primare pentru randul respectiv, si in care afisez detaliile complete pentru acel utilizator.

10.7   orderDetails.php
In listarea comenzilor (orders.php), avem un link ce ne trimite la orderDetails.php? orderId=valoare.
In orderDetails.php, avand valoarea cheii primare pentru comanda respectiva, extrag toate datele comenzii si le afisez.


In plus, afisez si datele utilizatorului ce a trimis comanda respectiva, astfel incat sa am o imagine completa asupra comenzii respective. De fapt, aceasta pagina afiseaza aceleasi date pe care le afiseaza o proforma, insa intr-un aranjament diferit.

orderDetails.tpl.php este compus din doua parti. Prima parte este string-ul $userDetails in care este extras codul html complet pentru afisarea utilizatorului respectiv iar logica pentru formarea acestei variabile este in cu totul alt sablon. A doua parte este afisarea efectiva a detaliilor comenzii pe baza variabilei $odata.


In orderDetails.php, in primul rand, extrag uid-ul utilizatorului ce a efectuat aceasta comanda. Apoi, prin metoda getFullData($uid) din clasa Users (ce extinde MyTable) obtin detaliile complete ale utilizatorului. getFullData() returneaza un array cu doua elemente corespunzatoare cheilor 'main' si 'billing'. Elementul 'main' este un array cu datele din tabelul users iar elementul cu cheia 'billing' este un array cu datele pe care getFullData() le-a obtinut din billingPerson sau billingCompany, in functie de caz, si reprezinta datele de facturare. $udata, datele utilizatorului, sunt transmise prin variabila $vu sablonului userDetails.tpl.php ce returneaza codul html pentru afisarea utilizatorului, tinut in $userDetails si transmis apoi catre orderDetails.tpl.php.


Apoi, prin obiectul $o din clasa Orders, prin metoda getCurrentOrderData() extrag datele comenzii (de fapt datele produselor din comanda, si totalurile) in variabila $odata. Aceasta metoda, si exact aceleasi informatii le obtineam si in site-ul principal in orderView.php. Este esential, oriunde am afisa aceasta informatie (in front, in x, le trimitem pe email, generam un pdf), sa le obtinem prin aceeasi metoda, astfel incat toate calculele (valoare, tva, totaluri) sa se realizeze intr-un singur loc, minimizand astfel bug-urile.

10.8   userPage.php

userPage.php este pagina utilizatorului. Incepe similar cu orderDetails.php, si anume extrage prin getFullData() datele complete ale utilizatorului, si le transmite apoi sablonului userPage.tpl.php prin variabila de sablon $userDetails.


Apoi, folosind metodele getListByUser($uid) si getTotalByUser($uid) din clasa Orders, extrag comenzile (trimise) pentru utilizatorul respectiv, si totalul lor.


Folosesc apoi in sablon $userDetails ce contine datele complete (si deja formatate, in cod html) ale utilizatorului, apoi $ordersData, si $ordersTotal pentru afisarea comenzilor acestui utilizator si totalul acestor comenzi.


Orice calcul, oricat de simplu (cum ar fi totalul comenzilor) il realizez in interiorul claselor specializate (Orders in acest caz) si nu in sabloane, astfel incat sa tin cat pot de separat, logica de prezentare de logica aplicatiei.


userPage.php si orderDetails.php se completeaza astfel reciproc: ambele ofera detalii complete despre utilizator, in aceeasi forma, insa in userPage.php se afiseaza lista completa a comenzilor pentru utilizator, iar in orderDetails.php pot vedea detaliile complete ale unei comenzi.





Politica de confidentialitate


creeaza logo.com Copyright © 2024 - Toate drepturile rezervate.
Toate documentele au caracter informativ cu scop educational.