Managementul Plăților Pentru Un Site E Commerce

LUCRARE DE DISERTAȚIE

Dumitru Claudiu Orlando

COORDONATOR ȘTIINȚIFIC

Șef lucrări dr. ing. Marian Marius

Iulie 2016

CRAIOVA

Managementul plăților pentru un site e-Commerce

Dumitru Claudiu Orlando

COORDONATOR ȘTIINȚIFIC

Șef lucrări dr. ing. Marian Marius

Iulie 2016

CRAIOVA

„Învățătura este o comoară care își urmează stăpânul pretutindeni.”

Proverb popular

DECLARAȚIE DE ORIGINALITATE

[anonimizat] Orlando, student la specializarea Tehnologii informatice în ingineria sistemelor din cadrul Facultății de Automatică, Calculatoare și Electronică a Universității din Craiova, certific prin prezenta că am luat la cunoștință de cele prezentate mai jos și că îmi asum, în acest context, originalitatea lucrării mele de disertație:

cu titlul Managementul plăților pentru un site e-Commerce,

coordonată de Șef lucrări dr. ing. Marian Marius,

prezentată în sesiunea Iulie 2016.

La elaborarea lucrării de disertație, se consideră plagiat una dintre următoarele acțiuni:

reproducerea exactă a cuvintelor unui alt autor, dintr-o altă lucrare, în limba română sau prin traducere dintr-o altă limbă, dacă se omit ghilimele și referința precisă,

redarea cu alte cuvinte, reformularea prin cuvinte proprii sau rezumarea ideilor din alte lucrări, dacă nu se indică sursa bibliografică,

prezentarea unor date experimentale obținute sau a unor aplicații realizate de alți autori fără menționarea corectă a acestor surse,

însușirea totală sau parțială a unei lucrări în care regulile de mai sus sunt respectate, dar care are alt autor.

Pentru evitarea acestor situații neplăcute se recomandă:

plasarea între ghilimele a citatelor directe și indicarea referinței într-o listă corespunzătoare la sfărșitul lucrării,

indicarea în text a reformulării unei idei, opinii sau teorii și corespunzător în lista de referințe a sursei originale de la care s-a făcut preluarea,

precizarea sursei de la care s-au preluat date experimentale, descrieri tehnice, figuri, imagini, statistici, tabele et caetera,

precizarea referințelor poate fi omisă dacă se folosesc informații sau teorii arhicunoscute, a căror paternitate este unanim cunoscută și acceptată.

Data, Semnătura candidatului,

LUCRARE DE DISERTAȚIE

REFERATUL CONDUCĂTORULUI ȘTIINȚIFIC

În urma analizei lucrării candidatului au fost constatate următoarele:

În concluzie, se propune:

Data, Semnătura conducătorului științific,

REZUMATUL LUCRĂRII

Payment Management este o aplicație web ce iși propune analizarea, structurarea și organizarea comenzilor și încasarilor unui site e-Commerce.

Cum funcționează?

Payment management este un site distinct unde se pot monitoriza toate comenzile ce au loc intr-o anumită perioadă pe orice platformă e-Commerce. În fapt, se trimite un raport cu toate comenzile în format .xls/.xlsx din site-ul e-Commerce către aplicația Payment Management. În aplicație, se va încărca fișierul în format .xls/.xlsx și vor fi listate toate comenzile din raport. De asemenea, după ce au fost încasați banii de la clienți prin intermediul curierilor, aceștia din urmă vor trimite un raport în format .xls/.xlsx cu toate comenzile încasate către aplicația Payment Management. În aplicație, se va încarca fișierul în format .xls/.xlsx și vor fi listate toate încasarile din raport.

Comenzile cât și încasarile vor avea elemente comune precum numarul de comandă, data comenzii, data finalizării comenzii, numele produsului, cât și cantitatea produsului. Insă, vor fi și elemente distincte cum ar fi valoarea comenzii pentru comenzile ce provin din site-ul e-Commerce, precum și valoarea încasată pentru plațile ce provin de la curieri.

În aplicația Payment Management se vor concatena cele doua sume: valoarea comenzii și valoarea încasată. Dacă aceste doua valori coincid înseamnă ca totul a decurs conform așteptarilor. Acesta este fluxul normal al unei comenzi finalizate cu succes și nu necesită intervenția unui operator. În cazul în care, valoarea comenzii este mai mică decât valoarea încasată deducem ca pe acea comandă s-au încasat mai mulți bani decât valoarea produselor. În acest moment, în aplicație, un operator va trata acestă comandă manual, adică va efectua o restituire către client prin transfer bancar. În cazul în care, valoarea comenzii este mai mare decât valoarea încasată deducem ca pe acea comandă s-au încasat mai mulți bani decat valoarea produselor. În acest moment, în aplicație, un operator va trata acestă comanda manual, adică va efectua un ordin de plată de la client către site-ul e-Commerce.

Scopul final este acela ca ambele sume: valoarea comenzii si valoarea încasată să coincidă. Daca acești factori sunt întruniți, în aplicație se va genera un desfașurator în format .pdf cu toate comenzile și încăsarile acestora.

MULȚUMIRI

Acest paragraf este destinat coordonatorului stiințific care m-a sprijinit și m-a ajutat pe toată durata realizării proiectului și tuturor cadrelor didactice care au contribuit la îmbogățirea cunoștiințelor în domeniul ingineria sistemelor pe parcursul celor 2 ani de studiu.

CUPRINSUL

1 INTRODUCERE 1

1.1 SCOPUL 1

1.2 MOTIVAȚIA 2

2 DESCRIEREA ȘI ANALIZA CERINȚELOR 3

2.1 CERINȚE GENERALE 3

2.2 NOȚIUNEA DE MANAGEMENT 5

2.3 NOȚIUNEA DE E-COMMERCE 7

3 ASPECTE TEORETICE ȘI TEHNOLOGII 8

3.1 HTML 8

3.2 CSS 12

3.3 PHP 16

3.4 SYMFONY 21

3.5 DOCTRINE 24

3.6 JAVASCRIPT 27

3.7 JQUERY 33

3.8 MYSQL 36

3.9 PHPSTORM 41

3.10 HEIDISQL 43

4 IMPLEMENTAREA APLICAȚIEI 45

4.1 ARHITECTURA APLICAȚIEI 45

4.2 DESCRIEREA DETALIATĂ A APLICAȚIEI 47

4.3 DESCRIEREA CLASELOR APLICAȚIEI 54

4.4 MANUAL DE UTILIZARE 70

5 CONCLUZII 88

6 BIBLIOGRAFIE 89

7 REFERINȚE WEB 90

8 ANEXE 92

CODUL SURSA 92

CD/DVD 229

9 INDEX 230

LISTA FIGURILOR

Figura 1: Clasa DefaultController………………………………………………………………………………..54

Figura 2: Clasa MatchingsController……………………………………………………………………….55-57

Figura 3: Clasa OrdersController…………………………………………………………………………….58-60

Figura 4: Clasa PaymentsController………………………………………………………………………..61-64

Figura 5: Clasa UsersController………………………………………………………………………………65-67

Figura 6: Funcția de încarcare a fișierului .xls……………………………………………………………….68

Figura 7: Funcția de parsare a rândurilor din fișierul .xls………………………………………………..79

Figura 8: Pagina de autentificare – ro…………………………………………………………………………..70

Figura 9: Pagina de autentificare – en…………………………………………………………………………..70

Figura 10: Pagina Panou de control – ro……………………………………………………………………….71

Figura 11: Pagina Panou de control – en……………………………………………………………………….71

Figura 12: Pagina Listă utilizatori – ro………………………………………………………………………….72

Figura 13: Pagina Listă utilizatori – en…………………………………………………………………………72

Figura 14: Fereastra Adaugare utilizatori – ro……………………………………………………………….73

Figura 15: Fereastra Adaugare utilizatori – en……………………………………………………………….73

Figura 16: Pagina Listă fișiere – ro………………………………………………………………………………74

Figura 17: Pagina Listă fișiere – en………………………………………………………………………………74

Figura 18: Pagina Listă comenzi – ro……………………………………………………………………………75

Figura 19: Pagina Listă comenzi – en…………………………………………………………………………..75

Figura 20: Fereastra de încarcare a fișierului .xls pentru comenzi – ro……………………………..76

Figura 21: Fereastra de încarcare a fișierului .xls pentru comenzi – en……………………………..76

Figura 22: Fișier .xls ce conține comenzi………………………………………………………………………77

Figura 23: Pagina Listă încasări – ro…………………………………………………………………………….78

Figura 24: Pagina Listă încasări – en……………………………………………………………………………78

Figura 25: Fereastra de încarcare a fișierului .xls pentru încasări – ro………………………………79

Figura 26: Fereastra de încarcare a fișierului .xls pentru încasări – en………………………………79

Figura 27: Fișier .xls ce conține încasări……………………………………………………………………….80

Figura 28: Pagina Mapare încasări – ro…………………………………………………………………………81

Figura 29: Pagina Mapare încasări – en………………………………………………………………………..81

Figura 30: Fereastra Legendă – ro………………………………………………………………………………..82

Figura 31: Fereastra Legendă – en……………………………………………………………………………….82

Figura 32: Fereastra Adaugare plată – ro………………………………………………………………………84

Figura 33: Fereastra Adaugare plată – en………………………………………………………………………84

Figura 34: Fereasta Observații – ro………………………………………………………………………………85

Figura 35: Fereasta Observații – en………………………………………………………………………………85

Figura 36: Pagina Desfășurătoare – ro………………………………………………………………………….86

Figura 37: Pagina Desfășurătoare – en………………………………………………………………………….86

Figura 38: Desfășurător în format .pdf………………………………………………………………………….87

Introducere

1.1 Scopul

Payment Management este o aplicație web ce poate fi utilă oricarui posesor de site e-Commerce. Aceasta iși propune analizarea și organizarea comenzilor si incasarilor efectuate de clienți prin intermediul curierilor.

Ce înțelegem prin comerț electronic?

Comerțul electronic este o activitate prin care se transmit date referitoare la produse sau servicii ce pot fi comandate sau vândute de la distanță. Prin intermediul internetului, această activitate specifică politicii expansive a marketingului, se dezvoltă o relație de servicii și schimb de marfuri între vânzator și viitorul cumpărator.

Ce impact are Payment Management asupra comerțului electronic?

Payment Management a fost creat din necesitatea verificării tranzacțiilor la nivel de comandă. În piața electronică, companiile comerciale pun la dispoziție bunuri și servicii către clienți. Aceștia încheie afaceri electronice prin comandarea bunurilor sau serviciilor. Insă, cu cât compania comercializează mai multe tipuri de bunuri sau servicii, cu atât este mai greu să analizeze și să structureze sumele capturate pentru bunurile și serviciile pe care la întreprind.

Scopul principal a fost acela de a realiza o aplicație web care poate fi accesibilă la scară largă către posesorii de site-uri e-Commerce. Payment Management este o soluție de business sigură ce este caracterizată prin rapiditatea și simplitatea structurării comenzilor și încasărilor. Uneori, între site-urile e-Commerce, curieri și clienți pot apărea neconordanțe între tranzacțiile ce se efectuează. Spre exemplu: site-ul e-Commerce poate afișa un produs, însă prețul poate varia în funcție de discountul aferent, un client poate refuza un produs sau poate cumpăra o cantitate mai mică decât cea comandată, curierii pot returna o sumă incorectă către site-ul e-Commerce. Toate aceste aspecte vor putea fi controlate utilizând platforma Payment Management.

1.2 Motivația

Platforma Payment Management a fost creată din dorința de a implementa în PHP o soluție de business pentru site-urile e-Commerce. Am hotarât să realizez acest proiect pentru a mă familiariza cu modul de implementare a aplicațiilor web.

Am dorit să dezvolt o aplicație web din dorința de a utiliza tehnologii precum: PHP, HTML, CSS, Symfony, JavaScript/Jquery , Doctrine cât și MySQL.

Am ales să implementez aplicația web cu titlul „Managementul plăților pentru un site e-Commerce” din necesitatea de a structura mult mai ușor comenzile și încasările ce au loc pe o durata de timp. De asemenea, se pot anticipa și verifica eventualele erori ce pot aparea la comercializarea de bunuri și servicii.

Descrierea si analiza cerintelor

2.1 Cerinte generale

Dorim să implementăm o aplicație web de care să beneficieze toate site-urile e-Commerce pentru organizarea, analizarea, verificarea și structurarea comenzilor și încasărilor efectuate de clienți prin intermediul curierilor.

Aplicația urmează să fie implementată în PHP, utilizând framework-ul symfony și o conexiune la baza de date.

Se cere implementarea unei aplicații web securizate, accesul se va face doar printr-o fereastă de autentificare. În aplicația web se vor putea autentifica două tipuri de utilizatori: administratorii si operatorii. Utilizatorii de tip administrator vor avea drepturi de adăugare utilizatori de tip operatori, cât și drepturi de activare sau inactivare a acestora. Utilizatori de tip operator nu vor avea drepturi de adăugare a altor tipuri de utilizatori, de asemenea, nu pot activa sau inactiva alte tipuri de utilizatori.

Payment Management va fi o aplicație complexă ce va fi structurată în mai multe secțiuni:

Panou de control

Listă utilizatori

Listă fișiere

Listă comenzi

Lista încăsari

Mapare plăți

Desfășurătoare

În pagina „Panou de control” vor fi evidențiate trei liste:

ultimele cinci desfasuratoare încasate

ultimuii cinci utilizatori autentificați

documentare tehnică (.pdf-uri ce pot fi descarcate din interfața: despre aplicație, manual de utilizare, etc)

În pagina „Listă utilizatori” vor fi listați toți utilizatorii ce au acces în aplicația Payment Management:

administratorii (au drept de a crea operatori sau administratori sau de a activa sau inactiva alte tipuri de utilizatori)

operatorii (nu au drepturi)

butonul „Adauga utilizator” va aparea doar în cazul în care se autentifică unul dintre administratori. Operatorii nu au dreptul de a crea alți utilizator, pentru aceștia butonul nu va apărea în interfață.

În pagina „Listă fișiere” vor fi listați utilizatorii în funcție de ultimele desfasuratoare încărcate.

se vor încărca desfășurătoare ce conțin comenzi din site-ul e-Commerce sau desfășurătoare cu încasările preluate de curieri.Acestea vor fi evidentiate în ‚‚listă fișiere” pentru a se centraliza toate desfășurătoarele împreună cu operatorii ce le-au încărcat.

Pagina „Listă comenzi” va fi structurată în trei părți:

Bara de filtre (drop-down-uri de filtrare dupa dată, filtre după numele produselor sau numărul comenzii)

Buton de încarcare a fșierelor (.xls/.xlsx ce conțin comenzile ce au fost plasate în site-ul e-Commerce)

Lista de comenzi (de pe desfășurătorele ce au fost încarcate de operatori)

Pagina „Listă încasări” va fi structurată în trei părți:

Bara de filtre (drop-down-uri de filtrare dupa dată, filtre după numele produselor sau numărul comenzii)

Buton: „încarca fișier plăți” (.xls/.xlsx ce conțin comenzile ce au fost încasate de către curieri)

Lista de încasări (de pe desfășurătorele ce au fost încărcate de operatori)

Pagina „Mapare încasări” va fi structurată în cinci părți:

Bara de filtre (drop-down-uri de filtrare dupa dată, filtre după numele produselor sau numărul comenzii, status)

Lista cu mapare între valoarea produsului și valoarea încasării

Buton de adaugare plată manual (se vor putea efectua încasări sau restituiri de către operatori)

Buton pentru generarea a unui desfășurător în format .pdf cu comenzile încasate și finalizate

Buton observatii

În pagina „Desfășurătoare” se vor evidenția desfășurătoarele. Ele pot conține:

Comenzi încasate și finalizate – status Verde

Comenzi cu valoarea încasată mai mare – status Protocaliu

Comenzi cu valoarea încasată mai mare – status Roșu

Acestea vor putea fi descărcate în format .pdf. Astfel se pot face rapoarte cu privire la erorile ce pot aparea într-un astfel de sistem.

2.2 Noțiunea de management

Managementul în întreprinderi și organizații este funcția care coordonează eforturile oamenilor de a-și realiza scopurile și obiectivele prin utilizarea eficientă și eficace a resurselor disponibile.

Managementul include planificarea, organizarea, angajarea de personal sau de conducere și controlul unei organizații pentru a realiza obiectivul sau ținta. Cuprinde desfășurarea strategiei și manipularea resurselor umane, resurse financiare, resurse tehnologice și a resurselor naturale. Managementul este, de asemenea, o disciplină academică, o știință socială al cărei obiectiv este acela de a studia organizarea socială.

În cadrul organizațiilor profitabile, funcția principală de administrare este satisfacerea unei game de părți interesate. Acest lucru implică, de obicei a face un profit (pentru acționari), crearea de produse evaluate la un cost rezonabil (pentru clienți) și oferirea unor oportunități mari de angajare pentru angajați. În managementul non-profit, se adaugă importanța păstrării credinței donatorilor. În cele mai multe modele de gestionare și de guvernare, acționarii votează pentru consiliul de administrație, iar consiliul apoi înștiințează conducerea. Anumite organizații au experimentat cu alte metode (cum ar fi modele de angajat drept de vot) de selectare sau revizuire a managerilor, dar acest lucru este rar.

Managementul funcționează prin cinci funcții de bază: planificarea, organizarea, coordonarea, comandarea și controlul.

Planificarea: A decide ce trebuie să se întâmple în viitor și a genera de planuri de acțiune (de a decide în avans)

Organizarea: Asigurarea resurselor umane și non-umane și punerea în aplicare

Coordonarea: Crearea unei structuri prin care obiectivele unei organizații pot fi realizate.

Comandarea: Determinarea activitaților și obținerea personalului

Controlarea: Verificarea progresului împotriva planurilor.

Rolurile de bază:

Interpersonale: roluri care implică coordonarea și interacțiunea cu angajații

Informaționale: roluri care implică manipularea, distribuirea și analizarea informațiilor

Decizionale: roluri care necesită luarea deciziilor

Punerea în aplicare a politicilor și strategiilor

Toate politicile și strategiile trebuie să fie discutate cu tot personalul de conducere și a personalului.

Managerii trebuie să înțeleagă în cazul în care și modul în care acestea pot pune în aplicare politicile și strategiile lor.

Un plan de acțiune trebuie să fie concepute pentru fiecare departament.

Politicile și strategiile trebuie să fie revizuite în mod regulat.

Planurile de urgență trebuie să fie concepute în cazul în care schimbările mediului.

Manageri de nivel superior ar trebui să efectueze evaluări intermediare periodice.

Afacerea necesită spirit de echipă și un mediu bun.

Misiunile, obiectivele, punctele forte și punctele slabe ale fiecărui departament trebuie să fie analizate pentru a determina rolul lor în realizarea misiunii întreprinderii.

Metoda de prognoză se dezvoltă o imagine fiabilă a viitorului mediului de afaceri a lui.

O unitate de planificare trebuie să fie create pentru a se asigura că toate planurile sunt consecvente și că politicile și strategiile au ca scop realizarea aceleiași misiuni și a obiectivelor.

2.3 Noțiunea de e-Commerce

Comerțul electronic, în mod obișnuit scris ca e-commerce sau eCommerce, se referă la tranzacționarea sau facilitarea comercializării de produse sau servicii care utilizează rețele de calculatoare, cum ar fi Internetul. Comerțul electronic se bazează pe tehnologii, cum ar fi comerțul mobil, transferul electronic de fonduri, managementul lanțului de aprovizionare, marketing pe Internet, procesarea a tranzacțiilor on-line prin schimbul electronic de date (EDI), sisteme de gestiune a stocurilor precum și sisteme automate de colectare a datelor. Comerțul electronic modern, folosește de obicei World Wide Web pentru cel puțin o parte din ciclul de viață al tranzacției, deși se pot utiliza și prin alte tehnologii, cum ar fi cele de e-mail.

Moduri de comerț electronic:

Online, prin site-uri web de cumpărături

Furnizarea sau participarea la piețe on-line

Business-to-business, vanzare și cumparare

Colectarea și utilizarea datelor demografice prin intermediul contactelor web și social media

Business-to-business, schimbul electronic de date

Marketing pentru clienții potențiali și stabiliți prin e-mail sau fax

Angajarea în pretail pentru lansarea de noi produse și servicii

Schimburi financiare online pentru schimburi valutare sau în scopuri de tranzacționare

Aspecte teoretice si tehnologii

3.1 HTML

HTML este un limbaj de marcare hipertext și este folosit în general pentru a crea pagini web. Împreuna cu CSS și JavaScript, HTML este baza utilizată pentru a crea pagini web, precum și pentru a crea interfețe de utilizator pentru aplicații mobile și web. Browserele Web pot citi fișiere HTML și le pot face pagini web vizibile sau sonore. HTML putea descrie structura unui site web chiar și înainte de apariția Cascading Style Sheets (CSS), pentru prezentarea sau aspectul documentului, făcându-l un limbaj de marcare, mai degrabă decât un limbaj de programare.

Elementele HTML formează blocurile de pagini HTML. HTML-ul permite imagini și alte obiecte care urmează să fie integrate și poate fi folosit pentru a crea formulare interactive. Acesta oferă un mijloc de a crea documente structurate prin ceea ce denotă semantica structurală pentru text, cum ar fi titluri, paragrafe, liste, link-uri, citate și alte elemente. Elementele HTML sunt delimitate de tag-uri, scrise folosind paranteze unghiulare. Tag-uri, precum <img /> și <input /> introduc conținut în pagina direct. Altele, cum ar fi <p> ​​… </ p> înconjoara și furnizeză informații cu privire la textul documentului și pot include și alte etichete precum sub-elementele. Browserele nu afișează etichetele HTML, dar le folosesc pentru a interpreta conținutul paginii.

HTML-ul poate încorpora script-uri scrise în alte limbaje de programare, cum ar fi JavaScript care afectează comportamentul paginilor web HTML. Markup-ul HTML se poate completa, de asemenea, cu Cascading Style Sheets (CSS) pentru a defini aspectul și dispunerea de text și alte materiale.In 1997, World Wide Web Consortium (W3C), menținător atât standardele CSS, cat si HTML, a încurajat utilizarea celor doua limbaje in prezentații.

In 1980, fizicianul Tim Berners-Lee, în calitate de contractor la CERN, a propus un sistem pentru cercetătorii CERN pentru a utiliza și partaja documente. În 1989, Berners-Lee a scris un memoriu care propune un sistem de hipertext bazat pe Internet.

In 1990, Berners-Lee si CERN împreuna cu inginerul Robert Cailliau au colaborat la o cerere comună de finanțare, dar proiectul nu a fost adoptat în mod formal de CERN. În notele sale personale, din 1990, a enumerat "unele dintre multiplele domenii în care se utilizează hipertext".

Prima descriere la dispoziția publicului a HTML a fost un document numit "HTML Tag-uri" si a fost menționat pentru prima dată pe internet de către Tim Berners-Lee la sfârșitul anului 1991. S-au descris 18 elemente ce cuprind proiectul inițial, un HTML relativ simplu. Unsprezece dintre aceste elemente există în HTML 4.

HTML-ul este un limbaj de marcare pe care browserele web îl folosesc pentru a interpreta și de a compune texte, imagini și alte materiale în pagini web vizuale sau sonore. Caracteristicile implicite pentru fiecare element HTML sunt definite în browser, iar aceste caracteristici pot fi modificate sau îmbunătățite prin utilizarea suplimentară a paginii de web designer de CSS. Multe dintre elementele de text se regăsesc in raportul tehnic în ISO TR 9537, tehnici pentru utilizarea SGML, care, la rândul său, se referă la caracteristicile de text ale limbajelor de formatare, cum ar fi CTSS (Time Compatibil Sharing Sistem – sistem de operare): aceste comenzi de formatare au fost derivate din comenzile utilizate de type setters pentru a căuta manual documente. Cu toate acestea, conceptul SGML de marcare se bazează mai degraba pe elemente (intervale imbricate adnotate cu atribute), decât pe imprimarea efectelor. HTML-ul a fost mutat în mod progresiv în această direcție cu CSS.

Berners-Lee a considerat HTML ca fiind o aplicație a SGML. Acesta a fost definit formal ca atare de către Internet Engineering Task Force (IETF), cu publicarea la mijlocul anului 1993, a primei propuneri pentru o specificație HTML: "Hypertext Markup Language (HTML)". Proiectul a expirat după șase luni, dar a fost notabil pentru recunoașterea sa de tag-ul personalizat browser-ul NCSA Mosaic pentru încorporarea imaginilor în linie, care reflectă filosofia bazându-standarde pe succesul IETF lui prototipuri. In mod similar, Dave Raggett, sfârșitul anului 1993, a sugerat standardizarea caracteristicilor deja puse in aplicare, cum ar fi tabele și formulare de umplere.

La începutul anului 1994, IETF a creat un grup HTML de lucru, care în 1995 a finalizat "HTML 2.0", prima specificație HTML a fost destinata să fie tratata ca un standard față de care ar trebui să se bazeze implementările viitoare.

Din 1996, specificațiile HTML au fost menținute, cu intrare de la furnizori de software comercial, de către World Wide Web Consortium (W3C). Cu toate acestea, în 2000, HTML a devenit, de asemenea, un standard internațional (ISO / IEC 15445: 2000). HTML 4.01 a fost publicat la sfârșitul anului 1999, cu erata publicat apoi prin 2001. În 2004 a început pe HTML5 în Application Group Web Hypertext Tehnologie de lucru (WHATWG), care a devenit un livrabile cu W3C în 2008 și completat și standardizat pe 28 octombrie 2014.

HTML5 este un limbaj de marcare utilizat pentru structurarea conținutului și prezentarea pe World Wide Web. Este versiunea a cincea și curentă a standardului HTML.

Acesta a fost publicat în octombrie 2014 de către World Wide Web Consortium (W3C) pentru a îmbunătăți limbajul ca suport pentru cele mai recente multimedia, în timp ce menținându-l ușor de citit de către oameni și consecvent înțeles de computere și dispozitive, cum ar fi web browsere, interpretoare, etc. HTML5 este destinat să subsumeze nu numai HTML 4, dar si XHTML 1 si DOM Level 2 HTML.

HTML5 include modele detaliate de prelucrare pentru a încuraja implementări mai interoperabile. HTML se extinde, îmbunătățește și raționalizează markup disponibile pentru documente și prezentări introductive de marcare și aplicații interfețele de programare (API) pentru aplicații web complexe. Din aceleași motive, HTML5 este, de asemenea, un candidat pentru aplicații mobile cross-platform, deoarece include caracteristici proiectate cu dispozitive alimentate de mici, cum ar fi smartphone-uri și tablete.

Multe caracteristici noi sintactice sunt incluse. Pentru a include și manipula multimedia nativ și conținut grafic, noul <video>, <audio> si elemente <panza> s-au adăugat și suport pentru grafice vectoriale scalabile (SVG), conținutul și MathML pentru formule matematice.

Pentru a îmbogăți conținutul semantic al documentelor, noi elemente de structură pagină, cum ar fi <main> <section>, <articol>, <header>, <aside>, <footer>, <nav> și <figure>, se adaugă. Noile atribute sunt introduse unele elemente si atribute au fost eliminate, iar altele, cum ar fi <a>, <cite> și <Menu> au fost modificate, redefinite sau standardizate.

API-urile și Document Object Model (DOM) sunt acum parti fundamentale ale caietului de sarcini HTML5, de asemenea, definește mai bine procesarea pentru orice documente invalide.

