Torna a Yii

Yii Reloaded

Spread the love

YiiReloaded

Con questo post voglio rivedere il concetto di MVC secondo un altro paradigma tratto dalla vita reale.

Indice

Vivisezione di index.php

Prendo un’applicazione di esempio, e scelgo un URL con un po’ di parametri dentro, tanto per capire meglio il funzionamento. Se non adopero la classe di riscrittura degli URL, l’applicazione chiama le sue varie componenti in un modo standard che è questo. Partiamo proprio dall’esempio:

  http://localhost/myapp/index.php?r=site/login

e vediamolo un pezzo alla volta:

  • http:// è il protocollo HTTP
  • localhost è il nome dell’host HTTP
  • /myapp è il nome dell’applicazione e, a meno di giochini fatti con Apache (VirtualHost o altre diavolerie), è anche il nome della cartella di filesystem che contiene i file dell’applicazione Yii
  • index.php qui viene la parte interessante: index.php è come uno snodo ferroviario attraverso cui passa tutto il flusso dell’applicazione, o se vogliamo è la centrale comandi, ogni componente dell’applicazione verrà lanciata da qui e gli oggetti da chiamare assieme alle azioni da intraprendere vengono scritte nei parametri dell’URL che contengono la query string, che è l’ultimo componente del nostro l’indirizzo:
  • ?r=site/login: r sta per rule, la regola che da indicazioni alla centrale di comando sul da farsi (il nome rule è scritto in qualsiasi dei file applicazione/protected/main.php all’incirca verso la riga 40, dove c’è l’urlManager); ebbene, al parametro r viene assegnato il valore magico site/login: questa stringa rappresenta convenzionalmente la coppia controller/action da lanciare. In questo caso si deve istanziare il controller site e deve essere eseguito il suo metodo login.

La centrale comandi

Se guardiamo ben bene il codice ce ne rendiamo conto:

  • index.php (la “centrale comandi”) è un brevissimo file che contiene il codice seguente:
 // change the following paths if necessary
 4  $yii=dirname(__FILE__).'/../yii/framework/yii.php';
 5  $config=dirname(__FILE__).'/protected/config/main.php';

 // remove the following lines when in production mode
 8  defined('YII_DEBUG') or define('YII_DEBUG',true);
 // specify how many levels of call stack should be shown in each log message
10  defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL',3);

12  require_once($yii);
13  Yii::createWebApplication($config)->run();

La numerazione delle istruzioni è quella delle righe nel file originale:

  • Le istruzioni 4 e 5 definiscono i path (percorsi di file system) in cui si trovano i file con le definizioni della classe master Yii ed il file dei parametri che configurano la nostra applicazione.
  • Le righe 8 e 10 semplicemente impostano, se non già fatto in precedenza (nota il costrutto defined or define), le costanti per la tracciatura dei log dell’applicazione e le segnalazioni per il debug: quando sviluppiamo un’applicazione è melius abundare quam deficere per farci aiutare sul modo di funzionare di Yii e della nostra applicazione. Quindi sono accessorie. Il bello viene adesso.
  • La riga 12 include nel file che va in esecuzione (ricordiamo che PHP struttura uno script secondo il codice contenuto nel file e degli eventuali file di cui è richiesta l’inclusione, mette tutto insieme e dopo di che produce un codice macchina che va in esecuzione e invia l’output a Apache) il file dichiarato nella riga 1 che è il motore Yii, la classe principale, la classe delle classi, quella che permette di istanziare la centrale comandi.
  • La riga 13 (questa è la più densa di tutte, il buco nero di questo file) istanzia la classe Yii (Yii::) poi chiama suo il metodo createWebApplication passandogli il file di configurazione definito in riga 5 (il main.php): facendo questo la classe Yii istanzia una seconda classe CWebApplication che è l’applicazione web vera e propria (quella indicata con Yii::createWebApplication) e di questa classe viene eseguito il metodo run().

