Cu structura de bază a cadrului nostru în vigoare, este timpul să începem să adăugăm funcționalitate la acesta. În acest tutorial vom crea un manager de șabloane și un handler de baze de date, care ne va aduce un pas mai aproape de un cadru puternic, potrivit pentru aproape orice proiect. Dacă nu ați făcut-o deja, asigurați-vă că ați examinat prima parte a acestei serii!
În prima parte a acestui tutorial, am creat un dosar numit controlere pentru a stoca logica de afaceri pentru aplicațiile noastre. Așa cum a subliniat Daok într-un comentariu, acest lucru nu este cel mai bun loc pentru toată logica de afaceri, și că a model ar trebui să fie utilizate pentru a stoca această logică. Anterior, am folosit întotdeauna baza de date în sine ca model în majoritatea aplicațiilor mele, cu toate acestea, separarea acestui lucru puțin mai mult va face cadrului nostru chiar mai puternic și mai ușor de extins.
Deci, ce este MVC? MVC este un model de design (așa cum au fost modelele Singleton și Registry pe care le-am analizat în partea 1) și reprezintă Model View Controller, iar scopul acestui model este de a separa logica de afaceri, acțiunile interfeței utilizator și interfața de utilizator de la unul pe altul. Deși nu vom face nimic cu modelele și controlorii noștri încă, să actualizăm structura directorului de cadre pentru a include dosarul "modele". Modelul va conține principala logică de afaceri, iar controlorul se va ocupa de interacțiunea utilizatorului (de exemplu, trimiterea de date, cum ar fi un comentariu). NB: Funcția noastră __autoload nu trebuie schimbată.
Cele mai multe site-uri web și aplicații web care utilizează PHP, de asemenea, fac uz de un motor baze de date, cum ar fi MySQL. Dacă păstrăm toate funcțiile bazate pe bază de date în același loc, atunci putem (teoretic) modifica cu ușurință motorul bazei de date pe care îl folosim. De asemenea, putem simplifica anumite operațiuni, cum ar fi inserarea de înregistrări, actualizarea înregistrărilor sau ștergerea înregistrărilor din baza de date. De asemenea, poate fi mai ușor atunci când se ocupă de mai multe conexiuni de baze de date.
Deci ... ce ar trebui al nostru operatorul de bază de date face:
Să ne uităm la codul handlerului bazei de date, apoi îl vom discuta după aceea.
conexiuni [] = noi mysqli ($ host, $ user, $ password, $ database); $ connection_id = numără ($ this-> connections) -1; dacă (mysqli_connect_errno ()) trigger_error ('Eroare la conectare la gazdă.'. $ this-> conexiuni [$ connection_id] -> error, E_USER_ERROR); return $ link_id; / ** * Închide conexiunea activă * @return void * / funcția publică closeConnection () $ this-> conexiuni [$ this-> activeConnection] -> close (); / ** * Modificarea conexiunii bazei de date utilizată în mod activ pentru următoarea operație * @param int noul id de conectare * @return void * / funcția publică setActiveConnection (int $ new) $ this-> activeConnection = $ new; / ** * Păstrați o interogare în cache-ul de interogare pentru procesarea mai târziu * @param String șirul de interogare * @returnul a indicat interogarea din cache * / public cacheQuery ($ queryStr) if (! $ Result = $ acest lucru -> conexiuni [$ this-> activeConnection] -> interogare ($ queryStr)) trigger_error ('Eroare la executarea și cache query:' $ this-> connections [$ this-> activeConnection] -> eroare, E_USER_ERROR); retur -1; altceva $ this-> queryCache [] = $ rezultat; retur numărul ($ this-> queryCache) -1; / ** * Obțineți numărul de rânduri din cache * @param int cache pointer interogare * @return int numărul de rânduri * / funcția publică numRowsFromCache ($ cache_id) return $ this-> queryCache [$ cache_id] -> num_rows; / ** * Obținerea rândurilor dintr-o interogare memorată în cache * @param int pointer cache de interogare * @return array rând * / funcția publică funcțiaFromCache ($ cache_id) return $ this-> queryCache [$ cache_id] -> fetch_array MYSQLI_ASSOC); / ** * Păstrați unele date într-o memorie cache pentru mai târziu * @param array datele * @ return int indicat la matricea cache-ului de date * / public cacheData ($ data) $ this-> dataCache [ date $; retur numărul ($ this-> dataCache) -1; / ** * Obțineți date din cache-ul de date * @param int data cache indicat * @return array data * / funcția publică dataFromCache ($ cache_id) return $ this-> dataCache [$ cache_id]; / ** * Ștergeți înregistrările din baza de date * @param String tabelul pentru a elimina rândurile de la * @param String condiția pentru care rândurile urmează să fie eliminate * @param int numărul de rânduri de eliminat * @return void * / funcția publică deleteRecords ($ table, $ condition, $ limit) $ limit = ($ limit == ")?": "LIMIT". limită de $; $ delete = "Șterge din $ table WHERE $ condition $ limit"; $ this-> executeQuery ($ delete); / ** * Actualizarea înregistrărilor în baza de date * @param String the table * @param array of changes field => value * @param String condiția * @return bool * / public function updateRecords ($ table, $ changes, $ condition ) $ update = "UPDATE". $ tabel. " A STABILIT "; foreach ($ se modifică ca $ field => $ value) $ update. = "" ". $ câmp. " '=' $ Value“"; // eliminați trailingul nostru, $ update = substr ($ update, 0, -1); ($ update); $ return- true; / ** * Inserarea inregistrarilor in baza de date * @param String the database table * @param matrice de date pentru a introduce câmp => valoare * @return bool * / funcția publică insertRecords ($ table, $ data) // configurarea unor variabile pentru câmpuri și valori $ fields = ""; / / ($ date) ca $ f => $ v) $ fields. = "'$ f',"; $ values. )), $ 'v', '; // eliminați trailing noastre, $ fields = substr ($ fields, 0, -1); // eliminați trailing, $ values = substr ( $ $ = $ INSERT INTO $ table ($ fields) Valori ($ values) "; $ this-> executeQuery ($ insert); return true; / ** * Executați un șir de interogare * @param String interogare * @return void * / funcția publică executeQuery ($ queryStr) if (! $ Result = $ this-> connections [$ this-> activeConnection] -> interogare ($ queryStr)) trigger_error ('Eroare în executarea interogării:'. $ this-> connections [$ t his-> activeConnection] -> eroare, E_USER_ERROR); altceva $ this-> last = $ result; / ** * Obținerea rândurilor din interogarea cea mai recentă executată, cu excepția interogărilor memorate în cache * @return array * / funcția publică getRows () return $ this-> last-> fetch_array (MYSQLI_ASSOC); / ** * Obține numărul de rânduri afectate din interogarea anterioară * @return int numărul rândurilor afectate * / funcția publică afectatăRows () return $ this -> $ this-> conexiuni [$ this-> activeConnection] - > affected_rows; / ** * Sanitizează datele * @param String datele care urmează să fie dezinfectate * @return String datele dezinfectate * / funcția publică sanitizeData ($ data) retur $ this-> conexiuni [$ this-> activeConnection] -> real_escape_string $ date); / ** * Deconstruiți obiectul * închideți toate conexiunile bazei de date * / funcția publică __deconstruct () foreach ($ this-> conexiuni ca $ conexiune) $ connection-> close (); ?>
Înainte de a discuta acest lucru în detaliu, ar trebui să subliniez că acest handler de bază de date este foarte simplu. Am putea oferi abstracție completă prin faptul că nu executăm interogări în mod direct, ci construim în schimb interogări bazate pe paramaters unei funcții de interogare și executându-o.
Metodele noastre de ștergere, inserare și actualizare facilitează efectuarea unor sarcini comune (așa cum am menționat mai sus, am putea extinde acest lucru pentru a face mult mai mult), oferind doar informații cum ar fi numele tabelului, o serie de câmpuri și valori corespunzătoare, valorile și condițiile limită. Interogările pot fi, de asemenea, "memorate în cache", astfel încât să putem face lucrurile cu ele mai târziu. Mă găsesc această caracteristică (precum și capacitatea de a "cache" matricea de date) este foarte utilă atunci când este combinată cu un manager de șabloane, deoarece putem itera cu ușurință prin rânduri de date și să o populam în șabloanele noastre, vedeți când ne uităm la managerul de șabloane.
// introduceți înregistrarea $ registry-> getObject ('db') -> insertRecords ('testTable', array ('name' => 'Michael')); // actualizați un registru $ register-> getObject ('db') -> updateRecords ('testTable', array ('name' => 'MichaelP'), 'ID = 2'); // ștergeți o înregistrare (bine, până la 5 în acest caz) $ registry-> getObject ('db') -> deleteRecords ('testTable', 'name =' MichaelP '";
De asemenea, putem lucra cu mai multe conexiuni de baze de date relativ ușor, atâta timp cât schimbăm conexiunile potrivite atunci când avem nevoie (deși acest lucru nu va funcționa atunci când cache-interogările și le-a preluat prin managerul șablonului fără a mai lucra) Fragmentul de cod de mai jos ne-ar permite să ștergem înregistrări din două baze de date.
// a doua conexiune la baza de date (să presupunem că avem deja o conexiune la DB-ul principal) $ newConnection = $ registry-> getObject ('db') -> newConnection ('localhost', 'root', 'password' „); // ștergeți din conexiunea db primară $ registry-> getObject ('db') -> deleteRecords ('testTable', 'name =' MichaelP '", 5); // modificați conexiunea db activă, pentru a permite ca interogările viitoare să se afle pe a doua conexiune $ registry-> getObject ('db') -> setActiveConnection ($ newConnection); // ștergeți din conexiunea secundară db $ registry-> getObject ('db') -> deleteRecords ('testTable', 'name =' MichaelP '", 5); // revin conexiunea activă, astfel că interogările viitoare se află pe conexiunea db primară $ registry-> getObject ('db') -> setActiveConnection (0);
Cum am putea dori să extindem această clasă?
Managerul de șabloane va gestiona toate ieșirile, trebuie să poată lucra cu diferite fișiere de șabloane diferite, să înlocuiască substituenții (le numesc etichete) cu date și să itereze prin părți ale șablonului cu mai multe rânduri de date din baza de date.
Pentru a face lucrurile mai ușoare, vom folosi o clasă de pagini pentru a conține conținutul legat de pagină. De asemenea, acest lucru ne ușurează extinderea și adăugarea de funcții ulterioare. Managerul de șabloane va gestiona acest obiect.
pagina = noua pagină (); / ** * Adăugați un bit șablon pe pagina noastră * @param String $ tag tag-ul unde inserăm șablonul de ex. hello * @param String $ biți bitul șablonului (calea către fișier sau doar numele fișierului) * @return void * / funcția publică addTemplateBit ($ tag, $ bit) if (strpos ($ bit, ) === false) $ bit = 'piei /'. PCARegistry :: getSetting ("piele"). '/ șabloane /'. bit $; $ this-> page-> addTemplateBit ($ tag, $ bit); / ** * Puneți biți șablon în conținutul paginii noastre * Actualizează conținutul paginilor * @return void * / funcția privată replaceBits () $ bits = $ this-> page-> getBits (); foreach ($ biți ca $ tag => $ template) $ templateContent = file_get_contents ($ bit); $ newContent = str_replace (''. $ tag. '', $ templateContent, $ this-> page-> getContent ()); $ this-> page-> setContent ($ newContent); / ** * Înlocuiți etichetele în pagina noastră cu conținut * @return void * / private function replaceTags () // a obține etichetele $ tags = $ this-> page-> getTags (); / / / ($ data [0] == 'SQL') // este o interogare memorată în cache ... ($ tags [$] = $ data) if_array ($ data) înlocuiți etichetele DB $ this-> replaceDBTags ($ tag, $ data [1]); elseif ($ data [0] == 'DATA') // sunt câteva date stocate în cache ... înlocuiți etichetele de date $ this-> replaceDataTags ($ tag, $ data [1]); altfel // înlocuiți conținutul $ newContent = str_replace (''. $ tag '', $ date, $ this-> page-> getContent ()); // actualizați conținutul paginilor $ this-> page-> setContent ($ newContent); / ** * Înlocuiți conținutul din pagină cu datele din tagul DB * @param String $ eticheta care definește zona de conținut * @param int $ cacheId ID-ul interogărilor în memoria cache * @return void * / private ($ tag, $ cacheId) $ block = "; $ blockOld = $ this-> page-> getBlock ($ tag); // foreach înregistrare referitoare la interogare ... în timp ce ($ tags = PCARegistry :: getObject db ') -> resultsFromCache ($ cacheId)) $ blockNew = $ blockOld; // crea un nou bloc de conținut cu rezultatele înlocuite în el (tagurile $ ca $ ntag => $ date) $ blockNew = str_replace ("$. $ ntag.", $ date, $ blockNew); $ block. = $ blockNew; $ pageContent = $ this-> page-> getContent (); // eliminați separatorul din șablon , curat HTML $ newContent = str_replace ('". $ blockOld. '', $ bloc, $ pageContent); // actualizați conținutul paginii $ this-> page-> setContent ($ newContent); / ** * Înlocuiți conținutul paginii cu date din memoria cache * @param String $ tag eticheta care definește zona de conținut * @param int $ cacheId ID-ul dat în cache-ul de date * @return void * / private replaceDataTags ($ etichetă, $ cacheId) $ block = $ this-> page-> getBlock ($ tag); $ blockOld = $ bloc; în timp ce ($ tags = PCARegistry :: getObject ('db') -> dataFromCache ($ cacheId)) foreach (etichete $ ca $ tag => $ date) $ blockNew = $ blockOld; $ blockNew = str_replace ("". $ tag. "", date $, $ blockNew); $ block. = $ blockNew; $ pageContent = $ this-> page-> getContent (); $ newContent = str_replace ($ blockOld, $ bloc, $ pageContent); $ this-> page-> setContent ($ newContent); / ** * Obține obiectul de pagină * @return Object * / funcția publică getPage () return $ this->; / ** * Setați conținutul paginii pe baza unui număr de șabloane * locați fișierele șablonului de șablon ca argumente individuale * @return void * / funcția publică buildFromTemplates () $ bits = func_get_args (); $ content = ""; foreach ($ biți ca $ bit) if (strpos ($ bit, 'skins /') === false) $ bit = 'piei /'. PCARegistry :: getSetting ("piele"). '/ șabloane /'. bit $; dacă (file_exists ($ bit) == true) $ content. = file_get_contents ($ bit); $ this-> page-> setContent ($ content); / ** * Convertește o serie de date (adică un rând db?) La unele etichete * @param array datele * @param string un prefix care este adăugat la numele câmpului pentru a crea numele etichetei * @return void * / public funcția dataToTags ($ date, $ prefix) foreach ($ date ca $ cheie => conținut $) $ this-> page-> addTag ($ key. $ prefix, $ content); funcția publică parseTitle () $ newContent = str_replace ('"," “. $ page-> getTitle (), $ aceasta-> pagina-> getContent ()); $ this-> page-> setContent ($ newContent); / ** * Parsează obiectul de pagină într-o anumită ieșire * @return void * / funcția publică parseOutput () $ this-> replaceBits (); $ This-> replaceTags (); $ This-> parseTitle (); ?>
Deci, ce face exact această clasă?
Creează obiectul de pagină și îl bazează pe fișiere șablon, obiectul de pagină conține conținutul și informațiile necesare pentru a alcătui HTML-ul paginii. Apoi, buildFromTemplate ('templatefile.tpl.php', 'templatefile2.tpl.php') pentru a obține conținutul inițial pentru pagina noastră, această metodă ia ca argument orice număr de fișiere șablon și le îmbină în ordine, utile pentru antet, conținut și footer șabloane.
Gestionează conținutul asociat paginii prin ajutarea obiectului de pagină să mențină o înregistrare a datelor care trebuie înlocuite în pagină, precum și biți suplimentari de șabloane care trebuie integrați în pagină (addTemplateBit ('userbar', 'usertoolsbar.tpl.php')).
Adaugă date și conținut în pagină prin efectuarea de diverse operațiuni de înlocuire a conținutului paginii, inclusiv prin preluarea rezultatelor dintr-o interogare memorată în cache și adăugarea lor la pagină.
Fișierul șablon trebuie să marcheze în sine în cazul în care trebuie interogată o interogare stocată în cache și să fie înlocuite datele din interogare. Când managerul de șabloane întâlnește o etichetă pentru a înlocui care este o interogare, ea primește bucata paginii în care are nevoie pentru a repeta prin apelul getBlock ("bloc") pe obiectul paginii. Această bucată de conținut este apoi copiată pentru fiecare înregistrare din interogare și etichetele din cadrul acesteia sunt înlocuite cu rezultatele interogării. Vom analiza modul în care aceasta arată în șablon mai târziu în acest tutorial.
Obiectul de pagină este gestionat de managerul de șabloane și conținea toate detaliile legate de pagină. Acest lucru lasă managerul șabloanelor să se ocupe de libertate, în același timp facilitând extinderea funcționalității acestuia la o dată ulterioară.
titlu; funcția publică setPassword ($ password) $ this-> password = $ password; funcția publică setTitle ($ title) $ this-> title = $ title; funcția publică setContent ($ content) $ this-> content = $ content; funcția publică addTag ($ key, $ data) $ this-> tags [$ key] = $ date; funcția publică getTags () return $ this-> tags; funcția publică addPPTag ($ key, $ data) $ this-> postParseTags [$ key] = $ date; / ** * Obțineți etichetele care urmează să fie analizate după ce primul lot a fost analizat * @return array * / funcția publică getPPTags () return $ this-> postParseTags; / ** * Adăugați un șablon bit la pagină, doesnt adăuga de fapt conținutul doar încă * @ param String eticheta în cazul în care șablonul este adăugat * @ param String numele fișierului șablon * @return void * / funcția publică addTemplateBit ($ etichetă, $ bit) $ this-> biți [$ tag] = $ bit; / ** * Obțineți biți șablon pentru a fi introdus în pagină * @return array matricea etichetelor de șablon și nume de fișiere șablon * / funcția publică getBits () return $ this-> bits; / ** * Obține o bucată de conținut de pagină * @param String eticheta care înfășoară blocul ( bloc ) * @return String blocul de conținut * / funcția publică getBlock ($ tag) preg_match ('#(. +?)#si ', $ this-> content, $ tor); $ tor = str_replace ('',' ', $ tor [0]); $ tor = str_replace ('',' ', $ tor); întoarcere $ tor; funcția publică getContent () return $ this-> content; ?>
Cum poate fi extinsă și îmbunătățită această clasă?
Acum că avem niște obiecte pe care registrul nostru le va stoca, trebuie să le spunem registrului care sunt obiectele acestea. Am creat o metodă în obiectul PCARegistry numit loadCoreObjects care (așa cum se spune) încarcă obiectele de bază. Acest lucru înseamnă că poate fi numit doar din fișierul index.php pentru a încărca registrul cu aceste obiecte.
funcția publică funcția storeCoreObjects () $ this-> storeObject ("baza de date", "db"); $ this-> storeObject ("șablon", "șablon");
Această metodă poate fi modificată mai târziu pentru a încorpora celelalte obiecte de bază pe care ar trebui să le încarce registrul, desigur că pot exista obiecte pe care doriți ca registrul să le gestioneze, dar numai în funcție de aplicația în care este folosit cadrul. Aceste obiecte ar fi încărcate în afara acestei metode.
Pentru a putea demonstra noile caracteristici adăugate în cadrul nostru, avem nevoie de o bază de date care să utilizeze funcția de gestionare a bazei de date și unele funcții de gestionare a șabloanelor (în cazul în care înlocuim un bloc de conținut cu rândurile din baza de date).
Site-ul demonstrativ pe care îl vom face cu cadrele noastre până la sfârșitul acestei serii de tutoriale este un site web cu un director de membri, deci facem o tabelă de bază de bază pentru profilurile membrilor, care conține un ID, un nume și o adresă de e-mail.
Evident, avem nevoie de câteva rânduri de date în acest tabel!
Pentru ca orice să fie afișat, avem nevoie de un șablon de bază, unde vom lista datele din tabelul membrilor noștri.
Powered by PCA Framework Membrii noștri
Mai jos este o listă a membrilor noștri:
Membrii START și membri END Membrii HTML denotă blocul de membri (care se obține prin metoda getBlock () din pagină), acesta este locul în care managerul de șabloane va itera prin înregistrările din baza de date și le va afișa.
Acum, trebuie să aducem toate astea, împreună cu fișierul index.php:
// cere registrul nostru requ_once ('PCARegistry / pcaregistry.class.php'); $ registrar = PCARegistry :: singleton (); // stocați acele obiecte de bază $ registry-> storeCoreObjects (); // crea o conexiune la baza de date $ registry-> getObject ('db') -> newConnection ('localhost', 'root', ',' pcaframework '); mai târziu ...) $ registry-> storeSetting ('default', 'skin'); // populează obiectul de pagină dintr-un fișier șablon $ registry-> getObject ('template') -> buildFromTemplates ('main.tpl.php') ; // cache o interogare a tabelului membrilor nostri $ cache = $ registry-> getObject ('db') -> cacheQuery ('SELECT * FROM members'); // aloca membrilor tag-ul $ registry-> getObject (' template ') -> getPage () -> addTag (' members ', array (' SQL ', $ cache)); // set title page $ registry-> getObject setTitle ("Membrii noștri"); // parse toate și scuipați-l de la $ registry-> getObject ('template') -> parseOutput (); print $ registry-> getObject (' -> getContent ();
Dacă vedem acum această pagină în browserul nostru, rezultatele interogării sunt afișate pe pagină:
În partea a treia vom face o ușoară ocolire din partea de dezvoltare a Cadrului nostru și vom examina cum să proiectăm cu cadrul nostru în minte și cum să aliniem șabloanele HTML astfel încât acestea să fie potrivite pentru cadrul nostru. Când vom începe să construim prima noastră aplicație cu cadrul nostru, vom examina mai detaliat unele dintre lucrările acestor clase. În cele din urmă, vă mulțumesc pentru comentariile dvs. ultima dată!