HTML5 introduce elemente și atribute care reflectă utilizarea tipică pe site-urile moderne. Unele dintre ele sunt înlocuiri semantice pentru utilizări comune ale blocului generic (<div>) și inline (<span>) elemente, de exemplu <nav> (bloc de navigare site), <footer> (de obicei, referindu-se la partea de jos a paginii web sau pentru a ultimele linii de cod HTML), sau <audio> și <video> in loc de <obiect>. Unele elemente perimate din HTML 4.01 au scăzut, inclusiv elemente pur formale, cum ar fi <font> și <center>, ale cărei efecte au fost mult timp înlocuite cu mai capabili Cascading Style Sheets. Există, de asemenea, un nou accent pe importanța scripting DOM (de exemplu, JavaScript) în comportament Web.

Sintaxa HTML5 nu se mai bazează, în ciuda similitudinii SGML, pe markup-urile sale. Cu toate acestea, ea a fost proiectata pentru a fi compatibila cu parsarea comună a versiunilor mai vechi ale HTML. Acesta vine cu o nouă linie introductivă, care arată ca o declarație de tip de document SGML, <! DOCTYPE html>, care declanșează modul de redare conform cu standardele. De la 5 ianuarie 2009, HTML5 include, de asemenea, Web Forms 2.0, un WHATWG separat anterior caietului de sarcini.

3.2 CSS

Cascading Style Sheets (CSS) este un standard folosit pentru a descrie prezentarea unui document scris într-un limbaj de marcare. Este cel mai des folosit pentru a seta stilul vizual de pagini web și interfețe scrise în HTML și XHTML. Limbajul poate fi aplicat oricărui document XML, inclusiv XML simplu, SVG și XUL. Impreuna cu HTML și JavaScript, CSS este o tehnologie de baza folosita de cele mai multe site-uri web pentru a crea pagini web, interfețe de utilizator pentru aplicații web și interfețe de utilizator pentru multe aplicații mobile.

CSS este destinat în primul rând pentru a permite separarea conținutului documentului de prezentare precum si factori cum ar fi aspectul, culorile și fonturile. Această separare poate îmbunătăți accesibilitatea conținutului, oferă mai multă flexibilitate și control în specificarea caracteristicilor de prezentare, poate fi activat pe mai multe pagini HTML pentru a partaja formatarea și de a reduce complexitatea și repetarea în conținutul structural.

Această separare de formatare și conținut face posibilă prezentarea aceleiași pagini de marcare în stiluri diferite pentru diferite metode de redare, cum ar fi pe ecran, în format tipărit, prin voce (când se citește de un browser sau un ecran cititor bazat pe voce) și dispozitive tactile. Acesta poate fi, de asemenea, utilizat pentru a afișa pagina de web diferit, în funcție de dimensiunea ecranului sau a dispozitivului pe care acesta este vizualizat.

Modificări aduse design-ului grafic al unui document (sau a mai multor documente) pot fi aplicate rapid și ușor, prin editarea câtorva linii în fișierul CSS pe care o folosesc, mai degrabă prin schimbarea de marcare în documente.

Caietul de sarcini CSS descrie un sistem de prioritate pentru a stabili stilul de reguli ce se aplică în cazul în care o functie este in antiteza cu un anumit element. În această așa-numita cascadă, prioritățile atribuite regulilor sunt calculate, astfel încât rezultatele sunt previzibile.

Caietul de sarcini CSS este menținut de către World Wide Web Consortium (W3C). Internet de tip media (tip MIME) text / css este înregistrat pentru utilizare cu CSS RFC 2318 (martie-1998). W3C opereaza pe un serviciu gratuit de validare pentru documente CSS.

CSS a fost propus pentru prima dată de Håkon Wium Lie pe 10 octombrie 1994. La acea vreme, Lie lucra cu Tim Berners-Lee la CERN. O serie de alte limbaje pentru web au fost propuse în acel timp și discuții cu privire la listele publice în interiorul World Wide Web Consortium a rezultat prima recomandare W3C CSS (CSS1). Fiind lansat în 1996, in special, le propunerea lui Bert Bos; el a devenit co-autor al CSS1 și este considerat ca fiind co-creator al CSS.

CSS 1

Prima specificație CSS pentru a deveni o recomandare oficială W3C CSS nivel 1, publicat la data de 17 decembrie 1996, Håkon Wium Lie și Bert Bos sunt creditati ca dezvoltatorii inițiali. Printre capacitățile sale sunt suportul pentru:

Proprietățile de fonturi precum setul de caractere și accentul

Culoarea de text, fundaluri și alte elemente

Atribute text, cum ar fi spațierea între cuvinte, litere și rânduri de text

Alinierea de text, imagini, tabele și alte elemente

Marja de frontieră, umplutură și de poziționare pentru majoritatea elementelor

Identificarea unică și clasificarea generică a grupurilor de atribute

W3C nu mai păstrează recomandarea CSS 1.

CSS 2

Caietul de sarcini pentru CSS nivelul 2 a fost dezvoltat de către W3C și publicat ca o recomandare, în mai 1998. Ca superset al CSS 1, CSS 2 include o serie de noi capabilități, cum ar fi poziționarea absolută, relativă și fixă ​​a elementelor și z-index, conceptul de tipuri de suporturi, suport pentru foi de stil fonetică (care ulterior au fost înlocuite cu modulele de vorbire CSS 3) și text bidirecțional, și noi proprietăți de fonturi, cum ar fi umbre.

W3C nu mai păstrează recomandarea CSS 2.

CSS 2.1

CSS nivel 2 revizie 1, adesea menționată ca "CSS 2.1", rezolvă unele erori în CSS 2, îndepărtează caracteristicile interoperabile și suplimentează extensii de browser pentru caietul de sarcini. Pentru a se conforma Procesului W3C pentru standardizarea specificațiilor tehnice, CSS 2.1 a mers înainte și înapoi între proiectul de stare de lucru și statutul de recomandare de mai mulți ani. CSS 2.1 a devenit mai întâi o recomandare la 25 februarie 2004, dar a revenit la un proiect de lucru pe 13 iunie 2005 pentru o examinare ulterioară.

CSS 2.1 a revenit la recomandare la 19 iulie 2007 și apoi actualizat de două ori în 2009. Cu toate acestea, deși că s-au făcut modificări și clarificări, sa revenit asuprea ultimului proiect de apel de lucru la 7 decembrie 2010.

CSS 2.1 a mers ca recomandare propusă la data de 12 aprilie 2011. După ce a fost examinat de către Comitetul consultativ W3C, acesta a fost în cele din urmă publicat ca o recomandare W3C la 7 iunie 2011.

CSS 3

Spre deosebire de CSS 2, care este o mare specificație unică definind diferite caracteristici, CSS 3 este împărțit în mai multe documente separate, numite "module". Fiecare modul adaugă capabilități noi sau extinde caracteristicile definite în CSS 2, păstrând compatibilitatea cu versiunile anterioare. Lucrările la nivelul CSS 3 a început din momentul publicării originalului pentru recomandarea CSS 2.

Din cauza modularizării, au fost stabilite o serie de module. În iunie 2012, există peste cincizeci de module CSS publicate de Grupul de lucru al CSS și patru dintre acestea au fost publicate sub formă de recomandări formale:

2012-06-19: Mass-media question

2011-09-29: Namespace

2011-09-29: Reserch level 3

2011-06-07: Color

3.3 PHP

PHP este un limbaj de scripting server-side proiectat pentru dezvoltările web, dar, de asemenea, utilizat ca un limbaj de programare de uz general. Inițial creat de Rasmus Lerdorf în 1994, punerea în aplicare a PHP-ului este produs de Grupul PHP. PHP-ul inițial a fost creat pentru Personal Home Page, dar acum este utilizat recursiv PHP: Hypertext Preprocessor.

Codul PHP poate fi încorporat în codul HTML, sau poate fi utilizat în combinație cu diverse sisteme web, sisteme de management al conținutului web și a unor cadre de web. Codul PHP este de obicei procesat de un interpret PHP implementat ca un modul în serverul web sau ca Common Gateway Interface (CGI) executabil. Server-ul web combină rezultatele codului PHP interpretat și executat, care poate fi orice tip de date, inclusiv imagini, cu pagina web a generat. Codul PHP poate fi, de asemenea, efectuat cu o interfață de linie de comandă (CLI) și pot fi utilizate pentru a pune în aplicare aplicații grafice de sine stătătoare.

Standardul PHP interpret, alimentat de motorul Zend, este un software gratuit eliberat sub licența PHP. PHP-ul a fost portat la o scara larga și poate fi instalat pe majoritatea serverelor web, pe aproape orice sistem de operare și platformă, în mod gratuit.

Limbajul PHP a evoluat fără o specificație formală scrisă sau standard, până în 2014. Din 2014 încoace, a inceput sa se creeze o specificație formală a PHP-ului.

In timpul anului 2010 s-au intensificat eforturile în vederea standardizării și a schimbului de cod în aplicațiile PHP prin proiecte, cum ar fi PHP-FIG sub forma PSR –initiative, precum manager de dependență composer și depozitul Packagist.

Rasmus Lerdorf a scris componenta originală Common Gateway Interface (CGI), împreună cu Andi Gutmans și Zeev Suraski, care a rescris parserul care a format PHP 3.

Dezvoltarea PHP a început în 1995, când Rasmus Lerdorf a scris mai multe programe de Common Gateway Interface (CGI) în C, pe care le-a folosit pentru a menține pagina sa personală. El a extins lucrul cu formulare web comunicand cu baze de date, și a făcut apel la aceast lucru în aplicatia "Personal Home Page / Formulare Interpret" sau PHP / FI.

PHP / FI ar putea fi folosite pentru a construi aplicații simple, aplicații web dinamice. Pentru a accelera raportarea a erorilor și de a îmbunătăți codul, Lerdorf a anunțat inițial lansarea PHP / FI ca "Unelte personale Prima pagina (Instrumente PHP) versiunea 1.0" pe Usenet, discuție de grup comp.infosystems.www.authoring.cgi pe 8 iunie, 1995. Această versiune a avut deja funcționalitatea de bază pe care PHP o are începând cu 2013. Aceasta a inclus variabile Perl, cum ar fi, manipulare formă și capacitatea de a încorpora HTML. Sintaxa semăna cu cea din Perl, dar a fost mai simplă, mai limitată și mai puțin consistentă.

PHP-ul nu a fost destinat să fie un nou limbaj de programare, dar a crescut organic. Lerdorf a observat în retrospectivă: "Nu știu cum să-l oprească, nu a existat niciodată nici o intenție de a scrie un limbaj de programare […] Nu am absolut nici o idee despre cum să scrie un limbaj de programare, am continuat să adaug următorul pas logic pe drum." O echipă de dezvoltare a început să se formeze și, după luni de muncă și de testare beta, a fost lansat oficial PHP / FI 2 în noiembrie 1997.

Faptul că PHP nu a fost proiectat inițial, ci a fost dezvoltat organic, a dus la numirea unor funcții inconsecvente și ordonarea inconsistentă a parametrilor lor. În unele cazuri, numele funcțiolor au fost alese pentru a se potrivi cu bibliotecile de nivel inferior ale PHP, un fel de "ambalaj". În unele versiuni foarte timpurii ale PHP-ului s-a folosit lungimea numelor de funcții pe plan intern ca o funcție hash, deci nume au fost alese pentru a îmbunătăți distribuția valorilor hash.

PHP 3 / 4

Zeev Suraski și Andi Gutmans a rescris parserul în 1997 și au format baza de PHP 3, schimbându-i numele limbii recursiv în acronimul PHP: Hypertext Preprocessor. Ulterior, testarea publică a PHP 3 a început, iar lansarea oficială a venit în iunie 1998. Apoi, Suraski și Gutmans au început o nouă rescriere a nucleului PHP, producând motorul Zend în 1999. De asemenea, au fondat Zend Technologies in Ramat Gan, Israel.

La data de 22 mai 2000 PHP 4, alimentat de Zend Engine 1.0, PHP a fost lansat. Începând cu luna august 2008, această ramură a ajuns la versiunea 4.4.9. PHP 4 nu mai este în curs de dezvoltare și nici nu va fi lansat în orice actualizări de securitate.

PHP 5

La data de 14 iulie 2004, PHP 5 a fost lansat, propulsat de un motor II nou Zend. PHP 5 a inclus caracteristici noi, cum ar fi suport îmbunătățit pentru programarea orientată pe obiecte, PHP Data Objects (DOP) – extensie (care definește o greutate redusă și interfață consistentă pentru accesarea bazelor de date), precum și numeroase îmbunătățiri de performanță. În 2008 PHP 5 a devenit singura versiune stabilă în curs de dezvoltare. Legarea târzie statică lipsea din PHP, asa ca a fost adăugată în versiunea 5.3.

De-a lungul timpului, PHP-ul a devenit disponibil pe majoritatea sistemelor existente de operare pe 32 de biți și pe 64 de biți, prin construirea lor din codul sursă PHP. Pentru versiunile PHP 5.3 și 5.4, era disponibil doar pe Microsoft Windows in distribuții binare pe 32 de biți x86, care necesita modul de compatibilitate pentru windows pe 32 de biți, dar si utilizat pe Internet Information Services (IIS), pe o platforma windows pe 64 de biți. PHP versiunea 5.5 s-a făcut pe 64 de biți x86-64, disponibil pentru Microsoft Windows.

PHP 6 și Unicode

PHP a primit recenzii mixte din cauza lipsei de suport Unicode nativ la nivel de limbă de bază. În 2005, un proiect condus de Andrei Zmievski a fost inițiat pentru a aduce suportul Unicode nativ de-a lungul PHP-ului, prin încorporarea International Components pentru Unicode (ATI ) bibliotecă, și care reprezintă șiruri de text ca UTF-16 pe plan intern. Din moment ce acest lucru ar provoca schimbări majore atât interne ale limbii cat și a codului de utilizator, acesta a fost planificat să elibereze acest lucru ca versiunea 6.0 a limbajului, împreună cu alte caracteristici majore apoi în dezvoltare.

Cu toate acestea, o parte din dezvoltatori care au înțeles modificările necesare, precum și probleme de performanță care rezultă din conversia la și de la UTF-16, care este rar utilizat într-un context de web, a dus la întârzieri în proiect. Ca rezultat, PHP 5.3 a fost creată în anul 2009, cu multe non-Unicode caracteristici de back-up de la PHP 6, în special namespace.

În martie 2010, proiectul în forma sa actuală, a fost abandonat în mod oficial, și o versiune PHP 5.4 a fost preparat, ce conține cele mai multe caracteristici de bază non-Unicode de la PHP 6, cum ar fi trăsături și închiderea bind. Speranțele inițiale au fost că un nou plan s-ar fi format pentru integrarea Unicode, dar începând cu 2014 nici unul nu a fost adoptat.

PHP 7

Pe parcursul anilor 2014 și 2015, a fost elaborată o nouă versiune majoră PHP, care a fost numărat ca PHP 7. Numerotarea acestei versiuni a implicat o dezbatere. În timp ce PHP 6 Unicode (experiment) nu a fost eliberat, mai multe articole și titluri de carte de referință PHP 6, ar fi putut provoca confuzie în cazul în care o nouă versiune ar reutiliza numele. După vot, numele PHP 7 a fost ales.

Fundația PHP 7 este o ramură PHP, care a fost numită inițial generația următoare (phpng). Acesta a fost scris de Dmitry Stogov, Xinchen Hui și Nikita Popov, având drept scop optimizarea performanței PHP restructurand Zend Engine și păstrând în același timp compatibilitatea limbajului aproape completă. In 14 iulie 2014, pe bază de WordPress, care a servit ca suita principală de referință pentru proiectul phpng, a aratat o crestere de aproape de 100% în performanță. Modificări de la phpng sunt, de asemenea de așteptat pentru a se face mai ușor, pentru a îmbunătăți performanța în viitor, ca structuri de date mai compacte și alte modificări ce sunt considerate ca fiind mai potrivite pentru o migrare de succes a unui compilator just-in-time (JIT). Deoarece s-au facut modificările importante, Zend Engine este de asemenea remodelat și este numit Zend Engine 3. Zend Engine 2 utilizat în PHP 5.

Din cauza schimbărilor interne majore în phpng, acesta trebuie să primească un nou număr mare de versiune PHP, în conformitate cu procesul de eliberare al PHP. Versiuni majore ale PHP sunt lăsate să rupă compatibilitãtii de cod și prin urmare, PHP 7 a prezentat o oportunitate pentru alte îmbunătățiri dincolo de phpng care necesită refacerea compatibilitãții.

În special, acesta a implicat următoarele modificări:

Mai multe mecanisme de eroare fatal au fost înlocuite cu unele excepții moderne orientate obiect

Sintaxa pentru dereferencing variabila a fost remodelate să fie pe plan intern mai coerentă și completă, care să permită utilizarea operatorilor ->, [], (), {}, și :: cu expresii semnificative arbitrare

Suport pentru moștenire PHP 4-style metode de constructor a fost descurajată

Declarația foreach a fost modificată pentru a fi mai previzibilă

Constructorii pentru puținele clase de built-in pentru PHP, a revenit nul la eșec și au fost modificate pentru a arunca o excepție

Mai multe interfețe neîntreținute sau programare aplicație server (SAPIs) și extensiile au fost eliminate din nucleul PHP, mai ales extensia mysql moștenire

Comportamentul operatorului listă () a fost modificat pentru a elimina suportul pentru siruri de caractere

Suport pentru moștenire ASP stil delimitatoare cod PHP (<% si%>, <script language = php> și </ script>) a fost îndepărtat

O supraveghere care să permită o declarație pentru a avea mai multe clauze implicite a fost fixată

Suportul pentru numerele hexazecimale în unele conversii implicite din siruri de caractere la tipurile de număr a fost îndepărtat

Operatorii de stânga-dreapta și shift-Shift au fost modificați pentru a se comporta mai consecvent pe toate platformele

Conversii între numere întregi și numere reale cu virgulă au fost strânse și puse în aplicare mai coerent pe mai multe platforme

PHP 7 a inclus, de asemenea, noi caracteristici lingvistice. Cel mai notabil, a introdus declarații de tip întoarcere pentru funcții, care completează declarațiile de tip parametru existente și suport pentru tipurile de scalare (număr întreg, float, string, și booleene) în parametru.

3.4 Symfony

Symfony este un cadru de aplicație web PHP pentru aplicații MVC. Symfony este un software gratuit și eliberat sub licență MIT. Site-ul web symfony-project.com a fost lansat la data de 18 octombrie 2005.

Symfony își propune să accelereze crearea și întreținerea aplicațiilor web și înlocuirea sarcinilor de codificare repetitive.

Symfony are o regie de performanță scăzută utilizat cu un cache bytecode.

Symfony are ca scop crearea unor aplicații robuste într-un context de întreprindere, și își propune să le ofere dezvoltatorilor un control deplin asupra configurației: din structura directorului bibliotecilor străine, aproape totul poate fi personalizat. Pentru a se potrivi liniile directoare de dezvoltare a întreprinderii, Symfony este la pachet cu instrumente suplimentare pentru a ajuta la proiecte de testare sau depanare.

Symfony a fost puternic inspirat de alte cadre de aplicații web, cum ar fi Ruby on Rails sau Django.

Symfony folosește intens proiecte PHP open-source existente, ca parte a cadrului, inclusiv:

Doctrine

DOP

PHPUnit, utilizat pentru testare unitare

Swift Mailer, o bibliotecă de e-mail

Symfony, de asemenea, face uz de propriile sale componente, care sunt disponibile în mod liber pe site-ul Symfony.

Componentele pot fi utilizate pentru diverse alte proiecte:

Symfony YAML, un interpretor YAML bazat pe Spyc

Symfony Eveniment Dispecer

Injector Symfony Dependență, un injector de dependență

Symfony Templating, un motor de templating

Cu ajutorul plugin-urilor, Symfony este capabil sa sprijineasca JavaScript și multe alte proiecte PHP, cum ar fi:

jQuery

script.aculo.us, pentru efecte vizuale

lessphp, un convertor mai puțin la-CSS

TinyMCE sau CKEditor, pentru editarea textului îmbogățit

TCPDF, o biblioteca PHP pentru generarea documentelor PDF

Includerea și punerea în aplicare a unei biblioteci JavaScript este lăsată la latitudinea utilizatorului.

Cateva cuvinte despre site-uri bazate pe Symfony..

Symfony este utilizat de open-source Q & A de serviciu.La un moment dat, a fost folosit pentru 20 de milioane de utilizatori ai Yahoo!

Symfony2 este utilizat de OPENSKY, o platformă socială de cumpărături, iar cadrul Symfony este, de asemenea, utilizat masiv de jocuri multiplayer in browser-ul on-line eRepublik, precum și de cadrul de management al conținutului eZ Publish în versiunea 5.

Drupal 8, phpBB si o serie de alte aplicații mari au inclus componente ale Symfony. Symfony2 este, de asemenea, utilizat de Meetic, una dintre cele mai mari platforme de dating online din lume, pe cele mai multe dintre site-urile sale pentru punerea în aplicare a logicii de afaceri în backend.

 Componentele Symfony sunt utilizate și în alte cadre de aplicații web, inclusiv Laravel, care este un alt cadru stivă completă și Silex, care este un microframework.
Din 12 februarie 2013 este utilizat masiv wiki-site-ul de baze de date de jocuri video GiantBomb.com, convertit de la Django la Symfony în urma unei achiziții.
Site-ul Vogue Franta este, de asemenea, construit pe cadrul Symfony

3.5 Doctrine

Doctrine a fost început de Konsta Vesterinen, de asemenea, cunoscut sub numele de zYne- inițială a proiectului Comite a fost făcută la data de 13 aprilie 2006. Pe măsură ce proiectul a devenit mai matur, adoptarea a început să se ridice. Înainte de mult timp, comunitatea a fost activă și primea contribuții regulate, printre altele, din proiectul Google Summer of Code.

Doctrine 1.0.0 a fost lansat la data de 1 septembrie 2008.

Prima versiune stabilă a Doctrinei 2.0 a fost lansat la 22 decembrie 2010, după 2,5 ani de pornire de dezvoltare dedicate la începutul anului 2008.

Doctrine a fost influențat de zeci de proiecte și multe persoane diferite. Cele mai mari influențe au fost Java ORM Hibernate si ActiveRecord de la Ruby on Rails. Ambele aceste soluții ORM au implementat o soluție complet prezentate în limbile Java și Ruby. Scopul proiectului Doctrine este de a construi o soluție la fel de puternică pentru limbajul PHP, pentru site-urile de mare de sarcină care trebuie să mențină un flux constant de vizitatori. Doctrine ORM poate fi utilizat pentru a îmbunătăți performanța acestor site-uri web.

Proiectul Doctrine este un set de biblioteci PHP, în primul rând axat pe furnizarea de servicii de persistența și funcționalitate. Proiectele sale sunt un premiu relațional-obiect (ORM) și stratul de bază de date de abstractizare este construit deasupra.

Una dintre caracteristicile cheie este opțiunea de a scrie interogări în baza de date Doctrine Query Language (DQL), un dialect orientat-obiect SQL.

Entitățile din Doctrine sunt Obiecte PHP care contin proprietati persistable. O proprietate persistablă este o variabilă, instanță a entității care este salvat și extrasă din baza de date prin capabilitățile de cartografiere a datelor prin intermediul administratorului Entitate.

O punere în aplicare a modelului mapper date:

  $ = Utilizator nou utilizator ();
  $ User-> name = "john1";
  $ User-> = parola "doe";
  $ EntityManager-> persistă (utilizator $);
  $ EntityManager-> spălare ();
  echo "Utilizatorul cu id $ user-> id a fost salvat.";

Doctrine 1 urmează modelul de înregistrare activ pentru lucrul cu date, în cazul în care o clasă corespunde unui tabel de baze de date.

De exemplu, în cazul în care un programator a dorit să creeze un nou obiect "User" într-o bază de date, nu mai este nevoie ca el să scrie interogări SQL, ci ar putea folosi următorul cod PHP:

  $ = Utilizator nou utilizator ();
  $ User-> name = "john";
  $ User-> = parola "doe";
  $ User-> save ();
  echo "Utilizatorul cu id $ user-> id a fost salvat.";

O caracteristică a Doctrine este nivelul scăzut de configurare, care este necesară pentru a începe un proiect. Doctrine poate genera clase de obiecte dintr-o bază de date existentă, iar programatorul poate specifica atunci relațiile, unde se adaugă funcționalitați personalizate la clasele generate. Nu este nevoie să genereze sau să mențină scheme complexe de baze de date XML, așa cum se vede în multe alte cadre.

O altă caracteristică esențială a Doctrine este abilitatea de a adauga opțional interogări de baze de date, scrise într-un OO (orientat obiect) SQL dialect numit DQL (Doctrine Query Language), inspirat de HQL. În mod alternativ, clasa QueryBuilder (Doctrine_Query în Doctrine 1) permite să se construiască interogări prin interfață. Aceste interfețe pot oferi dezvoltatorilor alternative puternice la SQL, care să mențină flexibilitatea și încă permit trecerea la baze de date back-end, fără a necesita nici o dublare de cod.

Scrierea de interogări în mod explicit nu este întotdeauna necesară, Doctrine preia automat obiectele aferente. Proiectele mici pot fi ușor construite fără interogări de scriere.

Alte caracteristici notabile ale doctrinei sunt:

suport pentru cârlige (metode care pot valida sau modifica baza de date cu intrare și de ieșire) și ascultătorii – eveniment de a structura logica legate de afaceri;

moștenire de agregare in coloană (obiecte similare pot fi stocate într-un tabel de bază de date, cu o singură coloană de tip specificând subtipul obiectului particular – subclasa corectă este întotdeauna returnată atunci când o interogare se face);

un cadru de cache, utilizarea mai multor backend, cum ar fi memcached, SQLite sau APC;

tranzacții ACID;

migrarea bazelor de date;

o funcție pentru a combina mai multe fișiere PHP într-una singură

3.6 Javascript

JavaScript este un nivel înalt, dinamic, netipizat și interpretat în limbajul de programare. A fost standardizat în specificația limbajului ECMAScript. Alături de HTML și CSS, acesta este unul dintre cele trei mari tehnologii de bază, de producție mondială, de conținut Wide Web. Majoritatea site-urilor îl folosesc și este susținut de toate browserele moderne Web fără a plug-in-uri. JavaScript are funcții de primă clasă, este un limbaj multi-paradigmă, suportul orientat spre obiect pe bază de prototip, stiluri de programare imperative, și funcționale. JavaScript conține un API pentru lucrul cu text, tablouri, date și expresii regulate, dar nu include nici un input/output, cum ar fi crearea de rețele, stocare, sau facilități grafice, bazându-se doar pe gazda mediului în care este încorporat.
Cu toate că există asemănări exterioare puternice între Javascript si Java, inclusiv numele limbii, sintaxa și bibliotecile standard respective sunt distincte și diferă foarte mult în designul lor. JavaScript a fost influențat de limbaje de programare, cum ar fi Self și Scheme.
JavaScript este de asemenea folosit în medii care nu sunt bazate pe Web, cum ar fi documente PDF, browsere specifice site-ului, și widget-uri desktop. Mașinile mai noi și mai rapide JavaScript virtuale (SMV) și platforme construite pe ele au crescut, de asemenea, popularitatea JavaScript pentru aplicații Web server-side. Pe partea de client, JavaScript a fost pus în aplicare în mod tradițional ca un limbaj interpretat, dar mai multe browsere recente efectueaza compilare just-in-time. Este, de asemenea, utilizat în dezvoltarea jocurilor, crearea de aplicații desktop și mobile, precum și programarea de rețea de server-side cu medii de rulare, cum ar fi Node.js.