E’ chiaro che la classe CWebApplication fa parte del framework generale, non la dobbiamo scrivere noi: semplicemente la parametrizzeremo ed estenderemo opportunamente con il nostro codice scritto con le nostre manine. E’ un po’ come prendere la scatola con il preparato per “La torta della Nonna” al supermercato e poi personalizzarla secondo i nostri gusti.

L’applicazione Web

Ma adesso siamo curiosi di capire meglio come funziona il tutto.

Quindi torniamo alla riga 6: c’è l’istanza della classe Yii. La classe Yii si trova nel file indicato in riga 1 (il path completo in chiaro è /DOCROOT/yii/framework/yii.php): apriamolo.

 1  require(dirname(__FILE__).'/YiiBase.php');

 2  class Yii extends YiiBase
 3  {
 4  }

Ho tolto tutti i commenti. Il file è tutto qua. Quindi dobbiamo aprire un’altra scatola cinese, perché la classe Yii è una estensione della classe di base YiiBase (Yii è tecnicamente definito come helper che serve tutte le funzionalità comuni del framework, basta leggere il commento che ho rimosso qui per brevità).

Quindi, forza, apriamo il file YiiBase.php e troviamo quanto segue:

 98  public static function createWebApplication($config=null)
 99  {
100     return self::createApplication('CWebApplication',$config);
101  }

Ovviamente mi sono fermato a quello che mi interessa in questo momento, cioè a come viene istanziata la classe. Quando prima ho detto che viene creata una istanza di CWebApplication mi riferivo a questo preciso passaggio qui. YiiBase::createWebApplication è una classe in sé che è proprio la nostra applicazione web a cui passiamo i parametri contenuti nell’array definito nel nostro ormai familiare main.php.

Ultimo passo apriamo il file contenente la dichiarazione della classe CWebApplication (/DOCROOT/yii/framework/web/CWebApplication.php) perché qui scopriamo la chiave di volta dell’architettura: infatti l’ultima cosa che fa la centrale comandi è di lanciare l’applicazione:

 Yii::createWebApplication($config)->run();

