Con la struttura di base per il nostro Framework in atto, è ora di iniziare ad aggiungere funzionalità ad esso. In questo tutorial creeremo un gestore di template e un gestore di database, portandoci un passo avanti verso un potente Framework adatto all'uso per quasi tutti i progetti. Se non lo hai già fatto, assicurati di rivedere prima la parte 1 di questa serie!
Nella prima parte di questo tutorial, abbiamo creato una cartella chiamata controllori per archiviare la logica aziendale per le nostre applicazioni. Come ha sottolineato Daok in un commento, questo non è il posto migliore per tutte le logiche di business, e che a modello dovrebbe essere usato per memorizzare questa logica. In precedenza, ho sempre usato il database come modello nella maggior parte delle mie applicazioni, tuttavia, separare un po 'di più renderà il nostro framework ancora più potente e più facile da estendere.
Quindi, cos'è MVC? MVC è un modello di progettazione (come lo erano i modelli Singleton e Registry che abbiamo esaminato nella parte 1), e sta per Model View Controller, e lo scopo di questo modello è quello di separare la business logic, le azioni dell'interfaccia utente e l'interfaccia utente da l'un l'altro. Sebbene non stiamo ancora facendo nulla con i nostri modelli e controllori, aggiorniamo la nostra struttura di cartelle framework per includere la cartella "models". Il modello conterrà la logica di business principale e il controller tratterà l'interazione dell'utente (ad esempio l'invio di dati, ad esempio un commento). NB: la nostra funzione __autoload non ha bisogno di essere modificata.
La maggior parte dei siti Web e delle applicazioni Web che fanno uso di PHP utilizzano anche un motore di database, come MySQL. Se manteniamo tutte le funzioni relative ai database nello stesso posto, possiamo (in teoria) modificare facilmente il motore di database che usiamo. Possiamo inoltre semplificare alcune operazioni, come l'inserimento di record, l'aggiornamento di record o l'eliminazione di record dal database. Può anche semplificare la gestione di più connessioni al database.
Quindi ... cosa dovrebbe nostro gestore di database fare:
Diamo un'occhiata al codice per il nostro gestore di database, quindi ne discuteremo in seguito.
connections [] = new mysqli ($ host, $ user, $ password, $ database); $ connection_id = count ($ this-> connections) -1; if (mysqli_connect_errno ()) trigger_error ('Errore durante la connessione all'host.'. $ this-> connections [$ connection_id] -> error, E_USER_ERROR); return $ connection_id; / ** * Chiudi la connessione attiva * @return void * / public function closeConnection () $ this-> connections [$ this-> activeConnection] -> close (); / ** * Modifica quale connessione di database viene utilizzata attivamente per l'operazione successiva * @param int il nuovo ID di connessione * @return void * / public function setActiveConnection (int $ new) $ this-> activeConnection = $ new; / ** * Memorizza una query nella cache di query per l'elaborazione successiva * @param String la stringa di query * @return l'indirizzata alla query nella cache * / public function cacheQuery ($ queryStr) if (! $ Result = $ this-> connections [$ this-> activeConnection] -> query ($ queryStr)) trigger_error ('Errore durante l'esecuzione e la ricerca nella cache:'. $ this-> connections [$ this-> activeConnection] -> error, E_USER_ERROR); ritorno -1; else $ this-> queryCache [] = $ result; return count ($ this-> queryCache) -1; / ** * Ottieni il numero di righe dalla cache * @param int il puntatore della cache di query * @return int il numero di righe * / funzione pubblica numRowsFromCache ($ cache_id) return $ this-> queryCache [$ cache_id] -> NUM_ROWS; / ** * Ottieni le righe da una query memorizzata nella cache * @param int il puntatore della cache di query * @return array la riga * / public function resultsFromCache ($ cache_id) return $ this-> queryCache [$ cache_id] -> fetch_array ( MYSQLI_ASSOC); / ** * Memorizza alcuni dati in una cache per un successivo * @param array i dati * @return int il puntatore all'array nella cache di dati * / public function cacheData ($ data) $ this-> dataCache [] = $ dati; return count ($ this-> dataCache) -1; / ** * Ottieni dati dalla cache di dati * @param int data cache pointed * @return array data * / public function dataFromCache ($ cache_id) return $ this-> dataCache [$ cache_id]; / ** * Elimina i record dal database * @param Stringa la tabella per rimuovere le righe da * @param String la condizione per cui le righe devono essere rimosse * @param int il numero di righe da rimuovere * @return void * / funzione pubblica deleteRecords ($ table, $ condition, $ limit) $ limit = ($ limit == ")?": 'LIMIT'. $ Limite; $ delete = "DELETE FROM $ table DOVE $ condition $ limit"; $ this-> executeQuery ($ delete); / ** * Aggiorna record nel database * @param String la tabella * @param array of changes campo => valore * @param String la condizione * @return bool * / public function updateRecords ($ table, $ changes, $ condition ) $ update = "UPDATE". $ tabella. " IMPOSTATO "; foreach ($ cambia come $ campo => $ valore) $ update. = "'". $ campo. " '=' $ Value',"; // rimuove il nostro trailing, $ update = substr ($ update, 0, -1); if ($ condition! = ") $ update. =" WHERE ". $ condition; $ this-> executeQuery ($ update); return true; / ** * Inserisci record nel database * @param String il database table * @param array data per inserire field => value * @return bool * / public function insertRecords ($ table, $ data) // imposta alcune variabili per campi e valori $ fields = ""; $ values = ""; // popolarli foreach ($ data as $ f => $ v) $ fields. = "'$ f',"; $ values. = (is_numeric ($ v) && (intval ($ v) == $ v ))? $ v. ",": "'$ v',"; // rimuove il nostro finale, $ fields = substr ($ fields, 0, -1); // rimuove il nostro trailing, $ values = substr ( $ values, 0, -1); $ insert = "INSERT INTO $ table ($ fields) VALUES ($ values)"; $ this-> executeQuery ($ insert); return true; / ** * Esegui una stringa di query * @param String la query * @return void * / public function executeQuery ($ queryStr) if (! $ Result = $ this-> connections [$ this-> activeConnection] -> query ($ queryStr)) trigger_error ('Errore durante l'esecuzione della query:'. $ this-> connections [$ t his-> activeConnection] -> error, E_USER_ERROR); else $ this-> last = $ result; / ** * Ottieni le righe dalla query eseguita più di recente, escludendo le query memorizzate nella cache * @return array * / public function getRows () return $ this-> last-> fetch_array (MYSQLI_ASSOC); / ** * Ottiene il numero di righe interessate dalla query precedente * @return int il numero di righe interessate * / funzione pubblica affectedRows () return $ this -> $ this-> connections [$ this-> activeConnection] - > affected_rows; / ** * Sanitize data * @param Stringa i dati da disinfettare * @return Stringa i dati sterilizzati * / public function sanitizeData ($ data) return $ this-> connections [$ this-> activeConnection] -> real_escape_string ( $ dati); / ** * Decostruisci l'oggetto * chiudi tutte le connessioni del database * / public function __deconstruct () foreach ($ this-> connections come $ connection) $ connection-> close (); ?>
Prima di discuterne più dettagliatamente, vorrei sottolineare che questo gestore di database è molto basilare. Potremmo fornire un'astrazione completa non eseguendo direttamente le query, ma costruendo invece query basate su parametri su una funzione di query e quindi eseguendole.
I nostri metodi di cancellazione, inserimento e aggiornamento dei record rendono più semplice l'esecuzione di alcune attività comuni (come accennato in precedenza potremmo estendere questo per fare molto di più), fornendo solo informazioni come il nome della tabella, una matrice di campi e valori corrispondenti, valori limite e condizioni. Le query possono anche essere "memorizzate nella cache" in modo che possiamo fare le cose con loro in seguito. Trovo che questa funzionalità (oltre alla capacità di "memorizzare" array di dati) sia molto utile se combinata con un gestore di template, poiché possiamo facilmente scorrere tra le righe di dati e popolarla nei nostri modelli con un po 'di confusione, come farai vedere quando guardiamo il gestore di template.
// inserisce record $ registry-> getObject ('db') -> insertRecords ('testTable', array ('name' => 'Michael')); // aggiorna un record $ registry-> getObject ('db') -> updateRecords ('testTable', array ('name' => 'MichaelP'), 'ID = 2'); // cancella un record (beh, fino a 5 in questo caso) $ registry-> getObject ('db') -> deleteRecords ('testTable', "name = 'MichaelP'", 5);
Possiamo anche lavorare con più connessioni di database in modo relativamente semplice, purché cambiamo tra le connessioni appropriate quando è necessario (sebbene ciò non funzioni quando si memorizzano nella cache le query e le si recupera tramite il nostro gestore di template senza ulteriore lavoro), ad esempio, lo snippet di codice seguente ci consentirebbe di eliminare i record da due database.
// la nostra seconda connessione al database (supponiamo di avere già una connessione al nostro DB principale) $ newConnection = $ registry-> getObject ('db') -> newConnection ('localhost', 'root', 'password', 'secondDB '); // cancella dalla connessione db primaria $ registry-> getObject ('db') -> deleteRecords ('testTable', "name = 'MichaelP'", 5); // cambia la nostra connessione db attiva, per consentire che le query future siano sulla seconda connessione $ registry-> getObject ('db') -> setActiveConnection ($ newConnection); // cancella dalla connessione db secondaria $ registry-> getObject ('db') -> deleteRecords ('testTable', "name = 'MichaelP'", 5); // ripristina la connessione attiva in modo che le query future siano sulla connessione db primaria $ registry-> getObject ('db') -> setActiveConnection (0);
Come potremmo voler estendere questa classe?
Il gestore dei modelli gestirà tutto l'output, dovrà essere in grado di lavorare con vari file di modelli diversi, sostituire i segnaposti (li chiamo tag) con i dati e scorrere tra le parti del modello con più righe di dati dal database.
Per semplificare le cose, faremo uso di una classe di pagine per contenere il contenuto relativo alla pagina, questo ci rende anche più facile estenderlo e aggiungere funzionalità in seguito. Il gestore dei modelli gestirà questo oggetto.
page = new Page (); / ** * Aggiungi un bit di modello sulla nostra pagina * @param String $ tagga il tag dove inseriamo il modello, ad es. hello * @param String $ bit il bit del template (percorso al file, o solo il nome del file) * @return void * / public function addTemplateBit ($ tag, $ bit) if (strpos ($ bit, 'skin /' ) === false) $ bit = 'skin /'. PCARegistry :: getSetting ('skin'). '/ templates /'. $ Bit; $ this-> page-> addTemplateBit ($ tag, $ bit); / ** * Inserisci i bit del template nel nostro contenuto della pagina * Aggiorna il contenuto delle pagine * @return void * / private function replaceBits () $ bits = $ this-> page-> getBits (); foreach ($ bit as $ tag => $ template) $ templateContent = file_get_contents ($ bit); $ newContent = str_replace (''. $ tag. '', $ templateContent, $ this-> page-> getContent ()); $ this-> page-> setContent ($ newContent); / ** * Sostituisci i tag nella nostra pagina con contenuto * @return void * / private function replaceTags () // ottiene i tag $ tags = $ this-> page-> getTags (); // passa attraverso tutti foreach ($ tag come $ tag => $ data) if (is_array ($ data)) if ($ data [0] == 'SQL') // è una query memorizzata nella cache ... sostituisci i tag DB $ this-> replaceDBTags ($ tag, $ data [1]); elseif ($ data [0] == 'DATA') // è un dato memorizzato nella cache ... sostituisce i tag di dati $ this-> replaceDataTags ($ tag, $ data [1]); else // sostituisce il contenuto $ newContent = str_replace (''. $ tag. '', $ data, $ this-> page-> getContent ()); // aggiorna il contenuto delle pagine $ this-> page-> setContent ($ newContent); / ** * Sostituisci il contenuto della pagina con i dati del DB * @param String $ tag il tag che definisce l'area del contenuto * @param int $ cacheId l'ID della query nella cache della query * @return void * / private function replaceDBTags ($ tag, $ cacheId) $ block = "; $ blockOld = $ this-> page-> getBlock ($ tag); // record foreach relativo alla query ... while ($ tags = PCARegistry :: getObject ( 'db') -> resultsFromCache ($ cacheId)) $ blockNew = $ blockOld; // crea un nuovo blocco di contenuto con i risultati sostituiti in foreach ($ tag come $ ntag => $ data) $ blockNew = str_replace ("". $ ntag. "", $ data, $ blockNew); $ block. = $ blockNew; $ pageContent = $ this-> page-> getContent (); // rimuove il separatore nel modello , cleaner HTML $ newContent = str_replace (''. $ blockOld. '', $ block, $ pageContent); // aggiorna il contenuto della pagina $ this-> page-> setContent ($ newContent); / ** * Sostituisci il contenuto della pagina con i dati della cache * @param String $ tag il tag che definisce l'area del contenuto * @param int $ cacheId l'ID dei dati nella cache di dati * @return void * / private function replaceDataTags ($ tag, $ cacheId) $ block = $ this-> page-> getBlock ($ tag); $ blockOld = $ block; while ($ tags = PCARegistry :: getObject ('db') -> dataFromCache ($ cacheId)) foreach ($ tag come $ tag => $ data) $ blockNew = $ blockOld; $ blockNew = str_replace ("". $ tag. "", $ data, $ blockNew); $ block. = $ blockNew; $ pageContent = $ this-> page-> getContent (); $ newContent = str_replace ($ blockOld, $ block, $ pageContent); $ this-> page-> setContent ($ newContent); / ** * Ottieni l'oggetto di pagina * @return Object * / public function getPage () return $ this-> page; / ** * Imposta il contenuto della pagina in base a un numero di modelli * passa le posizioni dei file modello come argomenti individuali * @return void * / public function buildFromTemplates () $ bits = func_get_args (); $ content = ""; foreach ($ bit come $ bit) if (strpos ($ bit, 'skin /') === false) $ bit = 'skin /'. PCARegistry :: getSetting ('skin'). '/ templates /'. $ Bit; if (file_exists ($ bit) == true) $ content. = file_get_contents ($ bit); $ this-> page-> setContent ($ content); / ** * Convertire un array di dati (ad esempio una riga db?) In alcuni tag * @param array i dati * @param string un prefisso che viene aggiunto al nome del campo per creare il nome del tag * @return void * / public function dataToTags ($ data, $ prefix) foreach ($ data as $ key => $ content) $ this-> page-> addTag ($ key. $ prefix, $ content); public function parseTitle () $ newContent = str_replace ('',' '. $ page-> getTitle (), $ this-> page-> getContent ()); $ this-> page-> setContent ($ newContent); / ** * Analizza l'oggetto della pagina in qualche output * @return void * / public function parseOutput () $ this-> replaceBits (); $ This-> replaceTags (); $ This-> parseTitle (); ?>
Quindi, cosa fa esattamente questa classe?
Crea il nostro oggetto pagina e lo basa da file modello, l'oggetto pagina contiene il contenuto e le informazioni necessari per creare l'HTML della pagina. Abbiamo quindi buildFromTemplate ('templatefile.tpl.php', 'templatefile2.tpl.php') per ottenere il contenuto iniziale per la nostra pagina, questo metodo prende qualsiasi numero di file modello come argomenti e li unisce in ordine, utile per modelli di intestazioni, contenuti e piè di pagina.
Gestisce il contenuto associato alla pagina aiutando l'oggetto pagina a mantenere un record di dati da sostituire nella pagina e anche i bit di template aggiuntivi che devono essere incorporati nella pagina (addTemplateBit ('userbar', 'usertoolsbar.tpl.php')).
Aggiunge dati e contenuti alla pagina eseguendo varie operazioni di sostituzione sul contenuto della pagina, incluso il recupero dei risultati da una query memorizzata nella cache e l'aggiunta alla pagina.
Il file modello deve contrassegnare al suo interno dove è necessario recuperare una query memorizzata nella cache e sostituire i dati dalla query. Quando il gestore di template incontra un tag per sostituire una query, ottiene il blocco della pagina in cui deve scorrere iterando chiamando getBlock ('block') sull'oggetto di pagina. Questa porzione di contenuto viene quindi copiata per ogni record nella query e i tag all'interno di esso vengono sostituiti con i risultati della query. Daremo un'occhiata a come appare nel modello più avanti in questo tutorial.
L'oggetto pagina è gestito dal gestore modelli e utilizzato per contenere tutti i dettagli relativi alla pagina. Ciò lascia il gestore di modelli libero di gestire, rendendo più facile per noi estendere la funzionalità di questo in un secondo momento.
titolo; public function setPassword ($ password) $ this-> password = $ password; public function setTitle ($ title) $ this-> title = $ title; funzione pubblica setContent ($ content) $ this-> content = $ content; addTag funzione pubblica ($ key, $ data) $ this-> tags [$ key] = $ data; public function getTags () return $ this-> tags; public function addPPTag ($ key, $ data) $ this-> postParseTags [$ key] = $ data; / ** * Ottieni i tag da analizzare dopo che il primo batch è stato analizzato * @return array * / public function getPPTags () return $ this-> postParseTags; / ** * Aggiungi un template bit alla pagina, in realtà non aggiunge ancora il contenuto * @param String il tag in cui è stato aggiunto il template * @param String il nome del file template * @return void * / public function addTemplateBit ($ tag, $ bit) $ this-> bits [$ tag] = $ bit; / ** * Ottieni i bit del template da inserire nella pagina * @return array la matrice di tag template e nomi di file template * / funzione pubblica getBits () return $ this-> bits; / ** * Ottiene un blocco del contenuto della pagina * @param Stringa il tag che avvolge il blocco ( bloccare ) * @return String il blocco del contenuto * / public function getBlock ($ tag) preg_match ('#(. +?)#si ', $ this-> content, $ tor); $ tor = str_replace ('', "", $ tor [0]); $ tor = str_replace ('', "", $ tor); ritorno $ tor; funzione pubblica getContent () return $ this-> content; ?>
In che modo questa classe può essere estesa e migliorata?
Ora che abbiamo alcuni oggetti che il nostro registro sta per memorizzare, dobbiamo dire al registro quali oggetti sono. Ho creato un metodo nell'oggetto PCARegistry chiamato loadCoreObjects che (come si dice) carica gli oggetti core. Ciò significa che può solo chiamare questo dal nostro file index.php per caricare il registro con questi oggetti.
funzione pubblica storeCoreObjects () $ this-> storeObject ('database', 'db'); $ this-> storeObject ('template', 'template');
Questo metodo può essere modificato in seguito per incorporare gli altri oggetti core che il registro dovrebbe caricare, ovviamente ci possono essere oggetti che vogliamo che il nostro registro gestisca, ma solo a seconda dell'applicazione per cui è usato il framework. Questi oggetti verrebbero caricati al di fuori di questo metodo.
In modo che possiamo dimostrare le nuove funzionalità aggiunte al nostro framework, abbiamo bisogno di un database per utilizzare il gestore del database e alcune delle funzioni di gestione dei modelli (in cui sostituiamo un blocco di contenuto con le righe nel database).
Il sito dimostrativo che realizzeremo con il nostro framework entro la fine di questa serie di tutorial è un sito Web con una directory dei membri, quindi creiamo una tabella di database molto semplice per i profili dei membri, contenente un ID, un nome e un indirizzo email.
Ovviamente, abbiamo bisogno di alcune righe di dati in questa tabella!
Per poter visualizzare qualsiasi cosa, abbiamo bisogno di un modello di base, in cui elencheremo i dati dalla nostra tabella dei membri.
Alimentato da PCA Framework I nostri membri
Di seguito è riportato un elenco dei nostri membri:
I commenti HTML dei membri START e END indicano il blocco dei membri (ottenuto tramite il metodo getBlock () nella pagina), questo è il punto in cui il gestore dei template esegue iterazioni attraverso i record nel database e li visualizza.
Ora, dobbiamo riunire tutto questo, con il nostro file index.php:
// richiede il nostro registro require_once ('PCARegistry / pcaregistry.class.php'); $ registry = PCARegistry :: singleton (); // memorizza quegli oggetti core $ registry-> storeCoreObjects (); // crea una connessione al database $ registry-> getObject ('db') -> newConnection ('localhost', 'root', ", 'pcaframework'); // imposta le impostazioni di default della skin (le memorizzeremo nel database più tardi ...) $ registry-> storeSetting ('default', 'skin'); // popola il nostro oggetto page da un file template $ registry-> getObject ('template') -> buildFromTemplates ('main.tpl.php') ; // memorizza nella cache una query della tabella dei membri $ cache = $ registry-> getObject ('db') -> cacheQuery ('SELECT * FROM members'); // assegna questo ai membri tag $ registry-> getObject (' template ') -> getPage () -> addTag (' members ', array (' SQL ', $ cache)); // imposta il titolo della pagina $ registry-> getObject (' template ') -> getPage () -> setTitle ('Our members'); // analizza tutto e sputalo $ registry-> getObject ('template') -> parseOutput (); stampa $ registry-> getObject ('template') -> getPage () -> getContent ();
Se ora visualizziamo questa pagina nel nostro browser Web, i risultati della query vengono visualizzati nella pagina:
Nella terza parte faremo una leggera deviazione dal lato dello sviluppo del nostro Framework e guardiamo a come progettare tenendo presente il nostro framework e come suddividere i template HTML in modo che siano adatti al nostro framework. Quando iniziamo a costruire la nostra prima applicazione con il nostro framework, esamineremo più in dettaglio alcuni dei meccanismi di queste classi. Infine, grazie per i tuoi commenti l'ultima volta!