JavaScript a fost inițial dezvoltat în 10 zile, in mai 1995 de către Brendan Eich, în timp ce lucra pentru Netscape Communications Corporation. Într-adevăr, în timp ce concura cu Microsoft pentru adoptarea de utilizarea tehnologiilor Web si platforme, Netscape a considerat ca client-server-ul lor care oferă un sistem de operare distribuit cu o versiune portabilă de Java Sun Microsystems și furnizează un mediu în care applet-uri ar putea fi rulat. Deoarece Java a fost un concurent al C ++ și care vizează programatori profesioniști, Netscape a dorit, de asemenea, un limbaj ușor interpretat, care ar completa Java apelând la programatori neprofesionisti, cum ar fi Microsoft Visual Basic.
Deși a fost dezvoltat sub numele Mocha, limbajul a fost numit oficial LiveScript atunci când a fost expediat pentru prima data in versiuni beta ale Netscape Navigator 2.0 în septembrie 1995, dar a fost redenumit JavaScript, atunci când a fost implementat în versiunea browser-ului Netscape 2.0B3.

Schimbarea numelui de la LiveScript JavaScript a coincis cu adăugarea de suport pentru tehnologia Java în browser-ul Web Netscape Navigator. Alegerea finală a numelui provocat confuzie, dând impresia că limbajul a fost un spin-off al limbajului de programare Java, iar alegerea a caracterizat ca un truc de marketing de Netscape pentru a da JavaScript semn distinctiv a ceea ce a fost atunci, un nou limbaj de programare Web.
Există o concepție greșită comună, că limbajul JavaScript a fost influențat de o pagină Web anterioară, limbaj de scripting dezvoltat de Nombas numit C– (a nu se confunda cu C– mai târziu, creat în 1997). Brendan Eich, cu toate acestea, nu a auzit de C– înainte de a crea LiveScript. Nombas a făcut pitch-încorporat pagina web scripting lor Netscape, deși pagina de web scripting nu a fost un concept nou, așa cum se arată prin ViolaWWW. Nombas mai târziu, a oferit JavaScript în loc de C– în produsul lor ScriptEase și a făcut parte din grupul care TC39 standardizat ECMAScript.

Cea mai comună utilizare a JavaScript este de a adăuga un comportament client-side la pagini HTML, Dynamic HTML cunoscut de asemenea (DHTML). Script-urile sunt incorporate sau incluse de pagini HTML și să interactioneze cu Document Object Model (DOM) al paginii. Câteva exemple simple de această întrebuințare sunt:

Încărcarea conținutului pentru pagină nouă sau transmiterea de date către server prin AJAX, fără a reîncărca pagina (de exemplu, o rețea socială ar putea permite utilizatorului să posta actualizări de stare, fără a părăsi pagina) 

Animație de elemente de pagină, decolorare-le în afară, redimensionarea ei, mutarea lor, etc.  

 Conținut interactiv, de exemplu, jocuri, precum și redarea audio și video  

 Se pot valida valorile de intrare a unui formular Web pentru a vă asigura că acestea sunt acceptabile înainte de a fi transmise la server.
  

 Deoarece codul JavaScript poate rula local în browser-ul unui utilizator (mai degrabă decât pe un server de la distanță), browser-ul poate răspunde la acțiunile utilizatorului rapid, făcând o aplicare mai receptivă. Mai mult decât atât, cod JavaScript poate detecta acțiunile utilizatorului. Aplicații, cum ar fi Gmail pot profita de acest lucru: o mare parte din logica interfeței de utilizator este scrisă în JavaScript și pot cere de informații (cum ar fi conținutul unui mesaj e-mail) la server. Tendința Ajax exploatează în mod similar, această putere.

Un motor JavaScript (de asemenea, cunoscut ca interpret JavaScript sau punerea în aplicare JavaScript) este un interpret care interpretează codul sursă JavaScript și execută script-ul în mod corespunzător. Primul motor JavaScript a fost creat de către Brendan Eich de la Netscape Communications Corporation, pentru browser-ul Web Netscape Navigator. Motorul, cu numele de cod SpiderMonkey, este implementat în C. Acesta a fost actualizat, deoarece (în JavaScript 1.5) pentru a se conforma la ECMA-262 Edition 3. Motorul Rhino, creat în primul rând de Norris Boyd (fosta de Netscape, acum la Google) este o punere în aplicare JavaScript în Java. Rhino, cum ar fi SpiderMonkey, este ECMA-262 Edition 3 conforme.
Un browser Web este de departe mediul gazdă cel mai comun pentru JavaScript. Browsere web creează de obicei "obiecte gazdă" pentru a reprezenta Document Object Model (DOM) în JavaScript. Serverul Web este un alt mediu gazdă comun. Un server de web JavaScript ar expune în mod tipic obiecte gazdă care reprezintă cererea HTTP și obiecte de răspuns, pe care un program JavaScript ar putea apoi interoga și manipula pentru a genera dinamic pagini web.
Pentru că JavaScript este singurul limbaj pe care cele mai populare browsere împărtășesc suport, aceasta a devenit un limbaj țintă pentru multe cadre în alte limbi, chiar dacă JavaScript nu a fost niciodată intenționat să fie un astfel de limabj. În ciuda performanței limitării inerente sale natura dinamică, viteza tot mai mare a motoarelor JavaScript a făcut limbajul o țintă de compilare în mod surprinzător este fezabil. Pentru că JavaScript rulează în medii foarte variate, o parte importanta de testare si de depanare este de a testa și de a verifica dacă JavaScript funcționează pe mai multe browsere.
Interfetele DOM pentru manipularea paginilor web nu fac parte din standardul ECMAScript, sau de la sine JavaScript. În mod oficial, interfețele DOM sunt definite printr-un efort de standardizare separat de W3C; în practică, implementări de browser diferă de la un standard la celălalt și nu toate browserele executa JavaScript.
Pentru a face față acestor diferențe, autorii JavaScript pot încerca să scrie cod conform cu standardele care vor fi executate corect de cele mai multe browsere; în lipsa acestuia, ei pot scrie cod care verifică prezența anumitor caracteristici de browser și se comportă în mod diferit în cazul în care acestea nu sunt disponibile. În unele cazuri, două browsere pot pune în aplicare o caracteristică, dar cu un comportament diferit, iar autorii pot găsi practic pentru a detecta ce browser se execută și de a schimba comportamentul script-ul lor pentru a se potrivi. Programatorii pot utiliza, de asemenea, biblioteci sau toolkit care iau în considerare diferențele de browser.

Mai mult decât atât, script-urile nu pot fi utilizate de absolut toti utilizatorii. De exemplu, un utilizator poate:

utiliza un browser vechi sau rar, cu suport DOM incomplet sau neobișnuit    

utiliza un PDA sau browser-ul telefonului mobil, care nu poate executa JavaScript   

 executa JavaScript dezactivat ca măsură de securitate  

  utiliza un browser de vorbire din cauza unui handicap vizual.

Pentru a sprijini acești utilizatori, autori Web pot încerca să creeze pagini care degradeaza browserele care nu suportă JavaScript. În special, pagina trebuie să rămână utilizabilă, chiar daca JavaScript-ul nu poate fi citit. O abordare alternativă, este de a gasi conținutul primului autor folosind tehnologii de bază care funcționează în toate browserele, apoi a spori conținutul pentru utilizatorii care au JavaScript activat. Acest lucru este cunoscut sub numele de îmbunătățire progresivă. JavaScript și DOM furnizează potențialul pentru autori rău intenționate de a livra script-uri pentru a rula pe un computer client prin intermediul Web. Autorii de browser conțin acest risc, folosind două restricții. În primul rând, script-uri care rulează într-un mediu de testare în care acestea se pot efectua doar acțiuni legate de Web, nu sarcini de programare de uz general, cum ar fi crearea de fișiere. În al doilea rând, script-uri sunt limitate de aceeași politică de origine: script-uri de la un site web nu au acces la informații, cum ar fi nume de utilizator, parole sau cookie-uri trimise către un alt site. Cele mai multe bug-uri legate de securitate ale JavaScript sunt încălcări ale aceleiași politici de origine.

Există subseturi generale JavaScript – adsafe, Secure ECMA Script (SES) care furnizează un nivel mai ridicat de securitate, în special pe codul creat de către părți terțe (cum ar fi anunțuri). Caja este un alt proiect pentru încorporarea în condiții de siguranță și izolarea unor terțe părți JavaScript și HTML.
Politica de securitate reprezintă metoda principală intenționată de a se asigura că numai codul de încredere este executat pe o pagină Web.

O problemă de securitate legată de JavaScript este cross-site scripting, sau XSS, o încălcare a politicii de origine. Vulnerabilități XSS apar atunci când un atacator este capabil de a provoca un site web țintă, cum ar fi un site online banking, pentru a include un script rău intenționat în pagina web. Script-ul în acest exemplu se accesa apoi aplicația bancară cu privilegiile victimei, care ar putea divulga informații secrete sau transferul de bani, fără autorizație victimei. O soluție pentru vulnerabilitățile XSS este de a folosi HTML in evadarea ori de câte ori se afișeaza date nesigure.
Unele browsere includ o protecție parțială împotriva atacurilor XSS reflectate, în care atacatorul furnizează o adresă URL, inclusiv script rău intenționat. Cu toate acestea, chiar și utilizatorii acestor browsere sunt vulnerabi la alte atacuri XSS, cum ar fi cele în care codul malițios este stocat într-o bază de date. Numai proiectarea corectă a aplicațiilor Web de pe partea de server poate preveni complet XSS.
Vulnerabilitățile XSS poate avea loc, de asemenea, din cauza greșelilor de punere în aplicare de către autori de browser.
O altă vulnerabilitate cross-site este cererea cross-site fals sau CSRF. Acesta funcționează pentru că, în cazul în care site-ul țintă se bazează doar pe cookie-uri pentru autentificarea cererilor, apoi cererile inițiate de cod de pe site-ul atacatorului va efectua aceleași acreditări de conectare legitime ca solicitările inițiate de către utilizator. În general, soluția la CSRF este de a impune o valoare de autentificare într-un câmp ascuns, și nu numai în cookie-uri, pentru a autentifica orice solicitare care ar putea avea efecte de durată. Verificarea antetul HTTP Referrer poate ajuta, de asemenea.
"JavaScript hijacking" este un tip de atac CSRF în care un <script> tag-ul de pe site-ul unui atacator exploatează o pagină pe site-ul victimei, care returnează informații private, cum ar fi JSON sau JavaScript.

Printre soluțiile posibile se numără:

un simbol de autentificare în POST și parametrii GET pentru orice răspuns care returnează informații private

încrederea în client Misplaced

Dezvoltatorii de aplicatii client-server trebuie să recunoască faptul că clienții de încredere pot fi sub controlul atacatorilor. Autorul cererii nu poate presupune că codul său JavaScript va rula ca scop (sau deloc), deoarece orice secret, încorporat în cod poate fi extras printr-un adversar hotărât. Implicații sunt:

Autorii site-ului nu pot ascunde perfect modul în care funcționează JavaScript lor. Codul poate fi obfuscated, dar disimularile poate fi inversate.

 Formularul de validare JavaScript oferă doar confort pentru utilizatori, nu de securitate. În cazul în care un site verifică dacă utilizatorul a fost de acord cu termenii și condițiile sale de serviciu, sau filtrele de caractere nevalide din câmpurile pe care ar trebui să conțină doar numere, acesta trebuie să facă acest lucru pe server, nu numai clientul.

Script-urile pot fi dezactivate în mod selectiv, astfel încât JavaScript nu poate fi invocat pentru a împiedica realizarea unor operațiuni, cum ar fi clic dreapta pe imagine pentru a o salva.

Este o practică extrem de proastă pentru a încorpora informații sensibile, cum ar fi parole în JavaScript, deoarece acesta poate fi extrase de către un atacator.

În cadrul JavaScript, accesul la un depanator devine de neprețuit atunci când dezvoltă programe mari, non-triviale. Pentru că pot exista diferențe de implementare între diferitele browsere (în special în Document Object Model), este util să aibă acces la un depanator pentru fiecare dintre browserele care țintele o aplicație Web.
În plus față de Internet Explorer Developer Tools, trei debuggeri sunt disponibili pentru Internet Explorer: Microsoft Visual Studio este cel mai bogat dintre cei trei, urmat îndeaproape de Microsoft Script Editor (o componentă a Microsoft Office) și în cele din urmă Microsoft script-ul Debugger, care este mult mai mult de bază decât celelalte două. Gratuit, Microsoft Visual Web Developer Express oferă o versiune limitată a funcționalității de depanare JavaScript în Microsoft Visual Studio. Internet Explorer a inclus instrumente pentru dezvoltatori de la versiunea 8 (atinsă prin apăsarea tastei F12).

În comparație cu Internet Explorer, Firefox are un set mai cuprinzător de instrumente pentru dezvoltatori, care includ un program de depanare. Versiunile mai vechi ale Firefox, fără aceste instrumente, au folosit un addon de Firefox numit Firebug sau debuggerul Venkman. De asemenea, Web Inspector WebKit include un debugger JavaScript, care este utilizat în Safari. O versiune modificată numită clipire DevTools este utilizat în Google Chrome. Node.js are nod-inspector, un depanator interactiv, care se integrează cu Blink DevTools, disponibil în Google Chrome. Nu în ultimul rând, Opera include un set de instrumente numite Libelula.
În plus față de software-ul computerului nativ, există on-line JavaScript IDEs, ajutoarele de depanare sunt ele însele scrise în JavaScript și construite pentru a rula pe Web. Un exemplu este programul JSLint, dezvoltat de Douglas Crockford, care a scris pe larg despre limbaj. JSLint scanează codul JavaScript pentru conformitatea cu un set de standarde și orientări. Multe biblioteci pentru JavaScript, cum ar fi three.js, oferă link-uri de cod demonstrative, care pot fi editate de către utilizatori. Ele sunt, de asemenea, folosite ca instrument pedagogic de către instituții, cum ar fi Academia Khan pentru a permite studenților să experimenteze scrierea de cod într-un mediu în care se pot vedea de ieșire a programelor lor, fără a avea nevoie de nici o configurare dincolo de un browser Web.

3.7 Jquery

jQuery este un cross-platform, bibliotecă JavaScript concepută pentru a simplifica client-side scripting HTML. jQuery este cel mai populară bibliotecă JavaScript în folosință astăzi, cu instalare pe 65% din primele 10 milioane de site-uri ale traficului de pe Web. jQuery este gratuit, software-ul este open-source sub licența MIT.
Sintaxa jQuery este proiectată pentru a face mai usoara navigarea printr-un document, selectarea elemente DOM, crearea de animatii, si de a dezvolta aplicatii Ajax. jQuery oferă de asemenea capabilități pentru dezvoltatori pentru a crea plug-in-uri pe partea de sus a bibliotecii JavaScript. Acest lucru permite dezvoltatorilor să creeze abstractizari cu nivel scăzut și animație, efecte avansate și la nivel înalt și widget-uri. Abordarea modulară a bibliotecii jQuery permite crearea de pagini web dinamice puternice și aplicații Web.
Microsoft și Nokia au pachete jQuery pe platformele lor. Microsoft include jQuery pentru a fi utiliza în contextul cadrelor ASP.NET AJAX și ASP.NET MVC Microsoft Visual Studio în timp ce Nokia l-a integrat în platforma de dezvoltare pentru widget-ul Web Run-Time. jQuery a fost de asemenea utilizat în MediaWiki la versiunea 1.16.

jQuery, de fapt, este un DOM (Document Object Model), o biblioteca de manipulare. DOM este o reprezentare arborescentă, structura tuturor elementelor unei pagini Web și jQuery simplifică sintaxa pentru identificarea, selectarea și manipularea acestor elemente DOM. De exemplu, jQuery poate fi folosit pentru identificarea unui element din document cu o anumită proprietate (de exemplu, toate elementele cu tag-ul H1), schimbarea uneia sau mai multora dintre atributele sale (de exemplu, culoare, vizibilitate), sau făcându-l să răspundă la un eveniment ( de exemplu, un clic de mouse).
jQuery oferă, de asemenea, o paradigmă pentru o manipularea unui eveniment care merge dincolo de selecție de bază. Atribuirea evenimentului și definirea funcției de apel invers, evenimentul se face într-o singură etapă, într-o singură locație în cod. jQuery. De asemenea, are scopul de a încorpora alte funcționalități JavaScript extrem de utilizate (de exemplu, se estompeze in-uri și se estompeze out-uri, atunci când se ascune elemente, animații prin manipularea proprietăților CSS).

Avantajele folosirii jQuery sunt:

Încurajează separarea JavaScript-ului de HTML: Biblioteca jQuery oferă sintaxă simplă pentru adăugarea evenimentelor la DOM folosind JavaScript, mai degrabă decât adăugarea de atribute HTML, evenimentele pot apela funcțiile JavaScript. Astfel,se încurajează dezvoltatorii să se separe complet codul JavaScript de HTML.

 Concizie și claritate: jQuery promovează concizie și claritate, cu caracteristici cum ar fi funcții chainable și nume de funcții prescurtate.

Elimină incompatibilități cross-browser: Motoarele JavaScript ale diferitelor browsere diferă ușor, astfel de cod JavaScript care funcționează pentru un singur browser-ul poate să nu funcționeze pentru altul. La fel ca alte JavaScript toolkit, jQuery se ocupă de toate aceste neconcordanțe cross-browser și oferă o interfață consistentă, care funcționează în diferite browsere.

Extensibil: Evenimente noi, elemente și metode pot fi adăugate cu ușurință și apoi reutilizate ca plugin.

jQuery include următoarele caracteristici:

Selecții element DOM folosind multi-browser open source

Manipulare DOM bazată pe selectori CSS care utilizează numele și atributele elementelor, cum ar fi ID-ul și clasa, drept criterii pentru a selecta nodurile din DOM

Evenimente

Efecte și animații

Ajax

Promisiunea de obiecte pentru a controla prelucrarea asincronă

parsare JSON

Extensibilitate prin intermediul plug-in-uri

Utilități, cum ar fi detectarea caracteristică

Metodele de compatibilitate care sunt disponibile în mod nativ pe browserele moderne, cum ar fi inArray ()

Multi-browser (de a nu se confunda cu cross-browser) suport

Biblioteca jQuery este un singur fișier JavaScript care conțineȘ DOM, evenimente, efecte și funcțiile sale comune Ajax. Acesta poate fi inclus într-o pagină Web prin conectarea la o copie locală sau la una dintre multele copii disponibile de la serverele publice. jQuery are o rețea de livrare de continut (CDN) găzduit de MaxCDN. Google și Microsoft o gazduiasc la fel de bine.
<Script src = "jquery.js"> </ script>

Este de asemenea posibil să se includă jQuery direct din CDN:

<Script src = "https://code.jquery.com/jquery-latest.min.js"> </ script>

jQuery are două stiluri de utilizare:

Prin funcția $, care este o metodă pentru obiectul jQuery. Aceste funcții, numite adesea comenzi, sunt chainable ca toate acestea se întoarcă obiecte jQuery.

Prin $ – prefixat funcții. Acestea sunt funcții de utilitate, care nu acționează direct asupra obiectului jQuery.

Accesul la manipularea nodurilor DOM multiple în jQuery, de obicei, începe cu apelarea funcției $ cu un șir de caractere CSS. Aceasta returnează un obiect jQuery și face trimitere la toate elementele care se potrivesc in pagina HTML. $ ( "Div.test"), de exemplu, returnează un obiect jQuery cu toate elementele div ale testului de clasă. Acest set nod poate fi manipulat prin apelarea unor metode asupra obiectului jQuery returnat sau pe nodurile ei înșiși.jQuery include, de asemenea, modul .noConflict (). Acest lucru poate fi de ajutor, dacă jQuery este utilizat împreună cu alte biblioteci, care utilizează, de asemenea, $ ca identificator. În modul nu dintr-un conflict, dezvoltatorii pot folosi jQuery ca un înlocuitor pentru $, fără a pierde funcționalitatea.

3.8 MySQL

MySQL este un sistem de management al bazelor de date relaționale open-source (RDBMS). În iulie 2013 a fost al doilea RDBMS din lume utilizat pe scară largă , iar cel mai utilizat pe scară largă open-source modelul client-server RDBMS. Este numit după Michael Widenius (care este co-fondator al MySQL), în timp ce "SQL" reprezintă abrevierea pentru limba de interogare structurată. Proiectul de dezvoltare MySQL a făcut codul sursă disponibil sub termenii GNU General Public License, precum și în conformitate cu o varietate de acorduri de proprietate. MySQL a fost deținut și sponsorizat de către o singură firmă pentru profit, compania suedeză MySQL AB, deținută în prezent de către Oracle Corporation. Pentru utilizarea de proprietate, mai multe ediții plătite sunt disponibile, și oferă funcționalități suplimentare.
MySQL este o alegere populara de baze de date pentru utilizarea în aplicații web, și este o componentă centrală a lămpii utilizate pe scară largă open-source, stiva software de aplicație web (și alte stack-uri "AMP"). LAMP este un acronim pentru "Linux, Apache, MySQL, Perl / PHP / Python". Free-software proiecte open-source, care necesită un sistem full-featured de gestionare a bazelor de date folosesc adesea MySQL. Aplicațiile care utilizează baza de date MySQL includ: TYPO3, MODx, Joomla, WordPress, phpBB, MyBB, Drupal și alte software-uri. MySQL este de asemenea folosit în multe de profil înalt, site-uri pe scară largă, inclusiv Facebook, Twitter, Flickr, Google și YouTube.
Pe toate platformele, cu excepția Windows, se pot administrarea bazele de date MySQL sau se pot gestiona datele conținute în bazele de date. Utilizatorii pot utiliza instrumentele incluse în linia de comandă, sau pot a instala MySQL Workbench printr-o descărcare separată. Mai multe instrumente GUI sunt de asemenea disponibile.

MySQL este scris în C și C ++. MySQL funcționează pe mai multe platforme de sistem, inclusiv AIX, BSDI, FreeBSD, HP-UX, eComStation, i5 / OS, IRIX, Linux, sistemul de operare X, Microsoft Windows, NetBSD, Novell NetWare, OpenBSD, OpenSolaris, OS / 2 Warp, QNX, Oracle Solaris, Symbian, SunOS, SCO OpenServer, SCO UnixWare, Sanos și Tru64. Există, de asemenea, un port de date MySQL la OpenVMS.

Server-ul MySQL și bibliotecile utilizează distribuție dual-licențiere. Acestea sunt oferite sub GPL versiunea 2, începând de la 28 iunie 2000 (care în 2009 a fost prelungită cu o excepție de licență FLOSS) sau de a folosi o licență de proprietate.

Suportul poate fi obținut din manualul oficial. Suport gratuit suplimentar este disponibil în diferite canale IRC și forumuri. Ofertele Oracle plătite de suport prin intermediul produselor sale MySQL Enterprise. Acestea diferă în domeniul de aplicare al serviciilor și în preț. În plus, exista, o serie de organizații care sprijin și servicii, inclusiv MariaDB și Percona.
MySQL a primit recenzii pozitive, iar examinatorii a observat ea "efectuează extrem de bine " și că "interfețele de dezvoltator sunt acolo, iar documentația (să nu mai vorbim de feedback-ul în lumea reală prin intermediul site-uri Web și altele asemenea) este foarte, foarte bun". Acesta a fost de asemenea testat pentru a fi un "rapid, stabil și adevărat multi-user, multi-threaded sql server bază de date ".
MySQL a fost creat de o companie suedeză, MySQL AB, fondat de David Axmark, Allan Larsson și Michael "Monty" Widenius. Prima versiune a MySQL a apărut la data de 23 mai 1995. A fost inițial creat pentru uzul personal din mSQL bazat pe ISAM limbaj de nivel scăzut, pe care creatorii il considerau prea lent și inflexibil. Ei au creat o nouă interfață SQL, păstrând în același timp același API ca și mSQL. Prin păstrarea API în concordanță cu sistemul mSQL, mulți dezvoltatori au fost capabili de a utiliza MySQL în loc de mSQL antecedent.

La data de 15 iunie 2001, a NuSphere dat in judecata MySQL AB, TCX DataKonsult AB si autorii sai originali Michael ( "Monty") Widenius si David Axmark in US District Court din Boston, pentru "încălcarea contractului, interferența tortionara cu contracte de terți și relații și concurența neloială".
In anul 2002, MySQL AB a dat in judecata Progress NuSphere pentru drepturile de autor și încălcarea mărcilor comerciale în Statele Unite ale Americii judecătorie. NuSphere a pretins încălcat drepturile de autor MySQL prin legarea de cod GPL'ed MySQL cu masa de NuSphere Gemeni, fără a fi în conformitate cu licența. După o audiere preliminară în fața judecătorului Patti Saris la 27 februarie 2002, părțile au încheiat discuțiile de decontare și în cele din urmă decontată. După audiere, FSF a comentat că "Judecătorul Šariš a făcut clar că ea vede GNU GPL să fie o licență executorie și obligatorie."

În octombrie 2005, Oracle a achizitionat Innobase OY, compania finlandeza care a dezvoltat motorul de stocare InnoDB, care permite MySQL sa ofere functionalitate, cum ar fi tranzacții și chei străine. După achiziție, un comunicat de presă al Oracle a menționat că contractele care fac software-ul companiei disponibile la MySQL AB ar fi din cauza de reînnoire (și probabil renegocierea) ceva timp în 2006. În acest timp, la Conferința din aprilie 2006, MySQL a emis un comunicat de presă, care a confirmat că MySQL și Innobase OY fost de acord cu o prelungire "mai multi ani" acordul lor de licență.

In februarie 2006, Oracle a achizitionat Sleepycat Software, factorii de decizie din Berkeley DB, un motor de baze de date care furnizează baza pentru un alt motor de stocare MySQL. Acest lucru a avut un efect redus, așa cum nu a fost utilizat pe scară largă Berkeley DB, și a fost abandonată (nu a fost utilizat) în MySQL 5.1.12, o versiune de pre-GA MySQL 5.1 lansat în octombrie 2006. În ianuarie 2008, Sun Microsystems a cumparat MySQL pentru 1 miliard de $. În aprilie 2009, Oracle Corporation a încheiat un acord pentru a achiziționa Sun Microsystems, apoi proprietarii de MySQL, drepturile de autor și mărcile comerciale.

O mișcare împotriva achiziționarii MySQL Oracle, "Salvați MySQL" de la Oracle a fost început de către unul dintre fondatorii MySQL, Monty Widenius. Petiția de 50,000+ dezvoltatori și utilizatori a solicitat Comisiei Europene să blocheze aprobarea achiziției. În același timp, mai mulți lideri de opinie Software Liber (inclusiv Eben Moglen, Pamela Jones a Groklaw, Jan Wildeboer și Carlo Piana, care, de asemenea, a acționat în calitate de co-consilier în cadrul procedurii de regulament privind concentrările economice) a pledat pentru aprobarea necondiționată a fuziunii. Ca parte a negocierilor cu Comisia Europeană, Oracle sa angajat că serverul MySQL va continua până când, cel puțin în 2015, pentru a utiliza strategia de dual-licențiere lung utilizate de către MySQL AB, cu versiuni proprietare si GPL sunt disponibile.
În ianuarie 2009, înainte de achiziționarea de MySQL Oracle, Monty Widenius a inceput o cu MariaDB. MariaDB se bazează pe același cod de bază ca server MySQL 5.5 și își propune să mențină compatibilitatea cu versiunile Oracle furnizate.

MySQL poate fi construit și instalat manual din codul sursă, dar este mai frecvent instalat dintr-un pachet binar, cu excepția cazului sunt necesare personalizări speciale. Pe cele mai multe distribuții Linux, sistemul de administrare a pachetelor se poate descărca și instala MySQL cu un efort minim, deși o configurare ulterioară este adesea necesară pentru a regla setările de securitate și optimizare.
Cu toate că MySQL a început ca o alternativa low-end la bazele de date proprietare mai puternice, ea a evoluat treptat, pentru a sprijini pe scară mai mare are nevoie. Acesta este în continuare cel mai frecvent utilizat pentru implementările cu un singur server de scară medie, fie ca o componentă într-o aplicație web sau LAMP ca un server de baze de date de sine stătătoare. O mare parte din apelul MySQL își are originea în simplitatea sa relativă și ușurința de utilizare, care este activat de un ecosistem de instrumente open source, cum ar fi phpMyAdmin. În intervalul mediu, MySQL poate fi scalat prin implementarea pe un hardware mai puternic, cum ar fi un server multi-procesor cu GB de memorie.