run() in realtà è un metodo che CWebApplication eredita dalla madre CApplication (CWebApplication è infatti definita come estensione di CApplication, basta guardare il file di cui sopra alla riga che contiene l’istruzione class:

 60  class CWebApplication extends CApplication

Ecco cosa recita il metodo run() alla riga 154 di framework/base/CApplication.php

 154  public function run()
 155  {
 156    if($this->hasEventHandler('onBeginRequest'))
 157      $this->onBeginRequest(new CEvent($this));
 158 $this->processRequest();
 159    if($this->hasEventHandler('onEndRequest'))
 160      $this->onEndRequest(new CEvent($this));
 161    }

Per semplicità soffermiamoci alla riga 158 che è il cuore pulsante, cioè come viene processata la request (in sostanza la query string index.php?r=site/login). La definizione la possiamo trovare nello stesso file alla riga

 106  abstract public function processRequest();

Questa è, tecnicamente parlando, una interfaccia, cioè la definizione di una funzione la cui implementazione è lasciata alla classe figlia, quindi dobbiamo tornare alla classe CWebApplication (framework/base/CWebApplication.php) perché è lì che processRequest() è definita:

Come creare una applicazione a partire dall’URL

 126  public function processRequest()
 127  {
 128    if(is_array($this->catchAllRequest) && isset($this->catchAllRequest[0]))
 129    {
 130      $route=$this->catchAllRequest[0];
 131      foreach(array_splice($this->catchAllRequest,1) as $name=>$value)
 132        $_GET[$name]=$value;
 133    }
 134    else
 135      $route=$this->getUrlManager()->parseUrl($this->getRequest());
 136    $this->runController($route);
 137  }

Ecco qui: nelle righe dalla 128 alla 135 viene fatto il parsing della query string (l’estrazione dei parametri dalla URL) e infine, alla riga 136, viene lanciato il controller.

Motore: azione!

Se avete avuto la costanza di seguirmi fin qui siamo arrivati al nocciolo della questione. Quando si carica una pagina del sito costruito con il framework Yii, nella sostanza viene lanciato un controller!

Questo è fondamentale.

Significa che tutto ruota attorno al controller. E’ il controller che prende i valori dalla request e li elabora, è il controller che istanzia gli oggetti del modello a seconda della sua specializzazione, è il controller che decide quale view lanciare e quali action intraprendere.

Questo mi fa tanto pensare ad un regista. E qui si innesta una visione un po’ nuova di questo modo di far funzionare le applicazioni. Trovo più intuitivo pensare ad un set cinematografico in cui:

  • il controller è il regista
  • i model che il controller manipola sono gli attori
  • le view che il controller richiama sono i set in cui si svolge l’azione
  • le action sono proprio le action del set cinematografico, i ciak, le scene.

Se proseguiamo con lo studio della nostra applicazione di base, vediamo che il regista prescelto è il controller site e l’azione che egli eseguirà sarà login: e in effetti se andiamo a vedere dentro al controller che troviamo al percorso /myapp/controllers/siteController.php (dentro alla directory /myapp/controllers/ci sono tutti i registi), noteremo che c’è una serie di action tra cui quella che stiamo studiando actionLogin:

 72  	public function actionLogin()

Nel caso più semplice – quello nel quale i parametri vengono inviati classicamente nella request globale (in alternativa alla più sofisticata login via AJAX) – ciò che viene fatto in questa “scena” è:

 74  $model=new LoginForm;
 ...
 83  // collect user input data
 84  if(isset($_POST['LoginForm']))
 85  {
 86  	$model->attributes=$_POST['LoginForm'];
 87  	// validate user input and redirect to the previous page if valid
 88  	if($model->validate() && $model->login())
 89  		$this->redirect(Yii::app()->user->returnUrl);
 90  }
 91  // display the login form
 92  $this->render('login',array('model'=>$model));
  • alla linea 86 vengono assegnati agli attributi del modello (il modello/attore in scena è LoginForm) i parametri _POST (ad esempio, username e password)
  • alla linea 88 vengono interrogati due metodi del model, cioè l’attore interpreta due pezzi del copione: validate (validazione dei dati di input, ad esempio non mettere la password vuota) e login (cerca nel database se c’è uno user corrispondente alla coppia username/password)
  • alla linea 89 il regista passa la palla ad un altro regista per un altro film (ad esempio la home del sito)
  • la linea 92 viene eseguita quando accediamo per la prima volta alla login (prima di immettere le credenziali) e viene costruita la scena in cui si chiama l’attore ($model) a recitare nel set (login).

Quindi invece di model MVC Model/View/Controller mi sembra più intuitivo parlare di ASD Actor/Scene/Director, anzi a dirla nell’ordine logico delle cose Director/Actor/Scene e, per dirla proprio tutta, parlerei di paradigma Director/Actor/Scene/Action (DASA).

The Blues

In questa visione delle cose, ogni applicazione è una piccola Hollywood in cui vengono prodotti molti film; potremmo pensare ad una serie di film coordinati, con storie, registi e cast diversi, si pensi ad esempio alla serie filmografica The Blues ideata da Martin Scorsese nella quale diversi registi (Martin Scorsese, Wim Wenders, Clint Eastwood e altri) raccontano storie di Blues in 7 film diversi (vedi la storia completa [1]). Una applicazione ha molti controller (molti registi), ognuno dei quale segue una scenografia (che è scritta dentro al controller) in cui intervengono degli attori (models) che si muovono in un set (view) e nei quali il regista gira varie scene (“action!” è anche quello che il regista dice ad alta voce quando viene avviata la registrazione!).

Lascia un commento

Your email address will not be published.