Questo articolo fa parte di una serie dedicata alla programmazione di un framework MVC personalizzato in PHP. L'autore ha chiamato questo framework "Taste". Gli altri articoli della serie sono disponibili nella categoria Taste framework di php.html.it.
Piano piano siamo giunti alla fine della serie dedicata allo studio e all'implementazione di un framework MVC in PHP 5. Abbiamo toccato in modo dettagliato i punti relativi al core, ai template e ai controller, lasciando alla teoria solamente i modelli e la persistenza che risultano concetti troppo complessi a livello implementativo per essere discussi in questa sede.
Con l'articolo di oggi si chiude per l'appunto la serie; ci occuperemo di riassumere i concetti trattati discutendo di come preparare Taste per essere praticamente utilizzato per la produzione di applicazioni di esempio sulla base di una versione corretta e bug free (ringrazio alcuni utenti di HTML.it per avermi segnalato gli errori) che potete scaricare come sempre dal link download in alto.
Preparare ed installare Taste
Molti di voi mi hanno chiesto dove posizionare i file che rappresentano i controller, i template ed i modelli in modo che risultino accessibili dalle funzionalità del framework. La configurazione standard del framework MVC (come impostata nel file static/debug.conf
che potete trovare nell'archivio allegato scaricabile dal link download) indica al framework di ricercare i controller all'interno della cartella controllers
, e i template all'interno della cartella views
. Nel caso in cui i path indicati siano relativi, viene utilizzata come directory di base quella in cui è contenuta la root del framework taste.
I file che andremo a scrivere dovranno quindi essere installati in queste cartelle, e saranno accessibili utilizzando due sistemi differenti:
- per accedere al template tramite le classi dedicate a questo compito ci basterà specificare il path relativo rispetto alla root dei template ove richiesto;
- al controller invece (nonostante sia accessibile comunque utilizzando le normali regole di inclusione dei file) si accede normalmente dall'
ExplicitRouter
che ha pero' bisogno che il controller da eseguire sia specificato con una sintassi particolare: partendo dal path relativo del controller ci basta eliminare l'estensione .php, sostituire gli slash con dei punti ed aggiungere al risultato il nome della classe rappresentante il controller seguita dal nome del metodo da richiamare. Qualche esempio:
path/al/mio/controller.php (classe MioController, metodo azione_di_prova) path.al.mio.controller.MioController.azione_di_prova controller.php (classe MioController, metodo azione_di_prova) controller.MioController.azione_di_prova
Dopo aver compreso questi concetti possiamo tranquillamente installare e configurare il framework. Proviamo a configurarlo in modo che:
- posizioni i file statici ed il file di bootstrap all'interno del webserver, in modo che siano correttamente accessibili;
- posizioni le cartelle dei controller, dei template, dei modelli ed il file di configurazione all'interno di una cartella con il nome dell'applicazione di esempio;
- posizioni i file temporanei all'interno di una cartella separata;
- posizioni il framework in un path accessibile globalmente;
Decomprimiamo l'intera cartella all'interno del filesystem in una locazione facilmente accessibile. Creiamo le cartelle che andranno ad ospitare i nostri dati (presuppongo di star lavorando su Linux):
shell> sudo mkdir /usr/local/taste shell> sudo mkdir /usr/local/taste/framework shell> sudo mkdir /usr/local/taste/applications shell> sudo mkdir /usr/local/taste/applications/test_app shell> sudo mkdir /usr/local/taste/tmp shell> sudo mkdir /var/www/taste/webroot
Ora spostiamo i file contenuti all'interno del file compresso scaricato in modo che:
- la cartella
tmp
risulti vuota; - la cartella
test_app
contenga a sua volta le cartelleviews
,models
,controllers
estatic
. L'ultima cartella conterrà anche il file di configurazione ed i due file statici html; - la webroot contenga il file di
bootstrap
; - ed infine la cartella
framework
contenga tutti gli altri file;
Aggiorniamo correttamente i permessi:
shell> sudo chown -r www-data:www-data /usr/local/taste shell> sudo chown -r www-data:www-data /var/www/taste shell> sudo chmod -R 755 /usr/local/taste shell> sudo chmod -R 777 /var/www/taste shell> sudo chmod -R 777 /usr/local/taste/tmp
Affinché il framework si comporti correttamente abbiamo bisogno di apportare delle modifiche ai file seguenti:
framework/taste.php
require_once '/usr/local/taste/framework/taste/config/Configurable.php'; define('TASTE_DIR', "/usr/local/taste/framework/"); define('APPS_DIR', "/usr/local/taste/applications/"); define('CONFIGURATION_FILE', realpath("/usr/local/taste/applications/test_app/static/debug.conf")); define('DEBUG_MODE', Configurable::queryConfiguration('Server', 'debug', 'off') == 'on');
webroot/bootstrap.php
<?php
ini_set('include_path', ini_get('include_path').':/usr/local/taste/framework:');
// il resto del file rimane invariato
// volendo è possibile impostare l'include_path dal file php.ini
applications/test_app/static/debug.conf
[Server] debug = on max_redirect = 10 dumpQueries = on notFoundUri = notfound [ExplicitRouter] controllersDirs = /usr/local/taste/applications/test_app/controllers [CachedRouter] cacheDir = /usr/local/taste/tmp [Database] host = localhost name = taste username = root [Template] templateDirs = /usr/local/taste/applications/test_app/views/ nullAsBlank = on invalidAsBlank = on [CacheApplication] cacheDir = /usr/local/taste/tmp timeout = 0
Ora il framework dovrebbe essere correttamente installato e configurato e possiamo procedere con l'implementazione di un controller e di un template di esempio per assicurarci del corretto funzionamento.
L'applicazione di prova
Per implementare un'applicazione di prova dobbiamo necessariamente svolgere alcune operazioni operazioni:
- in primo luogo è necessario creare una cartella all'interno di
applications
che rappresenti la root dei file della nostra applicazione (nel nostro caso sarà test_app); - in secondo luogo, se necessario, dobbiamo implementare la classe che servirà per usufruire della nostra applicazione. Nel nostro caso (e probabilmente anche in molti altri) possiamo fare affidamento ad MVCApplication;
- successivamente dobbiamo modificare le opzioni di routing in modo che le richieste vengano interpretate correttamente dal router;
- infine dobbiamo implementare i template ed i controller necessari;
Partiamo subito dal terzo punto tralasciando i primi due. La nostra applicazione si occuperà semplicemente di visualizzare una lista di dati contenuti in un file XML, filtrandoli ed ordinandoli eventualmente in base a determinati parametri. Le richieste che abbiamo la necessità di tracciare sono le seguenti (con il dollaro indico le parti che fungeranno da attributi alle nostre azioni):
userlist/list userlist/sort/$FIELD
Com'è facilmente comprensibile il primo URL visualizza tutti i valori, il secondo li visualizza ordinati per un determinato campo. Eventualmente potremmo aggiungere altre opzioni più avanti. Il router dovrà quindi accettare i seguenti parametri:
return array( "^userlist/show/?$" => "userlist.UserList.show", "^userlist/sort/([^/]+)/?$" => "userlist.UserList.sort", );
Che per comodità salveremo in /usr/local/taste/applications/test_app/route.php
e successivamente includeremo all'interno del file di bootstrap. Il CachedRouter
è stato disabilitato poiché in fase di debugging sarebbe necessario rimuovere il file di cache ogni volta che si modificano le opzioni di routing:
ini_set('include_path', ini_get('include_path').':/usr/local/taste/framework:'); define('APP_NAME', 'test_app'); require_once 'taste.php'; require_once 'taste/Server.php'; require_once 'taste/mvc/routing/ExplicitRouter.php'; //require_once 'taste/mvc/routing/CachedRouter.php'; require_once 'taste/applications/MVCApplication.php'; require_once 'taste/applications/CacheApplication.php'; $router = new ExplicitRouter( require_once(APPS_DIR."test_app/route.php") ); //$router = new CachedRouter($router); $server = new Server( new CacheApplication( new MVCApplication($router))); $server->run();
Creiamo ora il controller che salveremo all'interno del file controllers/userlist.php
:
<?php require_once 'taste/Request.php'; require_once 'taste/mvc/Controller.php'; require_once 'taste/mvc/template/Template.php'; define("XML_CONTENT", '<?xml version="1.0" encoding="UTF-8"?> <userlist> <user> <name>Gabriele</name> <surname>Farina</surname> </user> <user> <name>Francesco</name> <surname>Caccavella</surname> </user> <user> <name>Cesare</name> <surname>Lamanna</surname> </user> </userlist> '); class UserList extends Controller { private $sortField; public function show(Request $request) { $users = $this->loadUsers(); return $this->render($users, 'nessuno'); } public function sort(Request $request, $field) { $users = $this->loadUsers(); $this->sortField = $field; usort($users, array($this, 'sortByField')); return $this->render($users, $field); } private function render($users, $field) { return Template::renderToResponse('userlist.tpl', array( 'users' => $users, 'field' => $field )); } private function sortByField($a, $b) { return strcmp($a[$this->sortField], $b[$this->sortField]); } private function loadUsers() { $file = 'userlist.xml'; if(!file_exists($file)) { $fp = fopen($file, 'w+'); fwrite($fp, XML_CONTENT); fclose($fp); } $users = array(); $doc = DOMDocument::load($file); foreach($doc->getElementsByTagName('user') as $node) { $users[] = array( 'name' => $node->getElementsByTagName('name')->item(0)->childNodes->item(0)->nodeValue, 'surname' => $node->getElementsByTagName('surname')->item(0)->childNodes->item(0)->nodeValue ); } return $users; } } ?>
Il controller è veramente semplice e si occupa semplicemente di salvare in un array associativo gli utenti, ordinandoli eventualmente in base al campo sortField
nel caso in cui venga richiamata l'azione sort.
Infine l'ultimo tocco è creare il template, che salveremo in views/userlist.tpl
:
<?xml version="1.0" encoding="utf-8" ?> <html xmlns:tpl="http://taste.alittleb.it"> <head> <title>User List</title> </head> <body> <h3>Utenti ordinati per: $field</h3> <ul> <li> <a href="bootstrap.php?url=userlist/show">Nessuno</a> </li> <li> <a href="bootstrap.php?url=userlist/sort/name">Nome</a> </li> <li> <a href="bootstrap.php?url=userlist/sort/surname">Cognome</a> </li> </ul> <div tpl:repeat="users,user">$user.surname $user.name</div> </body> </html>
Per testare lo script ci basta puntare il browser al file di bootstrap, passando come parametro url=userlist/show
.
Conclusione
Siamo giunti alla fine della serie. Spero possa essere interessata a qualcuno e abbia stimolato altri di voi nella ricerca di soluzioni sempre più complete per l'implementazione del pattern MVC applicato allo sviluppo web.