Cu toate acestea, există limite pentru modul în care performanța poate scala pe un singur server ("extinderea"), multi-server MySQL ( 'scalarea') desfășurările sunt necesare pentru a oferi o performanță îmbunătățită și fiabilitate. O configurație tipică high-end poate include o bază de date de master puternic, care se ocupă de operațiuni de scriere a datelor și este replicat la mai multi slave care se ocupa de toate operațiunile de citit. Serverul de master împinge continuu evenimente slave conectați astfel încât, în cazul defectării unui slave poate fi promovat pentru a deveni master, minimizând timpii morți. Imbunătățiri suplimentare ale performanței poate fi atins prin cache, rezultatele de la interogări de baze de date în memorie, folosind memcached, sau de rupere jos o bază de date în bucăți mai mici, numite cioburi care pot fi repartizate pe un număr de clustere de servere distribuite.

Softwarele de backup sunt programe de calculator folosite pentru a efectua copie de rezervă; acestea creează copii exacte suplimentare de fișiere, baze de date sau calculatoare întregi. Aceste programe pot utiliza ulterior copiile suplimentare pentru a restabili conținutul original, în caz de pierdere de date.
Sistemul de fișiere instantaneu sau managerul instantaneu de volum de backup-uri sunt realizate prin utilizarea unui instrument extern furnizat de sistemul de operare (cum ar fi Manager de volum logic în Linux) sau un dispozitiv de stocare, cu sprijin suplimentar de la MySQL pentru a asigura coerența acestor instantanee.
Mysqldump este un instrument de backup logic inclus atât în ​​comunitate și Enterprise Edition MySQL. Acesta suporta backup-ul de la toate motoarele de stocare. MySQL Enterprise Backup este un utilitar de backup la cald inclus ca parte a abonamentului MySQL Enterprise de la Oracle, oferind backup de cald InnoDB nativ, precum și de rezervă pentru alte motoare de stocare.

XtraBackup este un open-source MySQL program software de backup. Anumite caracteristici notabile includ hot backup-uri, non-blocare pentru stocare InnoDB, backup-uri incrementale, streaming, backup-uri paralele comprimat, ștrangulare pe baza numărului de operații I / O pe secundă, etc.
Asigurarea unui grad de disponibilitate necesită o anumită cantitate de redundanță în sistem. Pentru sistemele de baze de date, redundanța ia în mod tradițional, sub forma de a avea un server primar care acționează ca un maestru, și utilizarea de replicare pentru a păstra Secundarele disponibile pentru a prelua, în cazul în care primar eșuează. Acest lucru înseamnă că "serverul" că aplicația se conectează la este, în realitate, o colecție de servere, nu un singur server. Într-un mod similar, în cazul în care aplicația utilizează o bază de date sharded, este, în realitate, care lucrează cu o colecție de servere, nu un singur server. În acest caz, o colecție de servere este de obicei menționată ca o fermă.

Unul dintre proiectele care au ca scop de mare disponibilitate pentru MySQL este MySQL Fabric, un sistem integrat de gestionare a unei colecții de servere MySQL, precum și un cadru peste care este construit și de înaltă disponibilitate și sharding bază de date. MySQL Fabric este open-source și este destinat să fie extensibil, ușor de utilizat, și pentru a sprijini procedura de executare chiar și în prezența unui eșec, oferind un model de execuție, de obicei, numit execuție elastic. MySQL biblioteci client sunt extinse, astfel încât acestea se ascund complexitatea de manipulare failover, în cazul unei defecțiuni de server, precum și în mod corect de expediere tranzacții cioburile. Din septembrie 2013 există suport pentru versiunile Fabric-conștient de conector / J, Conector / PHP, Conector / Python, precum și unele suport rudimentar pentru Hibernate și Doctrinei. Din luna mai 2014, MySQL Fabric este în stadiul general, disponibilitatea de dezvoltare.

3.9 PHP Storm

JetBrains PhpStorm este un cross-platform IDE comercial pentru PHP construit pe platforma JetBrains 'IDEA IntelliJ.

PhpStorm oferă un editor pentru PHP, HTML și JavaScript cu analiza on-the-fly de cod, de prevenire a erorilor și refactorizari automate pentru PHP și codul JavaScript. Completarea automată a codului PhpStorm suportă PHP 5.3, 5.4, 5.5, 5.6 & 7.0 (proiecte moderne, cât și cele vechi), inclusiv generatoare, în cele din urmă să aibăȘ cuvinte cheie, lista în foreach, namespaces, închideri, trăsături și sintaxă matrice scurtă. Acesta include un editor cu drepturi depline SQL cu rezultate de interogare care se pot modifica.
PhpStorm este construit pe IntelliJ IDEA, care este scris în Java. Utilizatorii pot extinde IDE prin instalarea plugin-uri create pentru platforma IntelliJ sau scrie propriile plugin-uri.
Toate caracteristicile disponibile în WebStorm sunt incluse în PhpStorm, care adaugă suport pentru PHP si baze de date, nave WebStorm cu plugin-uri pre-instalate JavaScript (cum ar fi pentru Node.js), care sunt disponibile pentru PhpStorm

PHP editor

PhpStorm oferă un editor de cod de bogat pentru PHP cu sintaxa, cod extins de configurare de formatare, on-the-fly verificarea erorilor și completarea automată a codului.
  PHP 5.3, 5.4, 5.5 și suport 5.6, inclusiv generatoare, să aibă, în cele din urmă cuvinte cheie, lista în foreach, folosind empty () pe rezultatul apelurilor de funcții și alte expresii, trăsături, închideri, acces membru al clasei pe instanțierea, sintaxa matrice scurtă , matrice dereferencing pe funcția de apel, literali binare, expresii în apeluri statice, etc. poate fi utilizat atât în ​​cazul proiectelor moderne, cât și cele vechi bazate pe PHP.

Codul de autocompletare finalizeaza clase, metode, nume de variabile, și cuvinte cheie PHP, plus nume frecvent utilizate pentru câmpuri și variabile în funcție de tipul lor.
    

Debugging și testare

 Xdebug sau Zend Debugger este un debugger vizual ușor de configurat pentru inspectarea variabilelor locale contextuale relevante și ceasuri definite de utilizator, inclusiv tablouri și obiecte complexe, și valorile de editare pe zbor.
   Script-urile pot fi profilate chiar de la PhpStorm fie cu XDebug sau Zend Debugger. Un raport agregat este disponibil, iar utilizatorul poate sări din statisticile de execuție direct funcția în codul PHP.
    Teste PHPUnit pot fi dezvoltate în PhpStorm și pot rula instantaneu dintr-un director, fișier sau clasă utilizând opțiunile meniului contextual cu acoperire de cod.

JavaScript, CSS și HTML caracteristici:

Completarea automată a codului pentru JavaScript, HTML si CSS (pentru tag-uri, cuvinte-cheie, etichete, variabile, parametri și funcții).

Suport HTML5.

JavaScript: modificări în codul vizualizat în browser fără a reîncărca pagina.

CSS / SASS / SCSS / suport MAI (cod de finalizare, subliniind eroare, de validare).

Codificare a mărfurilor Zen.

Navigare prin cod

Harmony Suport ECMAScript.

JavaScript refactorizare (Redenumire, Extras Variabila / Funcție, în linie variabilă / Funcție, Mutare / Copiere, Safe șterge, extrage script încorporat în fișier).

Sepanatorul JavaScript și unitate de testare.

IntelliJ IDEA suport PHP

Ultimate Edition de JetBrains poliglote IDE IntelliJ IDEA acceptă aceeași funcționalitate ca și PhpStorm de plugin-uri.

3.10 HeidiSQL

HeidiSQL, cunoscut anterior ca MySQL-Front, este un client gratuit și open source, cu interfață pentru MySQL (și pentru furcile sale, cum ar fi MariaDB și Percona Server), precum și Microsoft SQL Server și PostgreSQL. MySQL-Front a fost implementat de către dezvoltatorul german Nils Hoyer, care a oprit denumirea mysql-front după o discuție cu privire la încălcarea MysqLab pentru utilizarea numelui MySQL.

Mysql-Front a revenit din nou, sub numele HeidiSQL, dezvoltat de un programator german Ansgar Becker și alți câțiva contribuitori Delphi. Pentru a administra baza de date cu HeidiSQL, utilizatorii trebuie să fie conectați la un server (MySQL) local sau la distanță cu acreditări acceptabile, creând o sesiune. În cadrul acestei sesiuni utilizatorii pot administra baze de date MySQL în cadrul MySQL server conectat,si se pot deconecta de la server când se termina sesiunea. Setul de caracteristici este suficient pentru operațiile de baze de date, cele mai comune și avansate, de masă și de înregistrare a datelor, dar rămâne în dezvoltare activă pentru a se îndrepta spre funcționalitatea completă într-o bază de date SQL front-end.

Ansgar Becker a început dezvoltarea pe front-end în 1999, numind proiectul "MySQL-Front" si a folosit un strat API direct in scris de Matthias Fichtner pentru a interfața cu servere MySQL și baze de date conținute.
Dezvoltarea privata a continuat până la versiunea 2.5, pana in aprilie 2006, Ansgar, la cererea de pe SourceForge, a redenumit proiectulȘ "HeidiSQL". HeidiSQL a fost re-proiectat de catre ZeosLib, care a debutat cu o versiune majoră, versiunea 3.0 în aprilie 2006.
Suport pentru Microsoft SQL Server a incept din martie 2011 pentru versiunea 7.0.
De la versiunea 8.0, HeidiSQL oferă GUI în aproximativ 22 de alte limbi decât engleza. Traducerile au fost facute de către utilizatori din diferite țări, prin intermediul Transifex.
Suportul pentru PostgreSQL a fost introdus in martie 2014 dupa lansarea versiunii 9.0.

HeidiSQL are următoarele caracteristici GUI și capabilități:

conexiune server

sesiuni multiple salvate cu conexiune și acreditări stocate în protocol client / server comprimat pentru servere compatibile

 Interfață cu servere prin TCP / IP, tevi numite (prize) sau un protocol de tip tunel (SSH)

Sesiuni multiple care rulează într-o singură fereastră

Gestioneaza utilizatorii de pe server: adăugă, elimină și editeaza utilizatori și acreditările

Gestioneaza privilegiile de utilizator la nivel global și pe bază de date

Baze de date de export în fișiere SQL sau la alte servere

File de interogare multiple, fiecare dintre ele având mai multe subfile pentru rezultate lot

Se poate vizualiza și se filtra dupa toate variabilele de server, cum ar fi system_time_zone

Se pot edita toate variabilele de server, fie pentru o sesiune sau un domeniu de aplicare la nivel mondial

Variabile statistice

Vizualizare de tip server și valori medii pe oră și secundă    

Rulează procese de analiză SQL și ucide procese rele   

Se pot vizualiza toate bazele de date de pe server, se poate conecta la o singură bază de date pentru a lucra cu tabele și datele sale

Dimensiunea totală poate fi masurată în KB / MB / GB în cadrul structurii arborelui bazei de date

Tabele, vizualizări, proceduri, declanșatoare și evenimente

Se pot vizualiza toate obiectele din cadrul bazei de date selectate, se pot redenumi sau șterge obiecte

Coloane de editare tabel, index și chei externe

Coloane virtuale pe servere MariaDB sunt acceptate.

Modul editare interogare și setări

Editare structură SQL procedură și parametrii

Editare structură SQL de declanșare

Editare setări de timp structură SQL eveniment programat

IMPLEMENTAREA APLICATIEI

4.1 ARHITECURA APLICATIEI

Arhitectura software este definită de IEEE ca fiind: organizarea fundamentală a unui sistem, înglobată în componentele sistemului, în relațiile dintre acestea și în relațiile dintre componentele sistemului și mediul înconjurător, precum și principiile care guvernează proiectarea și evoluția sistemului. [ANSI/IEEE Std. 1471-2000, Recommended Practice for Architectural Description of Software-Intensive Systems].

Definiția propusă de IEEE spune faptul că arhitectura surprinde structura sistemului în ceea ce privește componentele acestuia precum și modul în care aceste componente interacționează. De asemenea arhitectura unui sistem definește și regulile după care sistemul este proiectat precum și cele care definesc modul în care el poate fi modificat.

Proiectarea arhitecturii software a aplicatiei Payment Management a fost realizată divizând programul în componente, module, obiecte sau orice alte unități de poziționare software. Împartirea aplicației în componente a fost realizată în funcție de cerințele și constrângerile pe care aplicația finală trebuia sa le îndeplinească.

Partitionarea aplicației a fost realizată în funcție de taskurile pe care o componentă trebuie să le îndeplinească în cadrul aplicației. Fiecare componentă joacă un anumit rol, dar și interacționează cu celelalte componente în vederea îndeplinirii funcționalității cerute.

Inițial au fost definite task-urile proiectului:

1.Identificarea scopurilor si obiectivelor:

Cercetarea bibliografică – presupune informarea generală asupra temei de cercetare. Principalele activitați implică documentarea asupra principalelor unelte software,precum și cautarea unor programe asemanatoare de manageriere a plaților.

Cerintele proiectului – se vor intelege obiectivele și scopurile proiectului. Cerințele sunt structurate și organizate într-un set de informatii complet, cuprinzator si coerent.

Planul Proiectului – reflecta modul în care se preconizeaza realizarea efectivă a acestuia, de la faza de concepere pâna la terminare.

2.Proiectarea aplicatiei:

Se va realiza o aplicație software cu ajutorul limbajelor HTML, CSS, PHP cu conexiune la baza de date.

Aplicația va fi divizata in urmatoarele task-uri:

Se va construi un GUI (graphical user interface) în framework-ul Symfony

Se vor stabili tipurile de utilizatori

Sa va stabili numarul de limbi in care va putea fi utilizată aplicația

Se va stabili ordinea barelor de meniu

Se va stabili numarul de filtre pentru fiecare pagina

Se vor determina numarul de butoane ce trebuie implementate pentru fiecare pagina

Se va crea fereastra principală, cat și cele secundare

Se va crea o functionalitate care ne va permite sa incarcam fișiere .xls

Se va crea o functionalitate care ne va permite sa descarcam fișiere .PDF

3.Implementare

Se va scrie cod HTML, CSS, PHP pentru realizarea aplicatiei.

4.Testare

In aceasta faza se vor testa toate functionalitatile aplicatiei software

4.2 DESCRIEREA DETALIATĂ A APLICAȚIEI

Payment management este un site distinct unde se pot monitoriza toate comenzile ce au loc intr-o anumiăa perioadă pe orice site e-Commerce. Aceasta iși propune analizarea și organizarea comenzilor și încasărilor efectuate de clienti prin intermediul curierilor.

Cum este posibil acest lucru?

Pentru inceput, vom determina către cine este destinată o astfel de aplicație web.

Pentru a cuprinde o gamă cat mai largă de utilizatori, vom putea utiliza aplicația atât în limba romană cât și intr-o limbă de circulație internaținală: englează.

Payment Management este o aplicatie web ce poate fi utila oricarui posesor de site e-Commerce. De aici deducem ca un posesor de site e-Commerce va trebui sa dețina drepturi absolute asupra aplicației. Astfel că, vom da drepturi de administrare pentru unul sau mai multi utilizatori. Minim un utilizator de tip administrator va fi inregistrat în aplicație.

Aplicația Payment Management va conține două tipuri de utilizatori: utilizatori de tip administrator și utimizatori de tip operator. Utilizatorii de tip administrator au drepturi asupra altor tipuri de utilizatori. Mai exact, utilizatorii de tip administrator pot creea unul sau mai mulți utilizatori de tip operator. De asemenea, utilizatorii de tip administrator pot activa sau inactiva utilizatorii de tip operator.

În momentul în care, un administrator creeaza un operator, acesta devine activ în aplicația Payment Management. Pentru ca acest lucru să fie posibil, administratorul trebuie să fie logat în aplicație și trebuie sa fie poziționat in secțiunea „lista utilizatori”. Aici va apasa butonul „Adaugă utilizator” și va completa urmatoarele campuri:

Nume utilizator

Email

Parolă

Verificare parolă

În momentul în care va salva formularul, un nou utilizator va fi creat. În lista de utilizatori va aparea noul utilizator, ce va fi in status activ și de tip operator.

Administratori au dreptul de a crea administratori. Cum vor putea face acest lucru?

Orice operator poate deveni admininistrator, daca un administrator îi va da acest drept apasând pe „Da” în dreptul campului „Administrator”.De asemenea,îi poate fi luat acest drept apasând pe „Nu” în dreptul campului „Administrator”.

Administratori au dreptul de a activa sau inactiva atât operatori cât și administratori.

În pagina „Listă utilizatori” va aparea o lista cu absolut toți administratorii și operatorii din aplicația Payment Management. Dacă un administrator este logat in aplicație, acesta poate activa alt utilizator apăsând pe „Da” în dreptul campului „Activ”. Din acest moment, utilizatorul activat are dreptul de a se autentifica în aplicație. Dacă un administrator este logat în aplicație, acesta poate inactiva alt utilizator apăsând pe „Nu” în dreptul campului „Activ”. Din acest moment, utilizatorul dezactivat nu are dreptul de a se autentifica în aplicație. În momentul în care utilizatorul dezactivat va incerca să se autentifice îi va apărea o eroare și nu va putea patrunde în aplicație pană ce contul nu va deveni activ.

Operatorii nu au dreptul de a activa sau inactiva alți operatori sau administratori. Insă, vor avea alte drepturi ce implică organizarea și structurarea aplicației, cum ar fi:

Încărcarea de fișiere .xls ce conțin comenzile plasate de către clienți din site-ul e-Commerce

Încărcarea de fișiere .xls ce conțin încasările de la curieri

Generarea de documente .pdf ce conțin:

Comenzi încasate si finalizate – status Verde

Comenzi cu valoarea încasată mai mare – status Protocaliu

Comenzi cu valoarea încasată mai mare – status Roșu

Operatorii vor genera PDF-uri ce conțin comenzi în status Portocaliu sau Rosu doar daca nu s-au putut efectua restituiri sau ordine de platî catre clienti.

Aceste comenzi urmând sa fie tratate manual la momentul efectuări tranzacțiilor.

Pentru ca aplicația să ofere atât transaparență cât și suport la adresa utilizatorilor am creat o secțiune cu numele „Panou de control”. În aceasta secțiune vor fi evidențiate trei liste:

Ultimele cinci fișiere de încasări încarcate

Ultimii cinci useri autentificați

Documentație tehnică

Aplicația Payment Management oferă control asupra comenzilor ce sunt plasate într-un site e-Commerce, cât și asupra încasărilor ce sunt preluate de curieri. Astfel că, am creeat o secțiune cu numele „Lista fișiere”. Aici vor fi listate ultimele desfasuratoare încarcate împreună cu utilizatorii ce le-au incărcat.

Mai exact, în momentul în care se vor încarca desfășurătoare ce conțin comenzi din site-ul e-Commerce sau desfășurătoare ce conțin încasarile preluate de curieri, vom evidenția în pagina ‚‚lista fișiere’’ fișierele încărcate de fiecare utilizator în parte.

Pentru a face cautarea mai ușoară am decis sa creez o bara de filtre ce conține:

Un drop-down „Origine” – ce se refera la originea fișierului încarcat, adică, din site-ul e-Commerce sau de la curieri

Un câmp „Nume” – unde putem filtra dupa numele fișierului. Câmpul acceptă caractere alfa-numerice.

Un câmp cu completare automată „Utilizator” – aici putem filtra dupa numele utilizatorilor din secțiunea „Listă utilizatori”. Orice alt nume de utilizator care nu face parte din lista utilizatorilor nu va întoarce niciun rezultat. Astfel că, daca vom filtra după numele unui utilizator existent în lista de utilizatori, nu va fi nevoie să scriem întregul nume al utilizatorului, de la primul caracter tastat se vor întoarce rezulatatele ce încep cu acel caracter.

Aplicația Payment Management se axează pe ordonarea și structurarea comenzilor ce provin din site-ul e-Commerce. Astfel că, pentru buna organizare și structurare a comenzilor am creat sectiunea „Lista comenzi”

Aceasta este structurată în trei părți:

Bara de filtre (drop-down-uri de filtrare dupa data, filtre dupa numele produselor sau numarul comenzii)

Buton de încarcare de fișiere (.xls/.xlsx ce conțin comenzile de au fost plasate în site-ul e-Commerce)

Lista de comenzi (de pe desfășurătorele ce au fost încarcate de operatori)

Pentru a face căutarea mai ușoară am decis să creez o bara de filtre ce conține:

Doua drop-down-uri de filtrare dupa data „Data comanda” și „Data finalizare” – la apăsarea acestor doua câmpuri, pentru a fi mai ușoară navigarea, vor apărea două drop-down-uri de unde se poate selecta foarte usor data. Drop-down-urile sunt în formă de calendar.

Un câmp „Nume produs” – aici putem filtra dupa numele produsului. Câmpul acceptă caractere alfa-numerice.

Un câmp „Numar comandă” – aici putem filtra dupa numărul comenzii. Câmpul acceptă doar caractere de tip intreg.

Pentru a încărca fișierul .xls ce conține comenzile plasate în site-ul e-Commerce vom acționa butonul „Încarca fisier site”. La apăsarea lui se va deschide o fereastra ce ne va permite sa navigam în memoria hard disk-ului. Dupa încarcarea fișierului .xls ce conține comenzile plasate în site-ul e-Commerce, acestea vor fi afișate în lista de comenzi.

Cum se construiește un fișier .xls ce conține comenzi?

Un fișier .xls ce conține comenzile plasate în site-ul e-Commerce este construit din șase coloane:

Numar comandă

Dată comandă

Dată finalizare comandă

Nume produs

Cantitate

Valoare comandă

Odată ce au fost specificate aceste șase elemente pentru o comandă ele pot fi așezate în fișierul .xls în dreptul coloanei specifice și mai apoi încarcate în aplicația Payment Management. La încarcarea unui .xls, baza de date se va încarca cu comenzile din fișier.

Aplicația Payment Management se axează pe ordonarea și structurarea încasărilor ce provin de la curieri. Astfel că, pentru buna organizare și structurare a încasarilor am creat sectiunea „Listă încasări”

Aceasta este structurată în trei părți:

Bara de filtre (drop-down-uri de filtrare dupa dată, filtre dupa numele produselor sau numărul comenzii)

Buton de incarcare de fisiere (.xls/.xlsx ce conțin încasările care provin de la curieri)

Lista de comenzi (de pe desfasuratorele ce au fost incarcate de operatori)

Pentru a face cautarea mai ușoară am decis sa creez o bara de filtre ce conține:

Doua drop-down-uri de filtrare dupa data „Data comanda” și „Data finalizare” – la apăsarea acestor doua campuri, pentru a fi mai ușoară navigarea, vor apărea două drop-dow-uri de unde se poate selecta foarte usor data. Drop-down-urile sunt în forma de calendar.

Un câmp „Nume produs” – aici putem filtra dupa numele produsului. Câmpul acceptă caractere alfa-numerice.

Un câmp „Numar comandă” – aici putem filtra dupa numarul comenzii. Câmpul acceptă doar caractere de tip intreg.

Pentru a încărca fișierul .xls ce conține încasările ce provin de la curieri vom acționa butonul „Încarca fisier plați”. La apăsarea lui se va deschide o fereastra ce ne va permite să navigăm în memoria hard disk-ului. Dupa încarcarea fișierului .xls ce conține încasările ce provin de la curieri, acestea vor fi afisate in lista de încasări.

Cum se construieste un fișier .xls ce conține încasări?

Un fișier .xls ce conține încasări ce provin de la curieri este construit din două coloane:

Numar comandă

Valoare comandă

Odată ce au fost specificate aceste două elemente pentru o comandă ele pot fi așezate în fișierul .xls în dreptul coloanei specifice si mai apoi încarcate în aplicația Payment Management. La încarcarea unui .xls, baza de date se va încarca cu încasarile din fișier.

Principala secțiune a aplicației Payment Management se numește „Mapare încasări” deoarece aici se evidentiază toate comenzile ce au fost plasate în site-ul e-Commerce de către clienți, cât și încasările preluate de catre curieri de la clienți.

Aceasta sectiune este structurată astfel:

Bara de filtre (drop-down-uri de filtrare dupa dată, filtre dupa numele produselor sau numărul comenzii, status)

Buton legendă

Lista comenzilor și a încasarilor

Buton de adaugare plată manual (se vor putea efectua ăncasari sau restituiri de către operatori)

Buton pentru generarea a unui desfășurptor în format .pdf

Buton observații

Pentru a face cautarea mai ușoară am decis sa creez o bară de filtre ce conține:

Doua drop-down-uri de filtrare dupa dată „Data comanda” și „Data finalizare” – la apăsarea acestor doua campuri, pentru a fi mai ușoară navigarea, vor apărea două drop-dow-uri de unde se poate selecta foarte ușor dată. Drop-down-urile sunt în formă de calendar.

Un câmp „Nume produs” – aici putem filtra dupa numele produsului. Câmpul acceptă caractere alfa-numerice.

Un câmp „Numar comandă” – aici putem filtra dupa numarul comenzii. Câmpul acceptă doar caractere de tip întreg.

Un drop-down „Status” – ce ne permite sa alegem doar unul dintre cele trei statusuri:

Verde

Portocaliu

Roșu

Butonul de legenda a fost adaugat pentru a fi accesibil utilizatorilor ce au acces la aplicația Payment Management. Acesta explică fiecare status al comenzii in parte.

În momentul în care o comanda si o încasare au ca element comun numarul comenzii aceasta va aparea in lista comenzilor si incasarilor in cadrul sectiunii „Mapare plati”.

În aceasta situatie vor aparea trei cazuri:

a) Valoarea produsului = Valoarea încasată – statusul plății va fi Verde

b) Valoarea produsului < Valoarea încasată – statusul plății va fi Portocaliu

c) Valoarea produsului > Valoarea încasată – statusul plății va fi Roșu

În cazul de la puncul a) valoarea produsului este egală cu încasarea,este fluxul normal al unei comenzi finalizate cu succes. Totul a decurs conform așteptarilor ceea ce înseamna ca toate comenzile au status Verde. În acest moment, operatorul nu trebuie sa trateze nicio comanda manual. Operatorul va apăsa butonul „Genereaza .pdf” și îi va da un nume sugestiv, apoi apoi toate comenzile vor fi centralizate sub forma unui fișier PDF și va putea fi descărcat din secțiunea „Desfășurătoare”

În cazul de la punctul b) valoarea produsului este mai mică decat încasarea,ceea ce înseamna că pentru un produs s-au încasat mai mulți bani decât valoarea lui.În acest moment, în Payment Management un operator va trata aceasta comandă manual.

Mai exact, va efectua o restituire catre client prin transfer bancar, dupa care, în interfata va apăsa butonul „Adauga plată”. În acest moment, se va deschide o fereastră ce va conține două câmpuri pe care operatorul trebuie să le completeze:

„Valoare plată” – suma care a fost restituită catre client

„Observații” – acțiunea pe care a intreprinso operatorul (ex: Suma X a fost restituită clientului Y prin transfer bancar)

Butonul „Observații” va ramane vizibil în interfața după efectuarea restituirii.

În cazul de la punctul c) valoarea produsului este mai mare decat încasarea,ceea ce înseamna ca pentru un produs s-au încasat mai puțini bani decat valoarea lui. În acest moment, în Payment Management un operator va trata aceasta comandă manual.

