Yes, it is

Impressioni sparse provando il framework Yii – partendo dall’esperienza di cakephp

 

La doverosa premessa

Non intendo creare scontri, e nemmeno reali confronti: semplicemente, si tratta delle prime impressioni  sparse –per definizione soggettive- di qualcuno che ha esperienza con una altro framework mvc, cakephp e fa una prova per vedere come si trova con Yii. Framework  più diverso da cake di quanto pensassi.

Per far ciò ho preso una applicazione semplice in cakephp 1.3 (una cosa ad uso interno sviluppata in due / tre giorni, anche per provare l’interessantissimo bakingPlate https://github.com/ProLoser/BakingPlate ) ed ho cercato, più che di rifarla, di fare una cosa simile per gli stessi scopi, partendo dallo stesso database- con Yii 1.1.8. Giusto per vedere il flusso dove mi portava, senza sforzarmi troppo rispetto al codice generato, raccogliendo impressioni in prima persona.
(Si l’applicazioncina si chiama TortaPlanner  [segnalazioni]. Lo so, è orribile, ma non devo mica venderla.)

Quindi non si tratta di un vero e proprio confronto, solo il racconto di una breve esperienza personale;  uno degli scopi è vedere anche quanto l’esperienza con cake aiuta (o complica) l’apprendimento del framework Yii, per il quale sono principiante assoluto, approcciandolo solo con un bagaglio di conoscenza relativa al design pattern mvc.

Requisiti

Requisiti e preferenze personali: per le mie esigenze attuali non ho particolare attenzione per le performances (che forse non sono così diverse tra applicazioni scritte con criterio indipendentemente dal framework), di più per la curva d’apprendimento, la rapidità di sviluppo, il supporto della comunità, la comodità degli elementi già pronti, l’implementazione di strumenti che semplifichino la programmazione lato client e le funzioni ajax, la solidità del framework da usare con fiducia più o meno in ogni situazione, oltre a difficilmente misurabili questioni di “feeling”.
Questi elementi sono gli stessi che mi hanno fatto scegliere di provare Yii, nell’ottica di aggiungere qualche freccia al mio arco, rispetto ad altri framework. Oltre all’immancabile Zend Framework, infatti, ci sono ormai tanti framework basati su idee originali, con approcci particolari, o caratteristiche accattivanti per contesti particolari.

 

Prime Impressioni

  • Installazione framework / creazione scheletro app:  ottimo, semplice, veloce, niente configurazioni particolari, basta seguire la documentazione. La mia prima impressione  (da verificare!) è che sia un po’ più semplice di cake installarlo anche in contesti particolari come shared hosting con relativi limiti, sottocartelle di webroot..
  • scaffolding / code generation
    • gii, il generatore web based, si presenta veramente bene. Ha molte funzioni : genera modello, controller, CRUD (controller e viste per le azioni comuni), form (input da utente per un modello, a differenza del modello “dati” che implementa ActiveRecord.. ci torneremo), moduli (più o meno i plugin). La generazione non è interattiva, come con cake bake, ma genera tutto desumendo da quel che è disponibile (es. nei modelli, le regole di validazione dipendono dalle definizioni dei singoli campi della tabella del database relativa).
    • Modelli: non rileva le relazioni per convenzione come in cake, ma solo se definite nel database. Quindi, niente relazioni ad esempio in mySql con motore myIsam, bisogna usare innoDb e definire le chiavi esterne.  Non è difficile aggiungere le relazioni a mano nel modello, ma cake su questo mi ha decisamente viziato. Così come mi ha viziato in generale nell’”automagic” dei modelli ed in molte altre cose relative agli automatismi derivanti dal seguire le convenzioni a partire dal database.
    • CRUD – viene generato il controller, le viste per le 4 azioni crud, admin e alcuni elementi (“partial”) riutilizzati nelle viste (_form, _view, _search) così ad esempio sia nella vista create.php che in quella update.php c’è il richiamo a _form:
      <?php echo $this->renderPartial(‘_form’, array(‘model’=>$model)); ?>

 


Passando al codice ed alla struttura..

I Modelli (e i controller)

Regole di validazione: sono generalizzati gli scenari (ovvero si possono definire come in cake regole valide sempre, on update, on create, ma anche in altri contesti, ad esempio di predefinito c’è “search”. Insomma, è semplice definire regole valide solo per il form di ricerca, oltre che per le operazioni di modifica)

In generale è più “libero” rispetto a cake.. e questo ha i suoi pro e contro. Meno automatismi, più controllo, ma anche più complessità. Già i modelli possono essere di vari tipi (a seconda se ereditino da CActiveRecord, da Form, o altre classi personalizzate).

Approfondisco un po’ sulle query perché è un aspetto molto importante.
In Cakephp c’è 1 astraction layer dal database molto chiaro e semplice, 1 tipo di modello e 1 modo di fare le query. Di default una chiamata a $Model->find() effetua una query su Model  e i modelli legati da relazione belongsTo, e query successive, per i modelli correlati (fino al livello impostato col parametro “recursive”), e restituisce il tutto in un bell’array. Oltre a recursive c’è l’ottimo behavior “containable” che permette di personalizzare molto le query successive sugli oggetti correlati.
Con plugin di terze parti, come “linkable”, è possibile effettuare invece query complesse che concatenano tutto (usando join), invece di query successive. Questo ha molte implicazioni sia sulle prestazioni che sulla scrittura del codice. Ad esempio, la possibilità di usare condizioni e ordinamento in base a campi di diverse tabelle non direttamente correlate: per farlo con gli strumenti base di cake bisogna usare il parametro ‘join’, che non va d’accordo con “contain”, e porta a scrivere pezzi di codice abbastanza lunghi che fanno rinunciare a parte degli automatismi offerti dal framework.
Insomma, l’approccio base è semplicissimo e funziona splendidamente – ma quando serve qualcosa di più particolare, che richieda il join di molte tabelle e condizioni composite, la sensazione è di minore naturalezza.
Yii ha un approccio diverso:  molto semplice e potente una volta appreso, con più alternative e quindi (mia impressione) di primo acchito più complessa di quella di cake, ma forse più flessibile.
L’esempio più semplice possibile (con lazy loading, query successive):

 

  1. // retrieve the post whose ID is 10
  2.  
  3. $post=Post::model()->findByPk(10);
  4.  
  5. // retrieve the post's author: a relational query will be performed here
  6.  
  7. $author=$post->author;

 

Oppure (eager loading, join in un’unica query):

  1. $posts=Post::model()->with('author')->findAll();

Molto semplice (model è un metodo statico ereditato da CActiveRecord), ma non è tutto qui..

Vedere: http://www.yiiframework.com/doc/guide/1.1/en/database.overview

Una delle cose che mi ha spaesato inizialmente è, rispetto a cake, la varietà di metodi per accedere ai dati e manipolarli.

Ad esempio, vedere i metodi per mostrare 1 record, una lista di record e una lista di record come admin:

 

  1. class CategoriaController extends Controller
  2.  
  3. {
  4.  
  5. /* varia roba..*/
  6.  
  7. /**
  8.  
  9.        * Displays a particular model.
  10.  
  11.        * @param integer $id the ID of the model to be displayed
  12.  
  13.        */
  14.  
  15.       public function actionView($id)
  16.  
  17.       {
  18.  
  19.             $this->render('view',array(
  20.  
  21.                   'model'=>$this->loadModel($id),
  22.  
  23.             ));
  24.  
  25. }
  26.  
  27. /**
  28.  
  29.        * Lists all models.
  30.  
  31.        */
  32.  
  33.       public function actionIndex()
  34.  
  35.       {
  36.  
  37.             $dataProvider=new CActiveDataProvider('Categoria');
  38.  
  39.             $this->render('index',array(
  40.  
  41.                   'dataProvider'=>$dataProvider,
  42.  
  43.             ));
  44.  
  45.       }
  46.  
  47.       /**
  48.  
  49.        * Manages all models.
  50.  
  51.        */
  52.  
  53.       public function actionAdmin()
  54.  
  55.       {
  56.  
  57.             $model=new Categoria('search');
  58.  
  59.             $model->unsetAttributes();  // clear any default values
  60.  
  61.             if(isset($_GET['Categoria']))
  62.  
  63.                   $model->attributes=$_GET['Categoria'];
  64.  
  65.             $this->render('admin',array(
  66.  
  67.                   'model'=>$model,
  68.  
  69.             ));
  70.  
  71.       }
  72.  
  73.       /**
  74.  
  75.        * Returns the data model based on the primary key given in the GET variable.
  76.  
  77.        * If the data model is not found, an HTTP exception will be raised.
  78.  
  79.        * @param integer the ID of the model to be loaded
  80.  
  81.        */
  82.  
  83.       public function loadModel($id)
  84.  
  85.       {
  86.  
  87.             $model=Categoria::model()->findByPk($id);
  88.  
  89.             if($model===null)
  90.  
  91.                   throw new CHttpException(404,'The requested page does not exist.');
  92.  
  93.             return $model;
  94.  
  95.       }

Vedere in particolare l’implementazione di ActiveRecord:

http://www.yiiframework.com/doc/guide/1.1/en/database.ar

Potente e flessibile (molto interessanti i named scopes, tra le altre cose..), ma ancora non so se mi piace di più, abituato alla linearità e facilità di cakephp. Banalmente, dettagli come il dover scrivere i parametri delle condizioni nelle query con dei placeholder..  è una cosa piccola, ma che mi sembra una prolissità inutile:

  1. $criteria=new CDbCriteria;
  2.  
  3. $criteria->select='title';  // only select the 'title' column
  4.  
  5. $criteria->condition='postID=:postID';
  6.  
  7. $criteria->params=array(':postID'=>10);
  8.  
  9. $post=Post::model()->find($criteria);

 

Altra impressione abbastanza generale, come si vede anche dal codice qui sopra, è che Yii sia un po’ più “orientato agli oggetti”, anche solo per il numero delle classi; mentre cake offre qualche compromesso in più per facilitare gli atuomatismi e l’approccio convention over configuration (come l’uso estensivo degli array invece degli oggetti per la manipolazione dei dati e i risultati delle query).

I controller sono forse la cosa che più è simile nei due framework.

 

 

Arrivando alle Viste

Rispetto a cake..  ci ho messo più tempo a capire come fare un semplice dropdown per selezionare valori dell’oggetto correlato che per aggiungere un elemento di interfaccia con jQuery.
La cosa semplice (che in cake, rispettando le convenzioni e il form helper, è automatica – già il codice generato lo fa): come mettere il classico select (il menu a tendina standard html) per i valori di un oggetto correlato con relazione belongs to.

Dropdown list dell’oggetto correlato (es. Employee BelongsTo Department)

Da (codice generato):

  1. <?php echo $form->textField($model,'departmentId'); ?>

 

A:

 

  1. <?php echo $form->dropDownList($model,'departmentId', CHtml::listData(Department::model()->findAll(), 'id', 'name')); ?>

L’elemento del form è riempito da helper formattatore di lista che chiama direttamente la query del modello (correlato, ma potrebbe anche non esserlo)

Qualcosa che è in contrasto col modello mvc più rigido come implementato in cake (i dati alla vista li da il controller), ma da anche maggiore flessibilità.. De gustibus. Ci sono vantaggi e rischi in entrambi gli approcci.

 

Non so, se avessi avuto un database con motore appropriato e relazioni definite, se Yii avrebbe generato direttamente la seconda versione (col menu a tendina per i valori del modello correlato).

Al contrario.. per trasformare un textfield in un calendarietto (datepicker jqueryUi) non ci ho messo che un minuto, compresa la ricerca di come si fa nella documentazione.

Eureka! Qui Yii brilla proprio:

 

  1. $this->widget('zii.widgets.jui.CJuiDatePicker', array(
  2.  
  3. 'name'=>'Appuntamento[data_inizio]')
  4. );

 

I widget come questo incapsulano funzionalità anche molto complesse, che semplificano soprattutto la parte di programmazione lato client ed ajax. Zii è una serie di widget “ufficiali” già inclusi nel framework. Oltre a jQuery UI ci sono altre cose che implementano funzioni comuni (grid ed altro).
Insomma, niente che non si possa fare – a volte anche abbastanza facilmente- direttamente con jQuery o altro, ma certo avere un set di elementi di interfaccia avanzati già pronto ed utilizzabile con una sola chiamata ad una funzione.. è decisamente comodo.
Soprattutto trattandosi di un framework php, quindi presumibilmente utilizzato da programmatori php e non javascript..
Con cake gli helper relativi non sono male (almeno dalla versione 1.3) e sono strumenti abbastanza “programmabili” (da php); ma di solito, anche semplicemente perché almeno qualche elemento di jQuery UI lo aggiungo sempre, finisco per usare direttamente jQuery saltando gli helper.

Altro widget:

Color picker: avevo deciso di permettere l’uso di un codice colore per le categorie; mentre nell’implementazione precedente ho cercato un componente javascript (un plugin jQuery) che mi ha dato una buona soluzione (ma non ottimale), con Yii è bastato usare la ricerca della sezione estensioni del sito ufficiale, scaricare uno dei tre risultati (il più recente), mettere il tutto nell’apposita cartella extensions, e copincollare il codice (solo una chiamata a $this->widget) adattando i valori di due variabili, et voilà, ecco che il mio campo di testo è diventato un colorpicker funzionale.
http://www.yiiframework.com/extension/minicolors-colorpicker

 

Altre segnalazioni sparse:

Collecting tabular input:

http://www.yiiframework.com/doc/guide/1.1/en/form.table

L’Active form..  notare la proprietà ‘enableAjaxValidation’

 

  1. $form=$this->beginWidget('CActiveForm', array(
  2.  
  3.             'id'=>'appuntamento-form',
  4.  
  5.             'enableAjaxValidation'=>true, // Si, tutto qua!
  6.  
  7.       ));

 

Modi per aggiungere funzionalità (e overloading): http://www.yiiframework.com/wiki/44/

(beh, non molto dissimile da cakephp 2)

 

Permette di usare gli array globali (come $_POST, etc.) senza privilegiare un proprio incapsulamento. In generale la mia impressione è che renda più visibili alcune operazioni ordinarie di php, con una astrazione leggermente minore (con l’eccezione dell’interfaccia e dei widget in generale, che incapsulano ad alto livello anche cose complesse).

 

 

Form con 2 model: http://www.yiiframework.com/wiki/19/how-to-use-a-single-form-to-collect-data-for-two-or-more-models/

Abbastanza semplice, ma alquanto meno immediato / automatico che in cake.

 

 

Cosa abituale in cake.. 

Abituato con I comodi array di cake, dovendo manipolare un po’ di dati prima di salvare.. ho modificato la proprietà che pensavo equivalente di  $Model->data in cake.

E quindi..:
Indirect modification of overloaded property Evento::$attributes has no effect

(Infatti “attributes” è impostato tramite magic methods, non è una proprietà accessibile direttamente)

 

  1. $model->attributes=$_POST['Evento']; // $model è istanza di Evento che estende CActiveRecord, da Model
  2.  
  3. $appuntamento->attributes=$_POST['Appuntamento'];
  4.  
  5. $model->attributes['created'] = $model->attributes['modified'] = date('Y-m-d H:i:s'); //WRONG!!
  6.  
  7. $model->attributes['data_inizio'] = $appuntamento->attributes['data_inizio']; //WRONG!!

 

invece:

 

  1. $model->created = $model->modified = date('Y-m-d H:i:s'); //OK
  2.  
  3. $model->data_inizio = $appuntamento->data_inizio; //OK

 

(L’assegnazione a $model->attributes chiama il metodo __set() di CActiveRecord. Questo metodo crea una serie di proprietà corrispondenti ai campi del modello cui assegna i rispettivi valori)

  1. /**
  2. * PHP setter magic method.
  3. * This method is overridden so that AR attributes can be accessed like properties.
  4. * @param string $name property name
  5. * @param mixed $value property value
  6. */
  7. public function __set($name,$value)
  8. {
  9. if($this->setAttribute($name,$value)===false)
  10. {
  11. if(isset($this->getMetaData()->relations[$name]))
  12. $this->_related[$name]=$value;
  13. else
  14. parent::__set($name,$value);
  15. }
  16. }

 

 

Conclusione

Più o meno.. Infatti non ho ancora finito la prova. Per il momento non penso che pubblicherò il codice della versione Yii.. (mentre quello originale si trova già su github)

Comunque, avendo raccolto già un buon numero di impressioni, e non sapendo quando avrò tempo di finire.. meglio pubblicare qualcosa!

In sintesi – scoprendo l’acqua calda: Cakephp ha più automatismi soprattutto nella gestione dei dati, nel modello e nel controller,  e premia il maggior rigore nel seguire il modello mvc e le convenzioni.

Yii è più libero, offre modi diversi di fare le cose (vedere in particolare il layer dei dati, modelli, active record, i diversi modi di fare le query) che portano a maggiore complessità e flessibilità, ed una curva di apprendimento a mio avviso un po’ più ripida a livello di business logic; d’altra parte l’impressione è di grande agio su altre cose – i piccoli dettagli già pronti, come il contact form con tanto di captcha (anche se non tutti lo ritengono un fatto positivo..), elementi basici di autenticazione ed autorizzazione, ed un template abbastanza modulare già nel codice generato da gii.

Per non parlare della parte lato client, con i widget (quelli ufficiali chiamati “zii” e quelli di terze parti) che forniscono molti elementi di interfaccia già pronti da usare (senza neanche dover includere jQuery/jQuery Ui).  Insomma, se col resto ho molte valutazioni da fare .. con il sistema di extensions / widget è stato amore a prima vista.

Insomma, alla fine, passerò a Yii, lo lascerò andare come momentaneo flirt, oppure cercherò di approfondire e lo userò in alternativa a cake a seconda del progetto? Solo il tempo potrà dirlo. Spero l’ultima – certo mi sembra poco adatto come amante saltuaria, piuttosto una compagna di una relazione duratura, che richiede un certo investimento ma da in cambio grandi soddisfazioni (la  curva d’apprendimento non mi sembra immediata, ma molto meno impegnativa di tanti altri framework e tutto il contesto sembra fatto apposta per creare un ambiente accogliente per lo sviluppatore). Intanto spero che le mie impressioni (prese con le dovute cautele) possano essere utili a qualcuno.

E se ho preso delle cantonate oppure conosci bene un aspetto che ho solo accennato e vuoi precisare, correggere, suggerire.. i commenti sono aperti anche per questo!