Mai exact, va trimite un ordin de plaăa catre client, după care, în interfata va apăsa butonul „Adauga plată”. În acest moment, se va deschide o fereastră ce va conține două câmpuri pe care operatorul trebuie să le completeze:

„Valoare plată” – suma care a fost primită de la client

„Observații” – acțiunea pe care a intreprinso operatorul (ex: Suma X a fost primita de la clientul Y)

Butonul „Observații” va ramane vizibil in interfată dupa efectuarea ordinului de plată.

In secțiunea „Desfasuratoare” sunt evidentiate desfășurătoarele. Ele pot conține:

Comenzi încasate si finalizate – status Verde

Comenzi cu valoarea încasată mai mare – status Protocaliu

Comenzi cu valoarea încasată mai mică – status Roșu

Acestea vor putea fi descarcate în format .pdf. Astfel se pot face rapoarte cu privire la erorile ce pot aparea intr-un astfel de sistem.

Se vor creea desfașuratoare pentru comenzile în status Portocaliu sau Roșu doar atunci cand nu pot fi trate de un operator la un moment dat.

4.3 DESCRIEREA CLASELOR APLICAȚIEI

Clasele principale ale aplicației sunt :

DefaultController – clasa în care este implementată pagina ‚‚Panoul de control’’

class DefaultController extends Controller
{
/**
* @Route("/", name="dashboard")
*/
public function indexAction(Request $request)
{
// replace this example code with whatever you need
// return $this->render('default/index.html.twig', [
// 'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..'),
// ]);

$em = $this->getDoctrine()->getManager();

$finder = new Finder();
/** @var \SplFileInfo[] $iterator */
$iterator = $finder->files()->in($this->get(PathService::SERVICE_NAME)->getDocumentationPath());

return $this->render('AppBundle:Default:index.html.twig', [
'users' => $em->getRepository(User::class)->findLastAuthenticated(),
'cashings' => $em->getRepository(CashingFile::class)->findBy([], ['created' => 'DESC'], 5),
'filesList' => $iterator,
]);
}
}

Figura 1: Clasa DefaultController

MatchingsController – clasa unde se genereaza PDF-urile

class MatchingsController extends Controller
{
/**
* @Route("/matchings", name="matchings")
*
* @param Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
public function indexAction(Request $request)
{
$form = $this->createForm(OrderPaymentMatchingFilterType::class);

return $this->render('AppBundle:Matchings:index.html.twig', [
'form' => $form->createView(),
]);
}

/**
* @Route("/matchings/ajax/list", name="matchings.ajax.list")
*
* @param Request $request
* @return Response
*/
public function listMatchingsAction(Request $request)
{
$params = $request->query->all();
$this->get('session')->set('matchings_filters', $params);
list($results, $totalResults) = $this->getOrders($params);

return JsonResponse::create([
'results' => $results,
'totalResults' => $totalResults,
'page' => array_key_exists('page', $params) ? $params['page'] : 1,
'orderBy' => $params['orderBy'],
'orderDir' => $params['orderDir'],
]);
}

/**
* @Route("/matchings/generate-pdf", name="matchings.generate_pdf")
*
* @param Request $request
* @return Response
*/
public function generatePdfAction(Request $request)
{
$ordersPdf = new OrdersPdfFile();
$form = $this->createFormBuilder($ordersPdf)
->setAction($this->generateUrl('matchings.generate_pdf'))
->setMethod('POST')
->add('name', TextType::class, [
'label' => 'matchings.labels.pdf_name',
'required' => true,
])
->getForm();

if ($request->isMethod('POST')) {
$form->handleRequest($request);
if ($form->isValid()) {
$pdfPath = $this->get('app.path')->getOrderPdfPath() . $ordersPdf->getName() . '.pdf';

$filters = $this->get('session')->get('matchings_filters', []);
unset($filters['page']);
list($orders,) = $this->getOrders($filters);

$html = $this->get('templating')->render(
'AppBundle:Matchings:pdf_template.html.twig',
['orders' => $orders]
);

$pdf = new \mPDF('', 'A4');
$pdf->writeHTML($html);
$pdf->Output($pdfPath, 'F');

$ordersPdf->setUser($this->container->get('security.token_storage')->getToken()->getUser());
$ordersPdf->setOriginalName($ordersPdf->getName());

$em = $this->get('doctrine')->getManager();
$em->persist($ordersPdf);
$em->flush();

// $response = new BinaryFileResponse($pdfPath);
// $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT);
//
// return $response;

return JsonResponse::create(['success' => true]);
}
}

if ($request->isXmlHttpRequest()) {
return JsonResponse::create([
'success' => false,
'html' => $this->get('twig')->render(
'AppBundle:Matchings:generate_pdf.html.twig',
['form' => $form->createView()]
),
]);
}

return $this->render('AppBundle:Matchings:generate_pdf.html.twig', [
'form' => $form->createView(),
]);
}

/**
* @param array $params
* @return array
*/
private function getOrders(array $params)
{
$results = [];
$orders = $this->getDoctrine()->getRepository(Order::class)->filterMatchings($params);

foreach ($orders as $o) {
list($order, $paymentsValue) = array_values($o);
$paymentsValue = $paymentsValue ?: 0;
$difference = $paymentsValue – $order->getValue();
$results[] = array_merge($order->toArray(), [
'paymentsValue' => $paymentsValue,
'difference' => $difference,
'status' => $difference <=> 0,
]);
}

return [$results, $orders->count()];
}
}

Figura 2: Clasa MatchingsController

OrdersController – clasa unde se încarca și se validează comenzile

class OrdersController extends Controller
{
/**
* @Route("/orders", name="orders")
*
* @param Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
public function indexAction(Request $request)
{
$form = $this->createForm(OrderFilterType::class);

return $this->render('AppBundle:Orders:index.html.twig', [
'form' => $form->createView(),
]);
}
/**
* @Route("/orders/upload", name="orders.upload");
*
* @param Request $request
* @return Response
*/
public function uploadAction(Request $request)
{
$form = $this->createFormBuilder()
->setAction($this->generateUrl('orders.upload'))
->setMethod('POST')
->add('file', FileType::class, [
'label' => 'orders.labels.filters.file',
'required' => true,
'constraints' => [
new \Symfony\Component\Validator\Constraints\File([
'mimeTypes' => ['text/csv', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],
]),
]
])
->getForm();
// form was submitted
if ($request->isMethod('POST')) {
$form->handleRequest($request);
$translator = $this->get('translator');
if ($form->isValid()) {
try {
/** @var UploadedFile $file */
$file = $form['file']->getData();
$this->get(OrderParserService::SERVICE_NAME)->processFile(
$file->getRealPath(),
function() use ($file) {
return $this->get(FileHandlerService::SERVICE_NAME)->moveOrdersFile($file);
}
);
$this->get('session')->getFlashBag()->set(
'success',
$translator->trans('common.file_upload_successful')
);
} catch (\Exception $e) {
$this->get('session')->getFlashBag()->set(
'error',
$e->getMessage()
);
}
} else {
$this->get('session')->getFlashBag()->set(
'error',
'orders.errors.invalid_file_type'
);
}

return $this->redirect($this->generateUrl('orders'));
}
return $this->render('AppBundle:Orders:upload.html.twig', [
'form' => $form->createView(),
]);
}
/**
* @Route("/orders/ajax/list", name="orders.ajax.list")
*
* @param Request $request
* @return Response
*/
public function listOrdersAction(Request $request)
{
$params = $request->query->all();
$orders = $this->getDoctrine()->getRepository(Order::class)->filter($params);
$results = [];
/** @var Order $order */
foreach ($orders as $order) {
$results[] = $order->toArray();
}

return JsonResponse::create([
'results' => $results,
'totalResults' => $orders->count(),
'page' => array_key_exists('page', $params) ? $params['page'] : 1,
'orderBy' => $params['orderBy'],
'orderDir' => $params['orderDir'],
]);
}
/**
* @Route("/orders/ajax/order-numbers", name="orders.ajax.order_numbers")
*
* @param Request $request
* @return Response
*/
public function orderNumbersListAction(Request $request)
{
$term = $request->query->get('term');
$results = $this->getDoctrine()->getRepository(Order::class)->findOrderNumbers($term);
return JsonResponse::create($results);
}
/**
* @Route("/orders/ajax/product-names", name="orders.ajax.product_names")
*
* @param Request $request
* @return Response
*/
public function productNameListAction(Request $request)
{
$term = $request->query->get('term');
$results = $this->getDoctrine()->getRepository(Order::class)->findProductNames($term);
return JsonResponse::create($results);
}

Figura 3: Clasa OrdersController

PaymentsController – clasa unde se încarcă și se validează încăsarile

class PaymentsController extends Controller
{
/**
* @Route("/payments", name="payments")
*
* @param Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
public function indexAction(Request $request)
{
$form = $this->createForm(PaymentFilterType::class);

return $this->render('AppBundle:Payments:index.html.twig', [
'form' => $form->createView(),
]);
}

/**
* @Route("/payments/upload", name="payments.upload");
*
* @param Request $request
* @return Response
*/
public function uploadAction(Request $request)
{
$form = $this->createFormBuilder()
->setAction($this->generateUrl('payments.upload'))
->setMethod('POST')
->add('paymentProcessor', EntityType::class, [
'class' => 'AppBundle:PaymentProcessor',
'choice_label' => 'name',
'label' => 'payments.labels.filters.payment_parser',
'required' => true,
'query_builder' => function (EntityRepository $repo) {
$qb = $repo->createQueryBuilder('pp');

return $qb->where('pp.id <> :manualProcessingId')
->setParameter('manualProcessingId', PaymentProcessor::PAYMENT_PROCESSING_MANUAL)
->orderBy('pp.id', 'ASC');
}
])
->add('file', FileType::class, [
'label' => 'payments.labels.filters.file',
'required' => true,
'constraints' => [
new \Symfony\Component\Validator\Constraints\File([
'mimeTypes' => ['text/csv', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],
]),
]
])
->getForm();

// form was submitted
if ($request->isMethod('POST')) {
$form->handleRequest($request);
$translator = $this->get('translator');
if ($form->isValid()) {
try {
/** @var PaymentProcessor $paymentProcessor */
$paymentProcessor = $form['paymentProcessor']->getData();
/** @var UploadedFile $file */
$file = $form['file']->getData();

$this->get(PaymentParserService::SERVICE_NAME)
->setPaymentProcessor($paymentProcessor)
->processFile($file->getRealPath(), function () use ($file) {
return $this->get(FileHandlerService::SERVICE_NAME)->moveCashingsFile($file);
});
$this->get('session')->getFlashBag()->set(
'success',
$translator->trans('common.file_upload_successful')
);
} catch (\Exception $e) {
$this->get('session')->getFlashBag()->set(
'error',
$e->getMessage()
);
}
} else {
$this->get('session')->getFlashBag()->set(
'error',
'payments.errors.invalid_file_type'
);
}
return $this->redirect($this->generateUrl('payments'));
}
return $this->render('AppBundle:Payments:upload.html.twig', [
'form' => $form->createView(),
]);
}

/**
* @Route("/payments/ajax/list", name="payments.ajax.list")
*
* @param Request $request
* @return Response
*/
public function listPaymentsAction(Request $request)
{
$params = $request->query->all();
$payments = $this->getDoctrine()->getRepository(Payment::class)->filter($params);
$results = [];
/** @var Payment $payment */
foreach ($payments as $payment) {
$results[] = [
'id' => $payment->getId(),
'value' => $payment->getValue(),
'order' => $payment->getOrder()->toArray()
];
}
return JsonResponse::create([
'results' => $results,
'totalResults' => $payments->count(),
'page' => array_key_exists('page', $params) ? $params['page'] : 1,
'orderBy' => $params['orderBy'],
'orderDir' => $params['orderDir'],
]);
}
/**
* @Route("/payments/add", name="payments.add");
*
* @param Request $request
* @return Response
*/
public function addAction(Request $request)
{
$orderId = $request->query->get('orderId', null);
$em = $this->getDoctrine()->getManager();
$order = $em->getRepository(Order::class)->find($orderId);
if (!$order) {
throw new NotFoundHttpException('Order does not exist.');
}
$form = $this->createFormBuilder()
->setAction($this->generateUrl('payments.add', ['orderId' => $orderId]))
->setMethod('POST')
->add('orderNumber', TextType::class, [
'label' => 'orders.labels.filters.orderNumber',
'data' => $orderId,
'disabled' => true,
])
->add('value', NumberType::class, [
'label' => 'payments.column_headers.value',
'scale' => 2,
])
->add('observations', TextareaType::class, [
'label' => 'common.observations',
])
->getForm();
if ($request->isMethod('POST')) {
$form->handleRequest($request);
$translator = $this->get('translator');
if ($form->isValid()) {
$value = $form['value']->getData();
$observations = $form['observations']->getData();
$paymentProcessor = $em->getRepository(PaymentProcessor::class)
->find(PaymentProcessor::PAYMENT_PROCESSING_MANUAL);
$payment = (new Payment())
->setOrder($order)
->setValue($value)
->setPaymentProcessor($paymentProcessor);
if (strlen(trim($observations))) {
$order->setObservations($observations);
}
$em->persist($payment);
$em->flush();
$this->get('session')->getFlashBag()->set(
'success',
$translator->trans('common.payment_added')
);
} else {
$this->get('session')->getFlashBag()->set(
'error',
$translator->trans('common.random_form_error')
);
}
return $this->redirect($this->generateUrl('matchings'));
}

return $this->render('AppBundle:Payments:add.html.twig', [
'form' => $form->createView(),
]);
}
}

Figura 4: Clasa PaymentsController

UsersController – clasa unde se adaugă un nou utilizator

class UsersController extends Controller
{
/**
* @Route("/users", name="users")
*/
public function indexAction(Request $request)
{
$users = $this->getDoctrine()->getRepository(User::class)->findAll();

return $this->render('AppBundle:Users:index.html.twig', [
'users' => $users,
]);
}

/**
* @Route("/users/add", name="users.add")
*
* @param Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
public function addAction(Request $request)
{
$user = new User();
$form = $this->createForm(
RegistrationFormType::class,
$user,
[
'validation_groups' => ['Registration'],
'action' => $this->generateUrl('users.add')
]
);

if ($request->isMethod('POST')) {
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();

$user->setEnabled(true);
$em->persist($user);
$em->flush();

$this->get('session')->getFlashBag()->add('success', $this->get('translator')->trans('users.labels.created_successfully'));

return JsonResponse::create(['success' => true]);
}
}

if ($request->isXmlHttpRequest()) {
return JsonResponse::create([
'success' => false,
'html' => $this->get('twig')->render(
'AppBundle:Users:add.html.twig',
['form' => $form->createView()]
),
]);
}

return $this->render('AppBundle:Users:add.html.twig', [
'form' => $form->createView(),
]);
}

/**
* @Route("/users/enable", name="users.enable")
*
* @param Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
public function enableToggleAction(Request $request)
{
$userId = $request->request->get('id');
$isEnabled = (bool)(int)$request->request->get('enabled');

$em = $this->getDoctrine()->getManager();
$user = $this->getDoctrine()->getRepository(User::class)->find($userId);

$user->setEnabled($isEnabled);
$em->flush();

return JsonResponse::create(['success' => true]);
}

/**
* @Route("/users/role", name="users.role")
*
* @param Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
public function makeAdminAction(Request $request)
{
$userId = $request->request->get('id');
$isAdmin = (bool)(int)$request->request->get('admin');

$em = $this->getDoctrine()->getManager();
$user = $this->getDoctrine()->getRepository(User::class)->find($userId);

$user->setSuperAdmin($isAdmin);
$em->flush();

return JsonResponse::create(['success' => true]);
}

/**
* @Route("/users/ajax/usernames", name="users.ajax.usernames")
*
* @param Request $request
* @return \Symfony\Component\HttpFoundation\Response|static
*/
public function listUsernamesAction(Request $request)
{
$term = $request->query->get('term');
$results = $this->getDoctrine()->getRepository(User::class)->findUsernames($term);

return JsonResponse::create($results);
}
}

Figura 5: Clasa UsersController

ExcelParserService – clasa unde se parsează un fișier .xls

Funcția de încarcare a fișierului .xls:

public function loadFile($file, array $columnsList, array $rowConstraints = [])
{
$this->headersCollection = new ExcelHeaderCollection();
$this->rowCollection = new ExcelRowCollection();

$this->envParams['oneReadCycle'] = $this->readOnlyOnce || $this->chunkSize === null ? true : false;
$this->envParams['useReadFilter'] = $this->startRow === 1 && $this->chunkSize === null ? false : true;

// we always read the headers first
$this->logger->info("Loading file {$file} for parsing.");
$this->load($file, $this->buildConfig(new ExcelReadFilter(1, 1)));

$this->logger->info('Reading headers.');
$this->parseHeaders($columnsList);
if ($this->startRow === 1) {
$this->startRow++;
}

$config = $this->buildConfig();
if ($this->envParams['oneReadCycle']) {
$this->readCycle($file, $config);
} else {
$readFilter = $config->getReadFilter();
while ($readFilter->getStartRow() <= $this->getTotalRows()) {
$this->logger->info("Read cycle: rows interval is [{$readFilter->getStartRow()}, {$readFilter->getEndRow()})");
$this->readCycle($file, $config);
$readFilter->setRows($readFilter->getEndRow(), $this->chunkSize);
}
}

$this->logger->info("Finished parsing file {$file}.");
$this->rowCollection->validate($rowConstraints);
if (!$this->rowCollection->isValid()) {
$errorsFormatter = new ErrorsFormatter($this->rowCollection->getErrors(), $columnsList, $this->translator);
throw new ExcelParserException($errorsFormatter->getAsString());
}

return $this;
}

Figura 6: Functia de incarcare a fisierului .xls

Funcția de parsare a rândurilor din fișierul .xls:

protected function parseDataRows()
{
if ($this->getRowsCount() === 0) { // todo: not needed ? returns false result ?
return $this;
}

$headers = $this->headersCollection->listIndexedKeys();
$headerIndexes = array_keys($headers);
while ($this->getCurrentRow()) {
$xlsRowData = $this->readRow($headerIndexes);
$rowData = [];

if (count(array_filter($xlsRowData))) { // if row is not empty
foreach ($xlsRowData as $columnIndex => $value) {
if (array_key_exists($columnIndex, $headers)) {
$rowData[$headers[$columnIndex]] = $value;
}
}

$this->rowCollection->add(
new ExcelRow($this->getCurrentRow()->getRowIndex(), $rowData, $this->validator)
);
}

$this->nextRow();
}

return $this;
}
}

Figura 7: Funcția de parsare a rândurilor din fișierul .xls

4.4 MANUAL DE UTILIZARE

Platforma Payment Management a fost creată pentru a putea fi utilizată la o scară cât mai largă de utilizatori. Astfel că aplicația va putea fi întrebuințată atât în limba romană (fig 8), cât și în limba engleză (fig 9):

Figura 8: Pagina de autentificare – ro

Figura 9: Pagina de autentificare – en

Pentru a ne familiariza cu aplicația web, vom atașa două capturi de ecran cu secțiunea „panoul de control” în limba romană (fig 10), cât și limba engleză (fig 11):

Figura 10: Pagina Panou de control – ro

Figura 11: Pagina Panou de control – en

Utilizatorii de tip administrator au drepturi asupra altor tipuri de utilizatori. Mai exact, utilizatorii de tip administrator pot creea unul sau mai mulți utilizatori de tip operator. De asemenea, utilizatorii de tip administrator pot activa sau inactiva utilizatorii de tip operator.

Mai jos este afișată pagina utilizatorilor în limba romană (fig 12), cât și limba engleză (fig 13):

Figura 12: Pagina Lista utilizatori – ro

Figura 13: Pagina Lista utilizatori – en

În momentul în care, un administrator creeaza un operator, acesta devine activ în aplicația Payment Management. Pentru ca acest lucru să fie posibil, administratorul trebuie să fie logat în aplicație și trebuie să fie poziționat în secțiunea „lista utilizatori”.

În această fereastra administratorul va apăsa butonul „Adauga utilizator” și va completa urmatoarele campuri:

Nume utilizator

Email

Parolă

Verificare parolă

Mai jos este afișată fereastra de adaugare utilizatori în limba romană (fig 14), cât și limba engleză (fig 15):

Figura 14: Fereastra Adaugare utilizatori – ro

Figura 15: Fereastra Adaugare utilizatori – en

Aplicația Payment Management oferă control asupra comenzilor ce sunt plasate într-un site e-Commerce, cât și asupra încasărilor ce sunt preluate de curieri. Astfel că, am creeat o secțiune cu numele „Lista incasari”. Aici vor fi listate ultimele desfășurătoare încarcate împreună cu utilizatorii ce le-au încărcat.

Mai exact, în momentul în care se vor încărca desfășurătoare ce conțin comenzi din site-ul e-Commerce sau desfășurătoare ce conțin încasarile preluate de curieri, vom evidenția în pagina ‚‚listă fișiere’’ documentele încărcate de fiecare utilizator în parte.

Pagina ‚‚lista fișiere’’ în limba romana (fig 16) și limba engleză (fig 17):

Figura 16: Pagina Lista fișiere – ro

Figura 17: Pagina Lista fișiere – en

Aplicația Payment Management se axează pe ordonarea și structurarea comenzilor ce provin din site-ul e-Commerce. Astfel că, pentru buna organizare și structurare a comenzilor am creat sectiunea „Lista comenzi”

Mai jos este afișată pagina „Lista comenzi” în limba romană (fig 18), cât și limba engleză (fig 19):

Figura 18: Pagina Lista comenzi – ro

Figura 19: Pagina Lista comenzi – en

Pentru a încărca fișierul .xls ce conține comenzile plasate în site-ul e-Commerce vom acționa butonul „Încarca fisier site”. La apăsarea lui se va deschide o fereastra ce ne va permite să navigam în memoria hard disk-ului. Dupa încarcarea fișierului .xls ce conține comenzile plasate în site-ul e-Commerce, acestea vor fi afișate în lista de comenzi.

Mai jos este afișată fereastra de încarcare a fisierului .xls în limba romană (fig 20), cât și limba engleză (fig 21):

Figura 20: Fereastra de încarcare a fisierului .xls pentru comenzi – ro

Figura 21: Fereastra de încarcare a fisierului .xls pentru comenzi – en

Cum se construiește un fișier .xls ce conține comenzi?

Un fișier .xls ce conține comenzile plasate in site-ul e-Commerce este construit din șase coloane:

Numar comandă

Dată comandă

Dată finalizare comandă

Nume produs

Cantitate

Valoare comandă

Mai jos este afișat un exemplu de fișier .xls ce contine comenzi din site-ul e-Commerce:

Figura 22: Fișier .xls ce contine comenzi

Odată ce au fost specificate aceste șase elemente pentru o comandă ele pot fi așezate în fișierul .xls in dreptul coloanei specifice și mai apoi încarcate în aplicația Payment Management. La încarcarea unui .xls, baza de date se va încarca cu comenzile din fișier.

Aplicația Payment Management se axează pe ordonarea și structurarea încasărilor ce provin de la curieri. Astfel că, pentru buna organizare si structurare a încasarilor am creat sectiunea „Listă încasări”

Mai jos este afișatîă pagina „Listă încasări” în limba romană (fig 23), cât și limba engleză (fig 24):

Figura 23: Pagina Lista încasări– ro

Figura 24: Pagina Lista încasări – en

Pentru a încărca fișierul .xls ce conține încasările ce provin de la curieri vom acționa butonul „Încarca fisier plați”. La apăsarea lui se va deschide o fereastra ce ne va permite să navigăm în memoria hard disk-ului. Dupa încarcarea fișierului .xls ce conține încasările ce provin de la curieri, acestea vor fi afișate în lista de încasări.

Mai jos este afișata fereastra de încarcare a fișierului .xls în limba romană (fig 25), cât și limba engleză (fig 26):

Figura 25: Fereastra de încarcare a fișierului .xls pentru încasări – ro

Figura 26: Fereastra de încarcare a fișierului .xls pentru încasări – en

Cum se construiește un fișier .xls ce conține încasări?

Un fișier .xls ce conține încasări ce provin de la curieri este construit din două coloane:

Numar comandă

Valoare comandă

Mai jos este afișat un exemplu de fișier .xls ce conține încasări de la curieri:

Figura 27: Fișier .xls ce contine încasări

Odată ce au fost specificate aceste două elemente pentru o comandă ele pot fi așezate în fișierul .xls in dreptul coloanei specifice și mai apoi încarcate în aplicația Payment Management. La încarcarea unui .xls, baza de date se va încărca cu încasarile din fișier.

Principala secțiune a aplicației Payment Management se numește „Mapare încasări” deoarece aici se evidentiază toate comenzile ce au fost plasate in site-ul e-Commerce de catre clienți, cât și încasările preluate de catre curieri de la clienți.

Mai jos este afișată pagina „Mapare încasari” în limba romană (fig 28), cât și limba engleză (fig 29).:

Figura 28: Pagina Mapare încasări – ro

Figura 29: Pagina Mapare încasări – en

Butonul de legendă a fost adăugat pentru a fi accesibil utilizatorilor ce au acces la aplicația Payment Management. Acesta explică fiecare status al comenzii în parte.

Mai jos este afisată o imagine cu legenda statusurilor în limba romană (fig 30), cât și limba engleză (fig 31).:

Figura 30: Fereastra Legendă – ro

Figura 31: Fereastra Legendă – en

În momentul în care o comandă și încasare au ca element comun numărul comenzii aceasta va aparea în lista comenzilor și încasarilor în cadrul secțiunii „Mapare plăți”.

În această situație vor aparea trei cazuri:

a) Valoarea produsului = Valoarea încasată – statusul plății va fi Verde

b) Valoarea produsului < Valoarea încasată – statusul plății va fi Portocaliu

c) Valoarea produsului > Valoarea încasată – statusul plății va fi Roșu

În cazul de la puncul a) valoarea produsului este egală cu încasarea,este fluxul normal al unei comenzi finalizate cu succes. Totul a decurs conform așteptarilor ceea ce înseamna ca toate comenzile au status Verde. În acest moment, operatorul nu trebuie sa trateze nicio comanda manual. Operatorul va apăsa butonul „Genereaza .pdf” și îi va da un nume sugestiv, apoi apoi toate comenzile vor fi centralizate sub forma unui fișier PDF și va putea fi descărcat din secțiunea „Desfășurătoare”

În cazul de la punctul b) valoarea produsului este mai mică decat încasarea,ceea ce înseamna că pentru un produs s-au încasat mai mulți bani decât valoarea lui.În acest moment, în Payment Management un operator va trata aceasta comandă manual.

Mai exact, va efectua o restituire catre client prin transfer bancar, dupa care, în interfata va apăsa butonul „Adauga plată”. În acest moment, se va deschide o fereastră ce va conține două câmpuri pe care operatorul trebuie să le completeze:

„Valoare plată” – suma care a fost restituită catre client

„Observații” – acțiunea pe care a intreprinso operatorul (ex: Suma X a fost restituită clientului Y prin transfer bancar)

Butonul „Observații” va ramane vizibil în interfața după efectuarea restituirii.

În cazul de la punctul c) valoarea produsului este mai mare decat încasarea,ceea ce înseamna ca pentru un produs s-au încasat mai puțini bani decat valoarea lui. În acest moment, în Payment Management un operator va trata aceasta comandă manual.

Mai exact, va trimite un ordin de plaăa catre client, după care, în interfata va apăsa butonul „Adauga plată”. În acest moment, se va deschide o fereastră ce va conține două câmpuri pe care operatorul trebuie să le completeze:

„Valoare plată” – suma care a fost primită de la client

„Observații” – acțiunea pe care a intreprinso operatorul (ex: Suma X a fost primita de la clientul Y)

Butonul „Observații” va ramane vizibil in interfată dupa efectuarea ordinului de plată.

Mai jos este afișată fereastra de adaugare plată în limba romana (fig 32), cât și limba engleză (fig 33):

Figura 32: Fereastră Adaugare plată – ro

Figura 33: Fereastră Adaugare plată – en

Mai jos este afișată fereastra de observații în limba romană (fig 34), cât și limba engleză (fig 35):

Figura 34: Fereasta Observații – ro

Figura 35: Fereasta Observații – en

În secțiunea „Desfășurătoare” sunt evidențiate desfășurătoarele. Mai jos este afișată pagina „Desfasuratoare” în limba romană (fig 36), cât și limba engleză (fig 37):

Figura 36: Pagina Desfășurătoare – ro

Figura 37: Pagina Desfășurătoare – en

Desfășurătoarele vor putea fi descărcate în format .pdf. Astfel se pot face rapoarte cu privire la erorile ce pot apărea într-un astfel de sistem.

Mai jos este afișat un exemplu de desfășurător în format .pdf:

Figura 38: Desfășurător în format .pdf

Concluzii

Alegerea acestui proiect ma ajutat să-mi dezvolt cunoștințele în domeniul programării în limbajele HTML, CSS, PHP, JavaScript, Jquery, Symnfony, Doctrine, MySQL și tehnologiile open source.

Aplicația este utilă persoanelor ce dețin site-uri e-Commerce.

Aplicația ar putea fi îmbunătățită adăugând o funcționalitate ce ajută la comunicarea mai eficienta cu site-ul e-Commerce cât și cu platformele curierilor.

Bibliografie

[DUC11] – Jon Duckett , HTML & CSS: Design and build web sites, Wiley, 2011

[KOS99] – Jirka Kosek, PHP – Creating Interactive Internet Applications, O’Reilly Media, 1999

[DUC14] – Jon Duckett , Javascript and Jquery: Interactive front-end web development, Wiley, 2014

[WIL06] – Seyed M.M. Tahaghoghi, Hugh E. Williams, Learning MySQL, O’Reilly Media, 2006

Referințe web

MANAGEMENT:

https://en.wikipedia.org/wiki/Management

E-COMMERCE:

https://en.wikipedia.org/wiki/E-commerce

HTML:

https://en.wikipedia.org/wiki/HTML

CSS:

https://en.wikipedia.org/wiki/Cascading_Style_Sheets

PHP:

http://php.net/

https://en.wikipedia.org/wiki/PHP

SYMFONY:

https://symfony.com/

https://en.wikipedia.org/wiki/Symfony

DOCTRINE:

https://en.wikipedia.org/wiki/Doctrine_(PHP)

JAVASCRIPT:

https://en.wikipedia.org/wiki/JavaScript

JQUERY:

https://en.wikipedia.org/wiki/JQuery

MYSQL:

https://en.wikipedia.org/wiki/MySQL

PHPSTORM:

https://en.wikipedia.org/wiki/PhpStorm

HEIDISQL:

https://en.wikipedia.org/wiki/HeidiSQL

Anexe

Codul sursă

Configs:

config.yml:

imports:

– { resource: parameters.yml }

– { resource: security.yml }

– { resource: services.yml }

# Put parameters here that don't need to change on each machine where the app is deployed

# http://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration

parameters:

locale: en

documents_path: "%kernel.root_dir%/../web/documents"

framework:

#esi: ~

translator: { fallbacks: ["%locale%"] }

secret: "%secret%"

router:

resource: "%kernel.root_dir%/config/routing.yml"

strict_requirements: ~

form: ~

csrf_protection: ~

validation: { enable_annotations: true }

#serializer: { enable_annotations: true }

templating:

engines: ['twig']

default_locale: "%locale%"

trusted_hosts: ~

trusted_proxies: ~

session:

# http://symfony.com/doc/current/reference/configuration/framework.html#handler-id

handler_id: session.handler.native_file

save_path: "%kernel.root_dir%/../var/sessions/%kernel.environment%"

fragments: ~

http_method_override: true

assets: ~

# Twig Configuration

twig:

debug: "%kernel.debug%"

strict_variables: "%kernel.debug%"

globals:

locale: "%locale%"

# Doctrine Configuration

doctrine:

dbal:

driver: pdo_mysql

host: "%database_host%"

port: "%database_port%"

dbname: "%database_name%"

user: "%database_user%"

password: "%database_password%"

charset: UTF8

# if using pdo_sqlite as your database driver:

# 1. add the path in parameters.yml

# e.g. database_path: "%kernel.root_dir%/data/data.db3"

# 2. Uncomment database_path in parameters.yml.dist

# 3. Uncomment next line:

# path: "%database_path%"

orm:

auto_generate_proxy_classes: "%kernel.debug%"

naming_strategy: doctrine.orm.naming_strategy.underscore

auto_mapping: true

# Swiftmailer Configuration

swiftmailer:

transport: "%mailer_transport%"

host: "%mailer_host%"

username: "%mailer_user%"

password: "%mailer_password%"

spool: { type: memory }

fos_user:

db_driver: orm # other valid values are 'mongodb', 'couchdb' and 'propel'

firewall_name: main

user_class: AppBundle\Entity\User

parameters.yml

parameters:

database_host: localhost

database_port: null

database_name: disertatie

database_user: root

database_password: null

mailer_transport: smtp

mailer_host: 127.0.0.1

mailer_user: null

mailer_password: null

secret: ThisTokenIsNotSoSecretChangeItU

security.yml

security:

encoders:

FOS\UserBundle\Model\UserInterface: bcrypt

role_hierarchy:

ROLE_ADMIN: ROLE_USER

ROLE_SUPER_ADMIN: ROLE_ADMIN

# http://symfony.com/doc/current/book/security.html#where-do-users-come-from-user-providers

providers:

# in_memory:

# memory: ~

fos_userbundle:

id: fos_user.user_provider.username_email

firewalls:

# disables authentication for assets and the profiler, adapt it according to your needs

dev:

pattern: ^/(_(profiler|wdt)|css|images|js)/

security: false

main:

# anonymous: ~

# activate different ways to authenticate

# http_basic: ~

# http://symfony.com/doc/current/book/security.html#a-configuring-how-your-users-will-authenticate

# form_login: ~

# http://symfony.com/doc/current/cookbook/security/form_login_setup.html

pattern: ^/

form_login:

provider: fos_userbundle

csrf_token_generator: security.csrf.token_manager

# if you are using Symfony < 2.8, use the following config instead:

# csrf_provider: form.csrf_provider

logout: true

anonymous: true

access_control:

– { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }

– { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }

– { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }

– { path: ^/, role: IS_AUTHENTICATED_FULLY }

Services.yml

parameters:

# parameter_name: value

services:

# service_name:

# class: AppBundle\Directory\ClassName

# arguments: ["@another_service_name", "plain_value", "%parameter_name%"]

gedmo.listener.timestampable:

class: Gedmo\Timestampable\TimestampableListener

tags:

– { name: doctrine.event_subscriber, connection: default }

calls:

– [ setAnnotationReader, [ "@annotation_reader" ] ]

Resources:

Security: layout.html.twig:

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8" />

<meta name="viewport" content="width=device-width, initial-scale=1">

<meta name="description" content="">

<meta name="author" content="">

<link rel="shortcut icon" href="{{ asset('images/favicon-dollar.ico') }}" type="image/x-icon">

<link rel="icon" href="{{ asset('images/favicon-dollar.ico') }}" type="image/x-icon">

<!– Latest compiled and minified CSS –>

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">

<!– Optional theme –>

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous">

<link rel="stylesheet" href="{{ asset('css/simple-sidebar.css') }}">

<link rel="stylesheet" href="{{ asset('css/font-awesome.min.css') }}">

<link rel="stylesheet" href="{{ asset('css/app.css') }}">

<link href='https://fonts.googleapis.com/css?family=Lato' rel='stylesheet' type='text/css'>

<link href="http://fonts.googleapis.com/css?family=Cookie" rel="stylesheet" type="text/css">

<script src="https://code.jquery.com/jquery-2.2.3.min.js"></script>

<!– Latest compiled and minified JavaScript –>

<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>

<!– HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries –>

<!– WARNING: Respond.js doesn't work if you view the page via file:// –>

<!–[if lt IE 9]>

<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>

<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>

<![endif]–>

{% block stylesheets %}{% endblock %}

{% block javascripts %}{% endblock %}

</head>

<body class="body-dark">

<!– Header –>

{% include 'common/header.html.twig' %}

<!– /#app-header –>

{% for type, messages in app.session.flashBag.all %}

{% for message in messages %}

<div class="{{ type }}">

{{ message|trans({}, 'FOSUserBundle') }}

</div>

{% endfor %}

{% endfor %}

<div class="login-container">

{% block fos_user_content %}

{% endblock fos_user_content %}

</div>

</body>

</html>

Messages.en.yml

menu:

control_panel: Dashboard

users_list: List users

files_list: List files

orders_list: List orders

cashings_list: List cashings

payment_matching: Cashings matching

pdfs: Payouts

layout:

logged_in_as: Welcome, %username%

login: Login

logout: Logout

common:

yes: yes

no: no

close: Close

upload: Upload

add: Add

generate: Generate

results: Results

reset: Reset

search: Search

no_results: No search results found

random_form_error: An error occurred. Please try again!

file_upload_successful: Fisierul a fost parsat cu success!

green: Green

red: Red

orange: Orange

payment_added: Payment created successfully!

wait: Please wait…

results_count: Results count

observations: Observations

dashboard:

labels:

users:

panel_title: Last 5 users authenticated

cashings:

panel_title: Last 5 Cargus cashings files

documentation:

panel_title: Technical documentation

users:

labels:

add_user: Add user

created_successfully: "User created successfully!"

column_headers:

id: ID

username: Username

email: Email

enabled: Enabled

admin: Admin

lastLogin: Date

orders:

labels:

filters:

orderNumber: "Order number"

date: "Order date"

dateFinalized: "Order finalization date"

productName: "Product name"

file: "File"

filter_panel_title: "Filter orders"

upload_file: "Upload file"

column_headers:

id: ID

orderNumber: "Order number"

date: "Order date"

dateFinalized: "Order finalization date"

productName: "Product name"

quantity: "Quantity"

value: "Order value"

errors:

invalid_file_type: "Uploaded file type must be CSV, XLS or XLSX"

payments:

labels:

filters:

orderNumber: "Order number"

date: "Order date"

dateFinalized: "Order finalization date"

productName: "Product name"

file: "File"

payment_parser: "Payment service"

filter_panel_title: "Filter payments"

upload_file: "Upload file"

column_headers:

id: ID

orderNumber: "Order number"

date: "Order date"

dateFinalized: "Order finalization date"

productName: "Product name"

quantity: "Quantity"

value: "Payment value"

errors:

invalid_file_type: "Uploaded file type must be CSV, XLS or XLSX"

matchings:

labels:

filters:

orderNumber: "Order number"

date: "Order date"

dateFinalized: "Finalization date"

productName: "Product name"

status: "Status"

filter_panel_title: "Filter"

add_payment: "Add payment"

generate_pdf: "Generate PDF"

pdf_name: "PDF Name"

legend: Legend

column_headers:

id: "ID"

orderNumber: "Order number"

date: "Order date"

dateFinalized: "Order finalization date"

productName: "Product name"

quantity: "Quantity"

value: "Order value (RON)"

paymentsValue: "Payments value (RON)"

difference: "Difference (RON)"

status: "Status"

actions: "Actions"

legend:

green: Great! The sum of the payments matches the order value.

orange: Oups! It seems the buyer has overpaid. We need to give some of the money back.

red: Unfortunately, the buyer has not yet fully paid the order.

files:

labels:

filters:

origin: Origin

name: Name

user: User

dateUploaded: Upload date

filter_panel_title: Filter files

origin:

site: Site

cargus: Cargus

column_headers:

id: ID

date: Date uploaded

origin: Origin

user: Username

name: File name

lines_count: Processed lines

total_value: Value

pdfs:

labels:

no_results: "Nu a fost generat niciun fisier PDF."

column_headers:

name: "PDF Name"

date_created: "Date created"

created_by: "Created by"

excel_parser:

errors:

row_error_label: "Row #{{ rowIndex }} has the following errors"

column_error: "Column '{{ columnName }}': {{ errorMessage }}"

exceptions:

missing_required_columns: "The following required columns are missing"

duplicated_columns: "The following columns are duplicatd"

csv_separator_notice: "None of the required columns were identified. Please make sure the CSV file uses a comma as a delimiter."

Messages.ro.yml

menu:

control_panel: Panou de control

users_list: Lista utilizatori

files_list: Lista fisiere

orders_list: Lista comenzi

cashings_list: Lista incasari

payment_matching: Mapare incasari

pdfs: Desfasuratoare

layout:

logged_in_as: Bine ai venit, %username%

login: Autentificare

logout: Iesire

common:

yes: da

no: nu

close: Inchide

upload: Incarca

add: Adauga

generate: Genereaza

results: Rezultate

reset: Reseteaza

search: Cauta

no_results: Cautarea nu a generat rezultate

random_form_error: Am intampinat o problema. Va rugam reincercati!

file_upload_successful: Fisierul a fost parsat cu success!

green: Verde

red: Rosu

orange: Portocaliu

payment_added: Plata a fost creata cu success!

wait: Va rugam, asteptati…

results_count: Numar rezultate

observations: Observatii

dashboard:

labels:

users:

panel_title: Ultimii 5 useri autentificati

cashings:

panel_title: Ultimele 5 fisiere de incasari incarcate

documentation:

panel_title: Documentatie tehnica

users:

labels:

add_user: Adauga utilizator

created_successfully: "Utilizatorul a fost creat cu success!"

column_headers:

id: ID

username: Nume

email: Adresa e-mail

enabled: Activ

admin: Administrator

lastLogin: Data

orders:

labels:

filters:

orderNumber: "Numar comanda"

date: "Data comanda"

dateFinalized: "Data finalizare comanda"

productName: "Nume produs"

file: "Fisier"

filter_panel_title: "Filtreaza comenzi"

upload_file: "Incarca fisier site"

column_headers:

id: ID

orderNumber: "Numar comanda"

date: "Data comanda"

dateFinalized: "Data finalizare comanda"

productName: "Nume produs"

quantity: "Cantitate"

value: "Valoare comanda"

errors:

invalid_file_type: "Fisierul incarcat trebuie sa aiba extensia CSV, XLS sau XLSX"

payments:

labels:

filters:

orderNumber: "Numar comanda"

date: "Data comanda"

dateFinalized: "Data finalizare comanda"

productName: "Nume produs"

file: "Fisier"

payment_parser: "Procesator plati"

filter_panel_title: "Filtreaza plati"

upload_file: "Incarca fisier plati"

column_headers:

id: ID

orderNumber: "Numar comanda"

date: "Data comanda"

dateFinalized: "Data finalizare comanda"

productName: "Nume produs"

quantity: "Cantitate"

value: "Valoare plata"

errors:

invalid_file_type: "Fisierul incarcat trebuie sa aiba extensia CSV, XLS sau XLSX"

matchings:

labels:

filters:

orderNumber: "Numar comanda"

date: "Data comanda"

dateFinalized: "Data finalizare"

productName: "Nume produs"

status: "Status"

filter_panel_title: "Filtrare"

add_payment: "Adauga plata"

generate_pdf: "Genereaza PDF"

pdf_name: "Nume PDF"

legend: Legenda

column_headers:

id: "ID"

orderNumber: "Numar comanda"

date: "Data comanda"

dateFinalized: "Data finalizare comanda"

productName: "Nume produs"

quantity: "Cantitate"

value: "Valoare comanda (RON)"

paymentsValue: "Valoare incasata (RON)"

difference: "Diferenta (RON)"

status: "Status"

actions: "Actiuni"

legend:

green: Perfect! Suma platilor este egala cu valoarea comenzii.

orange: Oups! Se pare ca utilizatorul a platit mai mult decat valoarea comenzii. Va trebui sa returnam o parte din bani.

red: Din pacate, cumparatorul nu a achitat inca intreaga valoare a comenzii.

files:

labels:

filters:

origin: Origine

name: Nume

user: Utilizator

dateUploaded: Data incarcare

filter_panel_title: Filtrare fisiere

origin:

site: Site

cargus: Cargus

column_headers:

id: ID

date: Data incarcare

origin: Origine

user: Utilizator

name: Nume fisier

lines_count: Linii procesate

total_value: Valoare

pdfs:

labels:

no_results: "No PDF file generated."

column_headers:

name: "Nume PDF"

date_created: "Data crearii"

created_by: "Creat de"

excel_parser:

errors:

row_error_label: "Randul #{{ rowIndex }} are urmatoarele erori"

column_error: "Coloana '{{ columnName }}': {{ errorMessage }}"

exceptions:

missing_required_columns: "Urmatoarele coloane obligatorii lipsesc"

duplicated_columns: "Urmatoarele coloane sunt duplicate"

csv_separator_notice: "Niciuna din coloanele asteptate nu a fost gasita. Te rugam sa verifici ca separatorul folosit de fisierul CSV este virgula."

Views:

Heades.html.twig

<div id="app-header">

<div class="app-name">Payment<span>Management</span></div>

<div class="app-status">

{% if is_granted("IS_AUTHENTICATED_REMEMBERED") %}

{{ 'layout.logged_in_as'|trans({'%username%': app.user.username}) }} |

<a href="{{ path('fos_user_security_logout') }}" class="logout-button">

{{ 'layout.logout'|trans({}) }}

</a>

{% else %}

<a href="{{ path('fos_user_security_login') }}" class="login-button">{{ 'layout.login'|trans({}) }}</a>

{% endif %}

<div class="app-locale">

<a class="{{ app.request.locale == 'ro' ? 'current-locale' : '' }}" href="?_locale=ro">RO</a>

<a class="{{ app.request.locale == 'en' ? 'current-locale' : '' }}" href="?_locale=en">EN</a>

</div>

</div>

</div>

Base.html.twig

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8" />

<title>{% block title %}Welcome!{% endblock %}</title>

{% block stylesheets %}{% endblock %}

<link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}" />

</head>

<body>

{% block body %}{% endblock %}

{% block javascripts %}{% endblock %}

</body>

</html>

Layout.html.twig

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8" />

<meta name="viewport" content="width=device-width, initial-scale=1">

<meta name="description" content="">

<meta name="author" content="">

<title>Disertatie – Orlando Dumitru</title>

<link rel="shortcut icon" href="{{ asset('images/favicon-dollar.ico') }}" type="image/x-icon">

<link rel="icon" href="{{ asset('images/favicon-dollar.ico') }}" type="image/x-icon">

<!– Latest compiled and minified CSS –>

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">

<!– Optional theme –>

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous">

<link rel="stylesheet" href="{{ asset('css/simple-sidebar.css') }}">

<link rel="stylesheet" href="{{ asset('css/font-awesome.min.css') }}">

<link rel="stylesheet" href="{{ asset('css/app.css') }}">

<link rel="stylesheet" href="{{ asset('css/helper.css') }}">

<link href='https://fonts.googleapis.com/css?family=Lato' rel='stylesheet' type='text/css'>

<link href="http://fonts.googleapis.com/css?family=Cookie" rel="stylesheet" type="text/css">

<script src="https://code.jquery.com/jquery-2.2.3.min.js"></script>

<script src="{{ asset('js/spin.min.js') }}"></script>

<script src="{{ asset('js/jquery.spin.js') }}"></script>

<script src="{{ asset('js/jquery.simplePagination.js') }}"></script>

<script src="//code.jquery.com/ui/1.11.4/jquery-ui.js"></script>

<link rel="stylesheet" href="//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css">

<!– Latest compiled and minified JavaScript –>

<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>

<!– HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries –>

<!– WARNING: Respond.js doesn't work if you view the page via file:// –>

<!–[if lt IE 9]>

<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>

<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>

<![endif]–>

<script src="{{ asset('js/common.js') }}"></script>

{% block stylesheets %}{% endblock %}

{% block javascripts %}{% endblock %}

</head>

<body>

<!– Header –>

{% include 'common/header.html.twig' %}

<!– /#app-header –>

<div id="wrapper">

<!– Sidebar –>

<div id="sidebar-wrapper">

<ul class="sidebar-nav">

<li {#class="sidebar-brand"#}>

<a href="{{ path('dashboard') }}" class="{{ app.request.attributes.get('_route') == 'dashboard' ? 'current' : '' }}">{{ 'menu.control_panel'|trans }}</a>

</li>

<li>

<a href="{{ path('users') }}" class="{{ app.request.attributes.get('_route') == 'users' ? 'current' : '' }}">{{ 'menu.users_list'|trans }}</a>

</li>

<li>

<a href="{{ path('files') }}" class="{{ app.request.attributes.get('_route') == 'files' ? 'current' : '' }}">{{ 'menu.files_list'|trans }}</a>

</li>

<li>

<a href="{{ path('orders') }}" class="{{ app.request.attributes.get('_route') == 'orders' ? 'current' : '' }}">{{ 'menu.orders_list'|trans }}</a>

</li>

<li>

<a href="{{ path('payments') }}" class="{{ app.request.attributes.get('_route') == 'payments' ? 'current' : '' }}">{{ 'menu.cashings_list'|trans }}</a>

</li>

<li>

<a href="{{ path('matchings') }}" class="{{ app.request.attributes.get('_route') == 'matchings' ? 'current' : '' }}">{{ 'menu.payment_matching'|trans }}</a>

</li>

<li>

<a href="{{ path('pdfs') }}" class="{{ app.request.attributes.get('_route') == 'pdfs' ? 'current' : '' }}">{{ 'menu.pdfs'|trans }}</a>

</li>

</ul>

{#<a href="#menu-toggle" id="menu-toggle"><i class="fa fa-angle-double-left fa-2x" aria-hidden="true"></i></a>#}

</div>

<!– /#sidebar-wrapper –>

<!– Page Content –>

<div id="page-content-wrapper">

<div class="container-fluid">

<div class="row">

<div class="col-md-12 col-lg-12 no-gutter">

{% for flashMessage in app.session.flashbag.get('error') %}

<div class="alert alert-danger alert-dismissible" role="alert">

<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>

{{ flashMessage | trans }}

</div>

{% endfor %}

{% for flashMessage in app.session.flashbag.get('notice') %}

<div class="alert alert-warning" role="alert">

<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>

{{ flashMessage | trans }}

</div>

{% endfor %}

{% for flashMessage in app.session.flashbag.get('success') %}

<div class="alert alert-success" role="alert">

<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>

{{ flashMessage | trans }}

</div>

{% endfor %}

</div>

</div>

<div class="row">

<div class="col-lg-12">

{% block content %}{% endblock %}

</div>

</div>

</div>

</div>

<!– /#page-content-wrapper –>

<div>

<!– Menu Toggle Script –>

<script>

$("#menu-toggle").click(function(e) {

e.preventDefault();

$("#wrapper").toggleClass("toggled");

});

</script>

</body>

</html>

BIN:

Console

#!/usr/bin/env php

<?php

use Symfony\Bundle\FrameworkBundle\Console\Application;

use Symfony\Component\Console\Input\ArgvInput;

use Symfony\Component\Debug\Debug;

// if you don't want to setup permissions the proper way, just uncomment the following PHP line

// read http://symfony.com/doc/current/book/installation.html#configuration-and-setup for more information

//umask(0000);

set_time_limit(0);

/**

* @var Composer\Autoload\ClassLoader $loader

*/

$loader = require __DIR__.'/../app/autoload.php';

$input = new ArgvInput();

$env = $input->getParameterOption(['–env', '-e'], getenv('SYMFONY_ENV') ?: 'dev');

$debug = getenv('SYMFONY_DEBUG') !== '0' && !$input->hasParameterOption(['–no-debug', '']) && $env !== 'prod';

if ($debug) {

Debug::enable();

}

$kernel = new AppKernel($env, $debug);

$application = new Application($kernel);

$application->run($input);

Symfony_requirements

#!/usr/bin/env php

<?php

require_once dirname(__FILE__).'/../var/SymfonyRequirements.php';

$lineSize = 70;

$symfonyRequirements = new SymfonyRequirements();

$iniPath = $symfonyRequirements->getPhpIniConfigPath();

echo_title('Symfony Requirements Checker');

echo '> PHP is using the following php.ini file:'.PHP_EOL;

if ($iniPath) {

echo_style('green', ' '.$iniPath);

} else {

echo_style('warning', ' WARNING: No configuration file (php.ini) used by PHP!');

}

echo PHP_EOL.PHP_EOL;

echo '> Checking Symfony requirements:'.PHP_EOL.' ';

$messages = array();

foreach ($symfonyRequirements->getRequirements() as $req) {

/** @var $req Requirement */

if ($helpText = get_error_message($req, $lineSize)) {

echo_style('red', 'E');

$messages['error'][] = $helpText;

} else {

echo_style('green', '.');

}

}

$checkPassed = empty($messages['error']);

foreach ($symfonyRequirements->getRecommendations() as $req) {

if ($helpText = get_error_message($req, $lineSize)) {

echo_style('yellow', 'W');

$messages['warning'][] = $helpText;

} else {

echo_style('green', '.');

}

}

if ($checkPassed) {

echo_block('success', 'OK', 'Your system is ready to run Symfony projects');

} else {

echo_block('error', 'ERROR', 'Your system is not ready to run Symfony projects');

echo_title('Fix the following mandatory requirements', 'red');

foreach ($messages['error'] as $helpText) {

echo ' * '.$helpText.PHP_EOL;

}

}

if (!empty($messages['warning'])) {

echo_title('Optional recommendations to improve your setup', 'yellow');

foreach ($messages['warning'] as $helpText) {

echo ' * '.$helpText.PHP_EOL;

}

}

echo PHP_EOL;

echo_style('title', 'Note');

echo ' The command console could use a different php.ini file'.PHP_EOL;

echo_style('title', '~~~~');

echo ' than the one used with your web server. To be on the'.PHP_EOL;

echo ' safe side, please check the requirements from your web'.PHP_EOL;

echo ' server using the ';

echo_style('yellow', 'web/config.php');

echo ' script.'.PHP_EOL;

echo PHP_EOL;

exit($checkPassed ? 0 : 1);

function get_error_message(Requirement $requirement, $lineSize)

{

if ($requirement->isFulfilled()) {

return;

}

$errorMessage = wordwrap($requirement->getTestMessage(), $lineSize – 3, PHP_EOL.' ').PHP_EOL;

$errorMessage .= ' > '.wordwrap($requirement->getHelpText(), $lineSize – 5, PHP_EOL.' > ').PHP_EOL;

return $errorMessage;

}

function echo_title($title, $style = null)

{

$style = $style ?: 'title';

echo PHP_EOL;

echo_style($style, $title.PHP_EOL);

echo_style($style, str_repeat('~', strlen($title)).PHP_EOL);

echo PHP_EOL;

}

function echo_style($style, $message)

{

// ANSI color codes

$styles = array(

'reset' => "\033[0m",

'red' => "\033[31m",

'green' => "\033[32m",

'yellow' => "\033[33m",

'error' => "\033[37;41m",

'success' => "\033[37;42m",

'title' => "\033[34m",

);

$supports = has_color_support();

echo($supports ? $styles[$style] : '').$message.($supports ? $styles['reset'] : '');

}

function echo_block($style, $title, $message)

{

$message = ' '.trim($message).' ';

$width = strlen($message);

echo PHP_EOL.PHP_EOL;

echo_style($style, str_repeat(' ', $width).PHP_EOL);

echo_style($style, str_pad(' ['.$title.']', $width, ' ', STR_PAD_RIGHT).PHP_EOL);

echo_style($style, str_pad($message, $width, ' ', STR_PAD_RIGHT).PHP_EOL);

echo_style($style, str_repeat(' ', $width).PHP_EOL);

}

function has_color_support()

{

static $support;

if (null === $support) {

if (DIRECTORY_SEPARATOR == '\\') {

$support = false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI');

} else {

$support = function_exists('posix_isatty') && @posix_isatty(STDOUT);

}

}

return $support;

}

Controller:

DefaultController

class DefaultController extends Controller

{

/**

* @Route("/", name="dashboard")

*/

public function indexAction(Request $request)

{

// replace this example code with whatever you need

// return $this->render('default/index.html.twig', [

// 'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..'),

// ]);

$em = $this->getDoctrine()->getManager();

$finder = new Finder();

/** @var \SplFileInfo[] $iterator */

$iterator = $finder->files()->in($this->get(PathService::SERVICE_NAME)->getDocumentationPath());

return $this->render('AppBundle:Default:index.html.twig', [

'users' => $em->getRepository(User::class)->findLastAuthenticated(),

'cashings' => $em->getRepository(CashingFile::class)->findBy([], ['created' => 'DESC'], 5),

'filesList' => $iterator,

]);

}

}

FilesController

<?php

namespace AppBundle\Controller;

use AppBundle\Entity\CashingFile;

use AppBundle\Entity\File;

use AppBundle\Entity\OrderFile;

use AppBundle\Form\Type\FilesFilterType;

use AppBundle\Service\PathService;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

use Symfony\Component\HttpFoundation\BinaryFileResponse;

use Symfony\Component\HttpFoundation\JsonResponse;

use Symfony\Component\HttpFoundation\Request;

use Symfony\Component\HttpFoundation\Response;

use Symfony\Component\HttpFoundation\ResponseHeaderBag;

use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class FilesController extends Controller

{

/**

* @Route("/files", name="files")

*

* @param Request $request

* @return \Symfony\Component\HttpFoundation\Response

*/

public function indexAction(Request $request)

{

$form = $this->createForm(FilesFilterType::class);

return $this->render('AppBundle:Files:index.html.twig', [

'form' => $form->createView(),

]);

}

/**

* @Route("/files/ajax/list", name="files.ajax.list")

*

* @param Request $request

* @return Response

*/

public function listAction(Request $request)

{

$params = $request->query->all();

$results = $this->getDoctrine()->getRepository(File::class)->filter($params);

foreach ($results->results as &$result) {

$result['downloadLink'] = $this->generateUrl('files.download', ['id' => $result['id']]);

}

return JsonResponse::create([

'results' => $results->results,

'totalResults' => $results->count,

'page' => array_key_exists('page', $params) ? $params['page'] : 1,

'orderBy' => $params['orderBy'],

'orderDir' => $params['orderDir'],

]);

}

/**

* @Route("/files/{id}/download", name="files.download")

*

* @param Request $request

* @return BinaryFileResponse

*/

public function downloadAction(Request $request, $id)

{

$file = $this->getDoctrine()->getRepository(File::class)->find($id);

$pathService = $this->get(PathService::SERVICE_NAME);

switch (true) {

case $file instanceof CashingFile:

$path = $pathService->getCashingsPath();

break;

case $file instanceof OrderFile:

$path = $pathService->getOrdersPath();

break;

default:

throw new NotFoundHttpException('Invalid file type.');

}

$response = new BinaryFileResponse($path . $file->getName());

$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $file->getOriginalName());

return $response;

}

}

MatchingsController

class MatchingsController extends Controller

{

/**

* @Route("/matchings", name="matchings")

*

* @param Request $request

* @return \Symfony\Component\HttpFoundation\Response

*/

public function indexAction(Request $request)

{

$form = $this->createForm(OrderPaymentMatchingFilterType::class);

return $this->render('AppBundle:Matchings:index.html.twig', [

'form' => $form->createView(),

]);

}

/**

* @Route("/matchings/ajax/list", name="matchings.ajax.list")

*

* @param Request $request

* @return Response

*/

public function listMatchingsAction(Request $request)

{

$params = $request->query->all();

$this->get('session')->set('matchings_filters', $params);

list($results, $totalResults) = $this->getOrders($params);

return JsonResponse::create([

'results' => $results,

'totalResults' => $totalResults,

'page' => array_key_exists('page', $params) ? $params['page'] : 1,

'orderBy' => $params['orderBy'],

'orderDir' => $params['orderDir'],

]);

}

/**

* @Route("/matchings/generate-pdf", name="matchings.generate_pdf")

*

* @param Request $request

* @return Response

*/

public function generatePdfAction(Request $request)

{

$ordersPdf = new OrdersPdfFile();

$form = $this->createFormBuilder($ordersPdf)

->setAction($this->generateUrl('matchings.generate_pdf'))

->setMethod('POST')

->add('name', TextType::class, [

'label' => 'matchings.labels.pdf_name',

'required' => true,

])

->getForm();

if ($request->isMethod('POST')) {

$form->handleRequest($request);

if ($form->isValid()) {

$pdfPath = $this->get('app.path')->getOrderPdfPath() . $ordersPdf->getName() . '.pdf';

$filters = $this->get('session')->get('matchings_filters', []);

unset($filters['page']);

list($orders,) = $this->getOrders($filters);

$html = $this->get('templating')->render(

'AppBundle:Matchings:pdf_template.html.twig',

['orders' => $orders]

);

$pdf = new \mPDF('', 'A4');

$pdf->writeHTML($html);

$pdf->Output($pdfPath, 'F');

$ordersPdf->setUser($this->container->get('security.token_storage')->getToken()->getUser());

$ordersPdf->setOriginalName($ordersPdf->getName());

$em = $this->get('doctrine')->getManager();

$em->persist($ordersPdf);

$em->flush();

// $response = new BinaryFileResponse($pdfPath);

// $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT);

//

// return $response;

return JsonResponse::create(['success' => true]);

}

}

if ($request->isXmlHttpRequest()) {

return JsonResponse::create([

'success' => false,

'html' => $this->get('twig')->render(

'AppBundle:Matchings:generate_pdf.html.twig',

['form' => $form->createView()]

),

]);

}

return $this->render('AppBundle:Matchings:generate_pdf.html.twig', [

'form' => $form->createView(),

]);

}

/**

* @param array $params

* @return array

*/

private function getOrders(array $params)

{

$results = [];

$orders = $this->getDoctrine()->getRepository(Order::class)->filterMatchings($params);

foreach ($orders as $o) {

list($order, $paymentsValue) = array_values($o);

$paymentsValue = $paymentsValue ?: 0;

$difference = $paymentsValue – $order->getValue();

$results[] = array_merge($order->toArray(), [

'paymentsValue' => $paymentsValue,

'difference' => $difference,

'status' => $difference <=> 0,

]);

}

return [$results, $orders->count()];

}

}

OrdersController

<?php

namespace AppBundle\Controller;

use AppBundle\Entity\File;

use AppBundle\Entity\Order;

use AppBundle\Entity\OrderFile;

use AppBundle\Entity\User;

use AppBundle\Form\Type\OrderFilterType;

use AppBundle\Service\FileHandlerService;

use AppBundle\Service\OrderParserService;

use AppBundle\Service\PathService;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

use Symfony\Component\Form\Extension\Core\Type\FileType;

use Symfony\Component\HttpFoundation\File\UploadedFile;

use Symfony\Component\HttpFoundation\JsonResponse;

use Symfony\Component\HttpFoundation\Request;

use Symfony\Component\HttpFoundation\Response;

class OrdersController extends Controller

{

/**

* @Route("/orders", name="orders")

*

* @param Request $request

* @return \Symfony\Component\HttpFoundation\Response

*/

public function indexAction(Request $request)

{

$form = $this->createForm(OrderFilterType::class);

return $this->render('AppBundle:Orders:index.html.twig', [

'form' => $form->createView(),

]);

}

/**

* @Route("/orders/upload", name="orders.upload");

*

* @param Request $request

* @return Response

*/

public function uploadAction(Request $request)

{

$form = $this->createFormBuilder()

->setAction($this->generateUrl('orders.upload'))

->setMethod('POST')

->add('file', FileType::class, [

'label' => 'orders.labels.filters.file',

'required' => true,

'constraints' => [

new \Symfony\Component\Validator\Constraints\File([

'mimeTypes' => ['text/csv', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],

]),

]

])

->getForm();

// form was submitted

if ($request->isMethod('POST')) {

$form->handleRequest($request);

$translator = $this->get('translator');

if ($form->isValid()) {

try {

/** @var UploadedFile $file */

$file = $form['file']->getData();

$this->get(OrderParserService::SERVICE_NAME)->processFile(

$file->getRealPath(),

function() use ($file) {

return $this->get(FileHandlerService::SERVICE_NAME)->moveOrdersFile($file);

}

);

$this->get('session')->getFlashBag()->set(

'success',

$translator->trans('common.file_upload_successful')

);

} catch (\Exception $e) {

$this->get('session')->getFlashBag()->set(

'error',

$e->getMessage()

);

}

} else {

$this->get('session')->getFlashBag()->set(

'error',

'orders.errors.invalid_file_type'

);

}

return $this->redirect($this->generateUrl('orders'));

}

return $this->render('AppBundle:Orders:upload.html.twig', [

'form' => $form->createView(),

]);

}

/**

* @Route("/orders/ajax/list", name="orders.ajax.list")

*

* @param Request $request

* @return Response

*/

public function listOrdersAction(Request $request)

{

$params = $request->query->all();

$orders = $this->getDoctrine()->getRepository(Order::class)->filter($params);

$results = [];

/** @var Order $order */

foreach ($orders as $order) {

$results[] = $order->toArray();

}

return JsonResponse::create([

'results' => $results,

'totalResults' => $orders->count(),

'page' => array_key_exists('page', $params) ? $params['page'] : 1,

'orderBy' => $params['orderBy'],

'orderDir' => $params['orderDir'],

]);

}

/**

* @Route("/orders/ajax/order-numbers", name="orders.ajax.order_numbers")

*

* @param Request $request

* @return Response

*/

public function orderNumbersListAction(Request $request)

{

$term = $request->query->get('term');

$results = $this->getDoctrine()->getRepository(Order::class)->findOrderNumbers($term);

return JsonResponse::create($results);

}

/**

* @Route("/orders/ajax/product-names", name="orders.ajax.product_names")

*

* @param Request $request

* @return Response

*/

public function productNameListAction(Request $request)

{

$term = $request->query->get('term');

$results = $this->getDoctrine()->getRepository(Order::class)->findProductNames($term);

return JsonResponse::create($results);

}

}

PaymentsController

<?php

namespace AppBundle\Controller;

use AppBundle\Entity\Order;

use AppBundle\Entity\Payment;

use AppBundle\Entity\PaymentProcessor;

use AppBundle\Form\Type\PaymentFilterType;

use AppBundle\Service\FileHandlerService;

use AppBundle\Service\PaymentParserService;

use Doctrine\ORM\EntityRepository;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

use Symfony\Bridge\Doctrine\Form\Type\EntityType;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

use Symfony\Component\Form\Extension\Core\Type\FileType;

use Symfony\Component\Form\Extension\Core\Type\NumberType;

use Symfony\Component\Form\Extension\Core\Type\TextareaType;

use Symfony\Component\Form\Extension\Core\Type\TextType;

use Symfony\Component\HttpFoundation\File\UploadedFile;

use Symfony\Component\HttpFoundation\JsonResponse;

use Symfony\Component\HttpFoundation\Request;

use Symfony\Component\HttpFoundation\Response;

use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class PaymentsController extends Controller

{

/**

* @Route("/payments", name="payments")

*

* @param Request $request

* @return \Symfony\Component\HttpFoundation\Response

*/

public function indexAction(Request $request)

{

$form = $this->createForm(PaymentFilterType::class);

return $this->render('AppBundle:Payments:index.html.twig', [

'form' => $form->createView(),

]);

}

/**

* @Route("/payments/upload", name="payments.upload");

*

* @param Request $request

* @return Response

*/

public function uploadAction(Request $request)

{

$form = $this->createFormBuilder()

->setAction($this->generateUrl('payments.upload'))

->setMethod('POST')

->add('paymentProcessor', EntityType::class, [

'class' => 'AppBundle:PaymentProcessor',

'choice_label' => 'name',

'label' => 'payments.labels.filters.payment_parser',

'required' => true,

'query_builder' => function (EntityRepository $repo) {

$qb = $repo->createQueryBuilder('pp');

return $qb->where('pp.id <> :manualProcessingId')

->setParameter('manualProcessingId', PaymentProcessor::PAYMENT_PROCESSING_MANUAL)

->orderBy('pp.id', 'ASC');

}

])

->add('file', FileType::class, [

'label' => 'payments.labels.filters.file',

'required' => true,

'constraints' => [

new \Symfony\Component\Validator\Constraints\File([

'mimeTypes' => ['text/csv', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],

]),

]

])

->getForm();

// form was submitted

if ($request->isMethod('POST')) {

$form->handleRequest($request);

$translator = $this->get('translator');

if ($form->isValid()) {

try {

/** @var PaymentProcessor $paymentProcessor */

$paymentProcessor = $form['paymentProcessor']->getData();

/** @var UploadedFile $file */

$file = $form['file']->getData();

$this->get(PaymentParserService::SERVICE_NAME)

->setPaymentProcessor($paymentProcessor)

->processFile($file->getRealPath(), function () use ($file) {

return $this->get(FileHandlerService::SERVICE_NAME)->moveCashingsFile($file);

});

$this->get('session')->getFlashBag()->set(

'success',

$translator->trans('common.file_upload_successful')

);

} catch (\Exception $e) {

$this->get('session')->getFlashBag()->set(

'error',

$e->getMessage()

);

}

} else {

$this->get('session')->getFlashBag()->set(

'error',

'payments.errors.invalid_file_type'

);

}

return $this->redirect($this->generateUrl('payments'));

}

return $this->render('AppBundle:Payments:upload.html.twig', [

'form' => $form->createView(),

]);

}

/**

* @Route("/payments/ajax/list", name="payments.ajax.list")

*

* @param Request $request

* @return Response

*/

public function listPaymentsAction(Request $request)

{

$params = $request->query->all();

$payments = $this->getDoctrine()->getRepository(Payment::class)->filter($params);

$results = [];

/** @var Payment $payment */

foreach ($payments as $payment) {

$results[] = [

'id' => $payment->getId(),

'value' => $payment->getValue(),

'order' => $payment->getOrder()->toArray()

];

}

return JsonResponse::create([

'results' => $results,

'totalResults' => $payments->count(),

'page' => array_key_exists('page', $params) ? $params['page'] : 1,

'orderBy' => $params['orderBy'],

'orderDir' => $params['orderDir'],

]);

}

/**

* @Route("/payments/add", name="payments.add");

*

* @param Request $request

* @return Response

*/

public function addAction(Request $request)

{

$orderId = $request->query->get('orderId', null);

$em = $this->getDoctrine()->getManager();

$order = $em->getRepository(Order::class)->find($orderId);

if (!$order) {

throw new NotFoundHttpException('Order does not exist.');

}

$form = $this->createFormBuilder()

->setAction($this->generateUrl('payments.add', ['orderId' => $orderId]))

->setMethod('POST')

->add('orderNumber', TextType::class, [

'label' => 'orders.labels.filters.orderNumber',

'data' => $orderId,

'disabled' => true,

])

->add('value', NumberType::class, [

'label' => 'payments.column_headers.value',

'scale' => 2,

])

->add('observations', TextareaType::class, [

'label' => 'common.observations',

])

->getForm();

if ($request->isMethod('POST')) {

$form->handleRequest($request);

$translator = $this->get('translator');

if ($form->isValid()) {

$value = $form['value']->getData();

$observations = $form['observations']->getData();

$paymentProcessor = $em->getRepository(PaymentProcessor::class)

->find(PaymentProcessor::PAYMENT_PROCESSING_MANUAL);

$payment = (new Payment())

->setOrder($order)

->setValue($value)

->setPaymentProcessor($paymentProcessor);

if (strlen(trim($observations))) {

$order->setObservations($observations);

}

$em->persist($payment);

$em->flush();

$this->get('session')->getFlashBag()->set(

'success',

$translator->trans('common.payment_added')

);

} else {

$this->get('session')->getFlashBag()->set(

'error',

$translator->trans('common.random_form_error')

);

}

return $this->redirect($this->generateUrl('matchings'));

}

return $this->render('AppBundle:Payments:add.html.twig', [

'form' => $form->createView(),

]);

}

}

PdfsController

<?php

namespace AppBundle\Controller;

use AppBundle\Entity\OrdersPdfFile;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

use Symfony\Component\HttpFoundation\BinaryFileResponse;

use Symfony\Component\HttpFoundation\Request;

use Symfony\Component\HttpFoundation\Response;

use Symfony\Component\HttpFoundation\ResponseHeaderBag;

class PdfsController extends Controller

{

/**

* @Route("/pdfs", name="pdfs")

*

* @param Request $request

* @return Response

*/

public function indexAction(Request $request)

{

$pdfs = $this->getDoctrine()->getRepository(OrdersPdfFile::class)->findBy([], ['id' => 'DESC']);

return $this->render('AppBundle:Pdfs:index.html.twig', [

'pdfs' => $pdfs,

]);

}

/**

* @Route("/pdfs/{id}/download", name="pdfs.download")

*

* @param Request $request

* @return BinaryFileResponse

*/

public function downloadAction(Request $request, $id)

{

$pdf = $this->getDoctrine()->getRepository(OrdersPdfFile::class)->find($id);

$response = new BinaryFileResponse($this->get('app.path')->getOrderPdfPath() . $pdf->getName() . '.pdf');

$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT);

return $response;

}

}

UsersController

<?php

namespace AppBundle\Controller;

use AppBundle\Entity\User;

use AppBundle\Form\Type\UserFilesFilterType;

use FOS\UserBundle\Form\Type\RegistrationFormType;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

use Symfony\Component\HttpFoundation\JsonResponse;

use Symfony\Component\HttpFoundation\Request;

class UsersController extends Controller

{

/**

* @Route("/users", name="users")

*/

public function indexAction(Request $request)

{

$users = $this->getDoctrine()->getRepository(User::class)->findAll();

return $this->render('AppBundle:Users:index.html.twig', [

'users' => $users,

]);

}

/**

* @Route("/users/add", name="users.add")

*

* @param Request $request

* @return \Symfony\Component\HttpFoundation\Response

*/

public function addAction(Request $request)

{

$user = new User();

$form = $this->createForm(

RegistrationFormType::class,

$user,

[

'validation_groups' => ['Registration'],

'action' => $this->generateUrl('users.add')

]

);

if ($request->isMethod('POST')) {

$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {

$em = $this->getDoctrine()->getManager();

$user->setEnabled(true);

$em->persist($user);

$em->flush();

$this->get('session')->getFlashBag()->add('success', $this->get('translator')->trans('users.labels.created_successfully'));

return JsonResponse::create(['success' => true]);

}

}

if ($request->isXmlHttpRequest()) {

return JsonResponse::create([

'success' => false,

'html' => $this->get('twig')->render(

'AppBundle:Users:add.html.twig',

['form' => $form->createView()]

),

]);

}

return $this->render('AppBundle:Users:add.html.twig', [

'form' => $form->createView(),

]);

}

/**

* @Route("/users/enable", name="users.enable")

*

* @param Request $request

* @return \Symfony\Component\HttpFoundation\Response

*/

public function enableToggleAction(Request $request)

{

$userId = $request->request->get('id');

$isEnabled = (bool)(int)$request->request->get('enabled');

$em = $this->getDoctrine()->getManager();

$user = $this->getDoctrine()->getRepository(User::class)->find($userId);

$user->setEnabled($isEnabled);

$em->flush();

return JsonResponse::create(['success' => true]);

}

/**

* @Route("/users/role", name="users.role")

*

* @param Request $request

* @return \Symfony\Component\HttpFoundation\Response

*/

public function makeAdminAction(Request $request)

{

$userId = $request->request->get('id');

$isAdmin = (bool)(int)$request->request->get('admin');

$em = $this->getDoctrine()->getManager();

$user = $this->getDoctrine()->getRepository(User::class)->find($userId);

$user->setSuperAdmin($isAdmin);

$em->flush();

return JsonResponse::create(['success' => true]);

}

/**

* @Route("/users/ajax/usernames", name="users.ajax.usernames")

*

* @param Request $request

* @return \Symfony\Component\HttpFoundation\Response|static

*/

public function listUsernamesAction(Request $request)

{

$term = $request->query->get('term');

$results = $this->getDoctrine()->getRepository(User::class)->findUsernames($term);

return JsonResponse::create($results);

}

}

DepedencyInjection

AppExtension

<?php

namespace AppBundle\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;

use Symfony\Component\Config\FileLocator;

use Symfony\Component\HttpKernel\DependencyInjection\Extension;

use Symfony\Component\DependencyInjection\Loader;

/**

* This is the class that loads and manages your bundle configuration

*

* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}

*/

class AppExtension extends Extension

{

/**

* {@inheritdoc}

*/

public function load(array $configs, ContainerBuilder $container)

{

$configuration = new Configuration();

$config = $this->processConfiguration($configuration, $configs);

$loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));

$loader->load('services.xml');

}

}

Configuration

<?php

namespace AppBundle\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\TreeBuilder;

use Symfony\Component\Config\Definition\ConfigurationInterface;

/**

* This is the class that validates and merges configuration from your app/config files

*

* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}

*/

class Configuration implements ConfigurationInterface

{

/**

* {@inheritdoc}

*/

public function getConfigTreeBuilder()

{

$treeBuilder = new TreeBuilder();

$rootNode = $treeBuilder->root('default');

// Here you should define the parameters that are allowed to

// configure your bundle. See the documentation linked above for

// more information on that topic.

return $treeBuilder;

}

}

Entity

FileRepository

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\EntityRepository;

class FileRepository extends EntityRepository

{

/**

* @param array $data

* @return array

* @throws \Doctrine\DBAL\DBALException

*/

public function filter($data)

{

$data = is_array($data) ? array_filter($data, 'strlen') : array();

$page = $this->getPage($data);

list($orderBy, $orderDir) = $this->getOrder($data);

$offset = ($page – 1) * 10;

$limit = 10;

$selectSql = 'f.id, f.name, f.original_name as originalName, f.created as dateUploaded, u.username as user, f.type,

(CASE

WHEN f.type = 1

THEN (SELECT COUNT(1) FROM orders o WHERE o.file_id = f.id)

WHEN f.type = 2

THEN (SELECT COUNT(1) FROM payments p WHERE p.file_id = f.id)

ELSE 0

END) as linesCount,

(CASE

WHEN f.type = 1

THEN (SELECT SUM(o.value) FROM orders o WHERE o.file_id = f.id)

WHEN f.type = 2

THEN (SELECT SUM(p.value) FROM payments p WHERE p.file_id = f.id)

ELSE 0

END) as totalValue';

$selectCountSql = 'COUNT(1) as count';

$sql = '

SELECT %s

FROM files f

INNER JOIN users u on f.user_id = u.id

WHERE %s %s

';

$limitSql = "ORDER BY {$orderBy} {$orderDir} LIMIT :limit OFFSET :offset";

$where = [sprintf('f.type IN (%s, %s)', File::FILE_TYPE_CASHINGS, File::FILE_TYPE_ORDERS)];

if (array_key_exists('origin', $data)) {

$where[] = "f.type = {$data['origin']}";

}

if (array_key_exists('name', $data)) {

$where[] = "f.original_name LIKE '%{$data['name']}%'";

}

if (array_key_exists('user', $data)) {

$where[] = "u.username = '{$data['user']}'";

}

if (array_key_exists('dateUploaded', $data)) {

$where[] = "DATE_FORMAT(f.created, '%Y-%m-%d') = '{$data['dateUploaded']}'";

}

$whereSql = implode(' AND ', $where);

$em = $this->getEntityManager();

$stmt = $em->getConnection()->prepare(

sprintf($sql, $selectSql, $whereSql, $limitSql)

);

$stmt->bindParam(':offset', $offset, \PDO::PARAM_INT);

$stmt->bindParam(':limit', $limit, \PDO::PARAM_INT);

$stmt->execute();

$result = new \stdClass();

$result->results = $stmt->fetchAll();

$stmt = $em->getConnection()->prepare(

sprintf($sql, $selectCountSql, $whereSql, '')

);

$stmt->execute();

$result->count = (int)$stmt->fetch()['count'];

return $result;

}

/**

* @param $data

* @return int

*/

protected function getPage(&$data)

{

$page = 1;

if (array_key_exists('page', $data)) {

$page = $data['page'];

unset($data['page']);

}

return $page;

}

/**

* @param $data

* @return bool

*/

protected function getOrder(&$data)

{

$by = isset($data['orderBy']) ? $data['orderBy'] : 'id';

$dir = isset($data['orderDir']) ? $data['orderDir'] : 'desc';

unset($data['orderBy'], $data['orderDir']);

return [$by, $dir];

}

}

OrderReopsitory

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\EntityRepository;

use Doctrine\ORM\QueryBuilder;

use Doctrine\ORM\Tools\Pagination\Paginator;

class OrderRepository extends EntityRepository

{

// fields that are search with LIKE,N not equality

protected $likeMatch = array('productName', 'date', 'dateFinalized');

protected function buildCondition($field, $value)

{

if (in_array($field, $this->likeMatch)) {

$c = sprintf('o.%s LIKE :%s', $field, $field);

$p = sprintf('%%%s%%', $value);

} else {

$c = sprintf('o.%s = :%s', $field, $field);

$p = $value;

}

return array($c, $p);

}

/**

* @param $term

* @return array

*/

public function findOrderNumbers($term)

{

$result = $this->createQueryBuilder('o')

->select('o.orderNumber')

->where('o.orderNumber LIKE :term')

->setParameter('term', '%' . $term . '%')

->distinct()

->getQuery()

->getResult();

return array_column($result, 'orderNumber');

}

/**

* @param $term

* @return array

*/

public function findProductNames($term)

{

$result = $this->createQueryBuilder('o')

->select('o.productName')

->where('o.productName LIKE :term')

->setParameter('term', '%' . $term . '%')

->distinct()

->getQuery()

->getResult();

return array_column($result, 'productName');

}

/**

* @param $data

* @return Paginator

*/

public function filter($data)

{

$data = is_array($data) ? array_filter($data, 'strlen') : array();

$page = $this->getPage($data);

/** @var QueryBuilder $qb */

$qb = $this->createQueryBuilder('o');

$this->order($qb, $data);

if (count($data)) {

$conditions = array();

$parameters = array();

foreach ($data as $k => $d) {

list($c, $p) = $this->buildCondition($k, $d);

$conditions[] = $c;

$parameters[$k] = $p;

}

$qb->where(implode(' AND ', $conditions));

$qb->setParameters($parameters);

}

$paginator = new Paginator($qb);

$paginator->getQuery()

->setFirstResult(($page – 1) * 10)

->setMaxResults(10);

return $paginator;

}

/**

* @param $data

* @return int

*/

protected function getPage(&$data)

{

$page = null;

if (array_key_exists('page', $data)) {

$page = $data['page'];

unset($data['page']);

}

return $page;

}

/**

* @param $qb

* @param $data

* @return bool

*/

protected function order($qb, &$data)

{

$by = isset($data['orderBy']) ? $data['orderBy'] : null;

$dir = isset($data['orderDir']) ? $data['orderDir'] : null;

unset($data['orderBy'], $data['orderDir']);

if (! $by) {

return false;

}

$qb->orderBy('o.' . $by, $dir);

return true;

}

public function filterMatchings(array $data)

{

$data = is_array($data) ? array_filter($data, 'strlen') : array();

$page = $this->getPage($data);

/** @var QueryBuilder $qb */

$qb = $this->createQueryBuilder('o');

$qb->select([

'o',

'(SELECT SUM(p.value) FROM AppBundle:Payment p WHERE p.order = o GROUP BY p.order) as paymentsValue'

]);

$this->order($qb, $data);

if (count($data)) {

$conditions = array();

$parameters = array();

foreach ($data as $k => $d) {

if ($k === 'status') {

switch ((int)$d) {

case Order::STATUS_GREEN:

$qb->having('paymentsValue IS NOT NULL')

->andHaving('o.value = paymentsValue');

break;

case Order::STATUS_RED:

$qb->having('paymentsValue IS NULL')

->orHaving('o.value > paymentsValue');

break;

case Order::STATUS_ORANGE:

$qb->having('paymentsValue IS NOT NULL')

->andHaving('o.value < paymentsValue');

break;

default:

break;

}

continue;

}

list($c, $p) = $this->buildCondition($k, $d);

$conditions[] = $c;

$parameters[$k] = $p;

}

if (count($conditions)) {

$qb->where(implode(' AND ', $conditions));

$qb->setParameters($parameters);

}

}

$paginator = new Paginator($qb);

if ($page) {

$paginator->getQuery()

->setFirstResult(($page – 1) * 10)

->setMaxResults(10);

}

return $paginator;

}

}

PaymentRepository

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\EntityRepository;

use Doctrine\ORM\QueryBuilder;

use Doctrine\ORM\Tools\Pagination\Paginator;

/**

* PaymentRepository

*/

class PaymentRepository extends EntityRepository

{

// fields that are search with LIKE, not equality

protected $likeMatch = array('productName', 'date', 'dateFinalized');

protected function buildCondition($field, $value)

{

if (in_array($field, $this->likeMatch)) {

$c = sprintf('o.%s LIKE :%s', $field, $field);

$p = sprintf('%%%s%%', $value);

} else {

$c = sprintf('o.%s = :%s', $field, $field);

$p = $value;

}

return array($c, $p);

}

public function filter(array $data)

{

$data = is_array($data) ? array_filter($data, 'strlen') : array();

$page = $this->getPage($data);

/** @var QueryBuilder $qb */

$qb = $this->createQueryBuilder('p');

$qb->join('p.order', 'o');

$this->order($qb, $data);

if (count($data)) {

$conditions = array();

$parameters = array();

foreach ($data as $k => $d) {

list($c, $p) = $this->buildCondition($k, $d);

$conditions[] = $c;

$parameters[$k] = $p;

}

$qb->where(implode(' AND ', $conditions));

$qb->setParameters($parameters);

}

$paginator = new Paginator($qb);

$paginator->getQuery()

->setFirstResult(($page – 1) * 10)

->setMaxResults(10);

return $paginator;

}

/**

* @param $data

* @return int

*/

protected function getPage(&$data)

{

$page = 1;

if (array_key_exists('page', $data)) {

$page = $data['page'];

unset($data['page']);

}

return $page;

}

/**

* @param QueryBuilder $qb

* @param $data

* @return bool

*/

protected function order(QueryBuilder $qb, &$data)

{

$by = isset($data['orderBy']) ? $data['orderBy'] : null;

$dir = isset($data['orderDir']) ? $data['orderDir'] : null;

unset($data['orderBy'], $data['orderDir']);

if (! $by) {

return false;

}

switch ($by) {

case 'id':

case 'value':

$qb->orderBy('p.' . $by, $dir);

break;

case 'orderNumber':

case 'date':

case 'dateFinalized':

$qb->orderBy('o.' . $by, $dir);

break;

default:

break;

}

return true;

}

}

UserRepository

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\EntityRepository;

class UserRepository extends EntityRepository

{

/**

* @param int $count

* @return array

*/

public function findLastAuthenticated($count = 5)

{

return $this->createQueryBuilder('u')

->orderBy('u.lastLogin', 'DESC')

->setMaxResults(5)

->getQuery()

->getResult();

}

public function findUsernames($term)

{

$result = $this->createQueryBuilder('u')

->select('u.username')

->where('u.username LIKE :term')

->setParameter('term', "%{$term}%")

->distinct()

->getQuery()

->getResult();

return array_column($result, 'username');

}

}

Listener

LocaleListener

<?php

namespace AppBundle\EventListener;

use Symfony\Component\HttpKernel\Event\GetResponseEvent;

use Symfony\Component\HttpKernel\KernelEvents;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class LocaleListener implements EventSubscriberInterface

{

private $defaultLocale;

public function __construct($defaultLocale = 'en')

{

$this->defaultLocale = $defaultLocale;

}

public function onKernelRequest(GetResponseEvent $event)

{

$request = $event->getRequest();

if (!$request->hasPreviousSession()) {

return;

}

// try to see if the locale has been set as a _locale routing parameter

if ($locale = $request->get('_locale')) {

$request->getSession()->set('_locale', $locale);

$request->setLocale($locale);

} else {

// if no explicit locale has been set on this request, use one from the session

$request->setLocale($request->getSession()->get('_locale', $this->defaultLocale));

}

}

public static function getSubscribedEvents()

{

return array(

// must be registered after the default Locale listener

KernelEvents::REQUEST => array(array('onKernelRequest', 15)),

);

}

}

Services

ExcelParser

<?php

namespace AppBundle\Service\Excel;

use AppBundle\Service\Excel\Exception\ExcelParserException;

use AppBundle\Service\Excel\Exception\ExceptionBuilder;

use Psr\Log\LoggerInterface;

use Symfony\Component\Translation\TranslatorInterface;

use Symfony\Component\Validator\Validator\ValidatorInterface;

/**

* Class ExcelParser

*

* TODO:

* – handle file with no headers

*/

class ExcelParserService extends ExcelParserAbstract

{

/** @var ExcelHeaderCollection */

protected $headersCollection;

/** @var ExcelRowCollection */

protected $rowCollection;

/** @var ValidatorInterface */

protected $validator;

/** @var LoggerInterface */

protected $logger;

/** @var ExceptionBuilder */

protected $exceptionBuilder;

/** @var TranslatorInterface */

protected $translator;

/** @var array */

protected $columnsMissingList = [];

/** @var bool */

protected $readOnlyData = false;

/** @var array */

protected $loadSheets = [];

/** @var int */

protected $startRow = 1;

/** @var int */

protected $chunkSize = null;

/** @var bool */

protected $readOnlyOnce = false;

/** @var array */

protected $envParams = [

'oneReadCycle' => true,

'useReadFilter' => false,

];

/**

* @param ValidatorInterface $validator

* @param LoggerInterface $logger

* @param ExceptionBuilder $exceptionBuilder

* @param TranslatorInterface $translator

*/

public function __construct(

ValidatorInterface $validator,

LoggerInterface $logger,

ExceptionBuilder $exceptionBuilder,

TranslatorInterface $translator

) {

$this->validator = $validator;

$this->logger = $logger;

$this->exceptionBuilder = $exceptionBuilder;

$this->translator = $translator;

}

/**

* @param bool $readOnlyData

*

* @return $this

*/

public function setReadOnlyData($readOnlyData)

{

$this->readOnlyData = (bool)$readOnlyData;

return $this;

}

/**

* @param int|int[] $loadSheets

*

* @return $this

*/

public function setLoadSheets($loadSheets)

{

$this->loadSheets = is_array($loadSheets) ? $loadSheets : [$loadSheets];

return $this;

}

/**

* @param int $startRow

*

* @return $this

*/

public function setStartRow($startRow)

{

$startRow = (int)$startRow;

$this->startRow = $startRow > 0 ? $startRow : 1;

return $this;

}

/**

* @param int $chunkSize

*

* @return $this

*/

public function setChunkSize($chunkSize)

{

$chunkSize = (int)$chunkSize;

$this->chunkSize = $chunkSize > 0 ? $chunkSize : 1;

return $this;

}

/**

* @param bool $readOnlyOnce

*

* @return $this

*/

public function setReadOnlyOnce($readOnlyOnce)

{

$this->readOnlyOnce = (bool)$readOnlyOnce;

return $this;

}

/**

* Load file for processing

*

* @param string $file

* @param array $columnsList

* @param array $rowConstraints

*

* @return $this

* @throws ExcelParserException

*/

public function loadFile($file, array $columnsList, array $rowConstraints = [])

{

$this->headersCollection = new ExcelHeaderCollection();

$this->rowCollection = new ExcelRowCollection();

$this->envParams['oneReadCycle'] = $this->readOnlyOnce || $this->chunkSize === null ? true : false;

$this->envParams['useReadFilter'] = $this->startRow === 1 && $this->chunkSize === null ? false : true;

// we always read the headers first

$this->logger->info("Loading file {$file} for parsing.");

$this->load($file, $this->buildConfig(new ExcelReadFilter(1, 1)));

$this->logger->info('Reading headers.');

$this->parseHeaders($columnsList);

if ($this->startRow === 1) {

$this->startRow++;

}

$config = $this->buildConfig();

if ($this->envParams['oneReadCycle']) {

$this->readCycle($file, $config);

} else {

$readFilter = $config->getReadFilter();

while ($readFilter->getStartRow() <= $this->getTotalRows()) {

$this->logger->info("Read cycle: rows interval is [{$readFilter->getStartRow()}, {$readFilter->getEndRow()})");

$this->readCycle($file, $config);

$readFilter->setRows($readFilter->getEndRow(), $this->chunkSize);

}

}

$this->logger->info("Finished parsing file {$file}.");

$this->rowCollection->validate($rowConstraints);

if (!$this->rowCollection->isValid()) {

$errorsFormatter = new ErrorsFormatter($this->rowCollection->getErrors(), $columnsList, $this->translator);

throw new ExcelParserException($errorsFormatter->getAsString());

}

return $this;

}

/**

* @param ExcelReadFilter $readFilter

*

* @return ExcelParserConfig

*/

protected function buildConfig(ExcelReadFilter $readFilter = null)

{

if ($readFilter === null && $this->envParams['useReadFilter']) {

$readFilter = new ExcelReadFilter($this->startRow, $this->chunkSize);

}

return new ExcelParserConfig(

$this->readOnlyData,

$readFilter,

$this->loadSheets

);

}

/**

* @param string $file

* @param ExcelParserConfig $config

*

* @return $this

*/

protected function readCycle($file, ExcelParserConfig $config)

{

$this->load($file, $config);

$this->logger->info("[$file] Reading rows.");

$this->parseDataRows();

$this->freeMemory();

$this->logger->info('Ending read cycle. Freed memory.');

return $this;

}

/**

* Return list of headers

*

* @return \ArrayIterator

*/

public function getHeaders()

{

return $this->headersCollection->getIterator();

}

/**

* Return rows data

*

* @return \ArrayIterator

*/

public function getData()

{

return $this->rowCollection->getIterator();

}

/**

* Read and validate file headers

*

* @param array $columnsList

*

* @return $this

* @throws ExcelParserException

*/

protected function parseHeaders(array $columnsList)

{

$this->setCurrentRow(1);

$headerRowData = $this->readRow();

$this->nextRow();

$columnsResolver = new ColumnsResolver($columnsList, $headerRowData);

if ($columnsResolver->hasMissingColumns()) {

throw $this->exceptionBuilder->createMissingRequiredColumnsException(

ExcelParserException::class,

$columnsResolver->getColumnsMissing()

);

}

if ($columnsResolver->hasDuplicatedColumns()) {

throw $this->exceptionBuilder->createDuplicatedColumnsException(

ExcelParserException::class,

$columnsResolver->getColumnsDuplicated()

);

}

foreach ($headerRowData as $colIndex => $colName) {

$colKey = array_search(

strtolower($colName),

$columnsResolver->getColumnsExpectedLowercase()

);

if ($colKey === false) {

continue;

}

$this->headersCollection->add(

new ExcelHeader($colName, $colIndex, $colKey)

);

}

return $this;

}

/**

* Read file data by row

*

* @return $this

*/

protected function parseDataRows()

{

if ($this->getRowsCount() === 0) { // todo: not needed ? returns false result ?

return $this;

}

$headers = $this->headersCollection->listIndexedKeys();

$headerIndexes = array_keys($headers);

while ($this->getCurrentRow()) {

$xlsRowData = $this->readRow($headerIndexes);

$rowData = [];

if (count(array_filter($xlsRowData))) { // if row is not empty

foreach ($xlsRowData as $columnIndex => $value) {

if (array_key_exists($columnIndex, $headers)) {

$rowData[$headers[$columnIndex]] = $value;

}

}

$this->rowCollection->add(

new ExcelRow($this->getCurrentRow()->getRowIndex(), $rowData, $this->validator)

);

}

$this->nextRow();

}

return $this;

}

}

FileHandlerService

<?php

namespace AppBundle\Service;

use AppBundle\Entity\CashingFile;

use AppBundle\Entity\File;

use AppBundle\Entity\OrderFile;

use AppBundle\Entity\User;

use Doctrine\ORM\EntityManagerInterface;

use Symfony\Component\HttpFoundation\File\UploadedFile;

use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

class FileHandlerService

{

const SERVICE_NAME = 'app.file_handler';

/** @var EntityManagerInterface */

private $manager;

/** @var TokenStorageInterface */

private $tokenStorage;

/** @var PathService */

private $pathService;

/**

* FileHandlerService constructor.

* @param EntityManagerInterface $em

* @param TokenStorageInterface $ts

* @param PathService $p

*/

public function __construct(EntityManagerInterface $em, TokenStorageInterface $ts, PathService $p)

{

$this->manager = $em;

$this->tokenStorage = $ts;

$this->pathService = $p;

}

public function moveOrdersFile(UploadedFile $file)

{

$orderFileName = $this->buildFileName($file, File::FILE_TYPE_ORDERS);

$file->move(

$this->pathService->getOrdersPath(),

$orderFileName

);

$orderFile = (new OrderFile())

->setName($orderFileName)

->setOriginalName($file->getClientOriginalName())

->setUser($this->getUser());

$this->saveFile($orderFile);

return $orderFile;

}

public function moveCashingsFile(UploadedFile $file)

{

$cashingFileName = $this->buildFileName($file, File::FILE_TYPE_CASHINGS);

$file->move(

$this->pathService->getCashingsPath(),

$cashingFileName

);

$cashingFile = (new CashingFile())

->setName($cashingFileName)

->setOriginalName($file->getClientOriginalName())

->setUser($this->getUser());

$this->saveFile($cashingFile);

return $cashingFile;

}

/**

* @param $file

*/

private function saveFile($file)

{

$this->manager->persist($file);

$this->manager->flush();

}

/**

* @return User

*/

private function getUser()

{

return $this->tokenStorage->getToken()->getUser();

}

/**

* @param UploadedFile $file

* @param int $fileType

* @return string

*/

private function buildFileName(UploadedFile $file, int $fileType)

{

return sprintf(

'%s_%s_%s.%s',

time(),

$this->getUser()->getId() . $fileType,

uniqid(),

$file->getClientOriginalExtension()

);

}

}

FileParserService

<?php

namespace AppBundle\Service;

use AppBundle\Service\Excel\ExcelParserInterface;

use Doctrine\ORM\EntityManagerInterface;

class FileParserService

{

/** @var ExcelParserInterface */

protected $excelParser;

/** @var EntityManagerInterface */

protected $manager;

/** @var array */

protected $columns = [];

/**

* FileParserService constructor.

*/

public function __construct()

{

$this->defineColumns();

}

/**

* @param ExcelParserInterface $excelParser

*/

public function setExcelParser(ExcelParserInterface $excelParser)

{

$this->excelParser = $excelParser;

}

/**

* @param EntityManagerInterface $manager

*/

public function setManager(EntityManagerInterface $manager)

{

$this->manager = $manager;

}

/**

* @param string $filePath

* @param callable $saveFileFunction

* @return array

* @throws \Exception

*/

public function processFile(string $filePath, callable $saveFileFunction)

{

if (!is_readable($filePath)) {

throw new \Exception(

sprintf('File %s is not readable.', $filePath)

);

}

/** @var \ArrayIterator $data */

$data = $this->excelParser

->setReadOnlyData(false) // http://stackoverflow.com/questions/24553792/phpexcel-issue-with-date-format

->setStartRow(1)

->setChunkSize(1000)

->loadFile(

$filePath,

$this->getColumns(),

$this->getColumnConstraints()

)

->getData();

return $this->processFileData($data, $saveFileFunction);

}

/**

* Get the list of columns to read as array ('columnKey' => 'columnHeader')

* eg: Array(

* 'productId' => 'id produs'

* )

*

* @return array

*/

protected function getColumns()

{

$cols = array();

foreach ($this->columns as $column => $columnSettings) {

$cols[$column] = $columnSettings['name'];

}

return $cols;

}

/**

* Get the constraints defined for each excel column, indexed by column key

* eg: Array(

* 'productId' => array(

* new NotBlank(),

* )

* )

*

* @return array

*/

protected function getColumnConstraints()

{

$constraints = array();

foreach ($this->columns as $column => $columnSettings) {

$constraints[$column] = array_key_exists('constraints', $columnSettings)

? $columnSettings['constraints']

: array();

}

return $constraints;

}

/**

* Do something with the file data

*

* @param $data

* @param callable $saveFileFunction

* @throws \Exception

*/

protected function processFileData($data, callable $saveFileFunction)

{

throw new \Exception('Method implementation missing.');

}

/**

* Define the list of column the Excel file contains and the constraints to be applied for each column

*

* @throws \Exception

*/

protected function defineColumns()

{

throw new \Exception('Method implementation missing.');

}

}

OrderParserService

<?php

namespace AppBundle\Service;

use AppBundle\Entity\Order;

use AppBundle\Entity\OrderFile;

use AppBundle\Service\Excel\ExcelRow;

use Carbon\Carbon;

use Doctrine\ORM\EntityManagerInterface;

use Symfony\Component\Validator\Constraints as Assert;

final class OrderParserService extends FileParserService

{

const SERVICE_NAME = 'app.order_parser';

/**

* @param \ArrayIterator $data

* @param callable $saveFileFunction

*/

protected function processFileData($data, callable $saveFileFunction)

{

$file = $saveFileFunction();

if (!$file instanceof OrderFile) {

throw new \LogicException('Expected instance of OrderFile.');

}

/** @var ExcelRow $row */

foreach ($data as $row) {

$rowData = $row->toArray();

$order = (new Order())

->setOrderNumber($rowData['orderNumber'])

->setDate(Carbon::createFromFormat('Y-m-d H:i:s', $rowData['date']))

->setDateFinalized(Carbon::createFromFormat('Y-m-d H:i:s', $rowData['dateFinalized']))

->setProductName($rowData['productName'])

->setProductQuantity($rowData['quantity'])

->setValue($rowData['value'])

->setFile($file);

$this->manager->persist($order);

}

$this->manager->flush();

}

/**

* {@inheritdoc}

*/

protected function defineColumns()

{

$this->columns = [

'orderNumber' => [

'name' => 'Numar comanda',

'constraints' => [

new Assert\Type([

'type' => 'numeric',

])

],

],

'date' => [

'name' => 'Data',

'constraints' => [],

],

'dateFinalized' => [

'name' => 'Data finalizare',

'constraints' => [],

],

'productName' => [

'name' => 'Produs',

'constraints' => [],

],

'quantity' => [

'name' => 'Cantitate',

'constraints' => [],

],

'value' => [

'name' => 'Valoare (RON)',

'constraints' => [

new Assert\Type([

'type' => 'float',

])

],

],

];

}

}

PathService

<?php

namespace AppBundle\Service;

/**

* Class PathService

*/

class PathService

{

const SERVICE_NAME = 'app.path';

/** @var string */

private $dir;

/**

* PathService constructor.

* @param string $dir

*/

public function __construct($dir)

{

$this->dir = $dir;

}

/**

* Create directory if it does not exist

*

* @param array $path

* @return array|string

* @throws \Exception

*/

private function make(array $path)

{

$path = $this->generatePath($path);

if (!is_dir($path) && !mkdir($path, 0777, true)) {

throw new \Exception('Could not create target directory.');

}

return $path;

}

/**

* Creates the path string from array of elements

*

* @param array $path

* @return string

*/

private function generatePath(array $path)

{

$regexp = sprintf('/\%s{2,}/', DIRECTORY_SEPARATOR);

$pathString = implode(DIRECTORY_SEPARATOR, $path) . DIRECTORY_SEPARATOR;

return preg_replace($regexp, DIRECTORY_SEPARATOR, $pathString);

}

/**

* @return string

* @throws \Exception

*/

public function getOrdersPath()

{

$path = [$this->dir, 'orders'];

return $this->make($path);

}

/**

* @return string

* @throws \Exception

*/

public function getCashingsPath()

{

$path = [$this->dir, 'cashings'];

return $this->make($path);

}

/**

* @return string

* @throws \Exception

*/

public function getOrderPdfPath()

{

$path = [$this->dir, 'pdfs'];

return $this->make($path);

}

/**

* @return string

* @throws \Exception

*/

public function getDocumentationPath()

{

$path = [$this->dir, 'documentation'];

return $this->make($path);

}

}

PaymentParserService

<?php

namespace AppBundle\Service;

use AppBundle\Entity\CashingFile;

use AppBundle\Entity\Order;

use AppBundle\Entity\Payment;

use AppBundle\Entity\PaymentProcessor;

use AppBundle\Service\Excel\ExcelRow;

use Symfony\Component\Validator\Constraints as Assert;

final class PaymentParserService extends FileParserService

{

const SERVICE_NAME = 'app.payment_parser';

/** @var PaymentProcessor */

private $paymentProcessor;

/**

* @param PaymentProcessor $paymentProcessor

* @return $this

*/

public function setPaymentProcessor(PaymentProcessor $paymentProcessor)

{

$this->paymentProcessor = $paymentProcessor;

return $this;

}

/**

* @param \ArrayIterator $data

* @param callable $saveFileFunction

* @return \ArrayIterator|void

*/

protected function processFileData($data, callable $saveFileFunction)

{

if (!$this->paymentProcessor) {

throw new \InvalidArgumentException('No payment processor given.');

}

$file = $saveFileFunction();

if (!$file instanceof CashingFile) {

throw new \LogicException('Expected instance of CashingFile.');

}

$orderRepo = $this->manager->getRepository(Order::class);

/** @var ExcelRow $row */

foreach ($data as $row) {

$rowData = $row->toArray();

$order = $orderRepo->findOneBy(['orderNumber' => $rowData['orderNumber']]);

if (!$order) {

continue;

}

$payment = (new Payment())

->setOrder($order)

->setPaymentProcessor($this->paymentProcessor)

->setValue($rowData['value'])

->setFile($file);

$this->manager->persist($payment);

}

$this->manager->flush();

return $data;

}

/**

* {@inheritdoc}

*/

protected function defineColumns()

{

$this->columns = [

'orderNumber' => [

'name' => 'Numar comanda',

'constraints' => [

new Assert\Type([

'type' => 'numeric',

])

],

],

'value' => [

'name' => 'Valoare (RON)',

'constraints' => [

new Assert\Type([

'type' => 'float',

])

],

],

];

}

}

CD / DVD

Index

B

Bibliografie………………………………………………….90

C

Cuprins…………………………………………………………x

Codul sursa………………………………………………….93

CSS…………………………………………………………….13

D

Doctrine……………………………………………………….10

E

e-Commerce……………………………………………………8

H

HTML……………………………………………………………9

Heidi…………………………………………………………….46

I

Introducere……………………………………………………..1

J

JavaScript……………………………………………………..29

Jquery…………………………………………………………..36

M

Motivatie………………………………………………………..2

Management……………………………………………………6

MySQL………………………………………………………..39

L

LISTA FIGURILOR……………………………,………..xii

P

PHP……………………………………………………………..17

PhpStorm………………………………………………………44

R

Referințe web………………………………………………..91

S

Symfony……………………………………………………….23

Similar Posts

  • Consumul de Droguridoc

    === Consumul de droguri === ROMÂNIA MINISTERUL EDUCAȚIEI NAȚIONALE ȘI CERCETĂRII ȘTIINȚIFICE ȘCOALA POSTLICEALĂ „HENRI COANDĂ” PROIECT PENTRU EXAMENUL DE CALIFICARE A COMPETENȚELOR PROFESIONALE NIVEL 5 ÎNDRUMĂTOR PROIECT: Profesor –––––––- ABSOLVENT: Elev LĂBONȚU Elena CONSTANȚA -2016- ȘCOALA POSTLICEALĂ „HENRI COANDĂ” CONSTANȚA CALIFICAREA: ASISTENT MEDICAL DE FARMACIE FORMA DE ÎNVĂȚĂMÂNT: ZI Consumul de droguri ÎNDRUMĂTOR PROIECT:…

  • Combaterea Traficului de Fiinte Umane

    FACULTATEA DE FLOSOFIE ȘIITINȚE SOCIAL POLITICE SPECIALIZAREA: ASISTENȚĂ SOCALĂ COMBATEREA TRAFICUL DE FIINȚE UMANE Obiect:Politici-Sociale STUDENT: NEDELCU VLAD AN 2,GRUPA 3 IASI-2016 1. Natura problemei sociale Traficul de ființe umane se defineste ca fiind recrutarea, transportarea, transferul, adăpostirea sau primirea unei persoane cu sau fără consimțămîntul acesteia, în scop de exploatare sexuală comercială sau necomercială,…

  • Factori Care Distorsionează Concurența în Economia de Piață

    === 63b007557b4094f946f7140eb0e96f86569066e9_498159_1 === Capitolul 3 Studiu de caz Parerea tinerilor asupra marcilor de echipamente sportive contrafacute 3.1 Metodologia cercetării Metodologia de cercetare calitativă, dar și cantitativă, a prevăzut aplicarea unui chestionar cu 16 întrebări, ce a fost introdus în cadrul unui studiu de caz, ceea ce a necesitat construirea unui eșantion reprezentativ alcătuit din 50…

  • Modalitati de Abordare a Conflicetlor In Relatiile Internationaledoc

    === Modalitati de abordare a conflicetlor in relatiile internationale === АCАDEMIА DE STUDII ECONOMICE BUCUREȘTI FАCULTАTEА DE RELАTII ECONOMICE INTERNАTIONАLE LUCRАRE DE LICENȚĂ COORDONАTOR ȘTIINȚIFIC BELU MIHAELA АBSOLVENTĂ PIPOI MADALINA BUCUREȘTI 2016 АCАDEMIА DE STUDII ECONOMICE BUCUREȘTI FАCULTАTEА DE RELАTII ECONOMICE INTERNАTIONАLE MODАLITĂȚI DE АBORDАRE А CONFLICTELOR ÎN RELАȚIILE INTERNАȚIONАLE COORDONАTOR ȘTIINȚIFIC BELU MIHAELA АBSOLVENTĂ…

  • Managementul Destinatiei Turistice Italia

    === 01320fd13b34cb91cdb2d5d2255981663220eff6_636916_1 === Concluzii Introducere–––––––––––––––––––––––––––––––-2 Capitolul 1. Managementul destinatiei turistice – delimitari conceptuale–––––––-4 1.1. Tipologia destinației turistice–––––––––––––––––––––––4 1.2. Dinamica turismului și impactul destinațiilor turistice la economia destinațiilor turistice ––––––––––––––––––––––––––––––––––––8 1.3. Politicile statale și rolul statului în dezvoltarea Italiei ca destinație turistică––––-12 Capitolul 2. Analiza destinației turistice Italia––––––––––––––––––15 2.1. Italia – localizare și puncte de atracție…

  • Modalitati de Abordare a Conflictelor In Organizatia Scolaradoc

    === Modalitati de abordare a conflictelor in organizatia scolara === Introducere Cоnflіctul este ɑtât de prezent șі de іntegrɑt în vіɑțɑ de zі cu zі, încât ɑm început să ne оbіșnuіm cu prezențɑ luі. Lumeɑ dіn jurul nоstru este mоdelɑtă șі desenɑtă de cоnflіct, de încleștɑreɑ de energіі dіn cɑre ne fоrmăm nоі pоsіbіlіtățі șі…