Cakephp, code insight anche nelle viste in zend studio e altre IDE

Il contesto

Una buona IDE è molto utile.

Tra le varie preziose funzioni, apprezzo alquanto i suggerimenti nella scrittura del codice: con framework ampi come cakephp, ed usando codice di terze parti, il suggerimento di funzioni, metodi, proprietà, chiamate statiche, è estremamente comodo e fa risparmiare un sacco di tempo.

Il problema è che con framework complessi come cake, non esistendo editor / ide specifici, resta costantemente la necessità di a) scartabellare spesso sulle API o nel cookbook per andarsi a cercare la firma di una certa funzione, o quali parametri ci sono ed a cosa servono; oppure b) essere estremamente capaci ed avere una memoria mostruosa.
O forse no?

Se anche tu come me non rientri nel caso b, continua a leggere.

Il problema

Dunque: normalmente, qualsiasi ide analizza l’ereditarietà; per cui, se
class MioModello extends AppModel
l’ide saprà suggerirci, all’interno di MioModello, proprietà e metodi di AppModel, e -cosa fondamentale-  se abbiamo incluso la directory cake/libs come libreria nelle proprietà del progetto, anche quelli ereditati dai  progenitori come Model (cosine come find() e save()..).
Quindi, di default, se all’interno di MioModello digito $this->f.. l’ide mi suggerirà, ad esempio,il metodo find con tanto di firma e documentazione; inoltre, tipicamente con un CTRL+click, mi aprirà direttamente il file con la classe Model alla definizione del metodo stesso.
Fin qui tutto normale.

Quel che l’ide non può sapere è che nella classe mieiModelliController, grazie alle convenzioni di cake, c’è automaticamente una proprietà MioModello che è l’oggetto omonimo, e che è sufficiente dichiarare la proprietà
$components = array(‘Auth’,’Session’, ‘Email’)
per chiamare implicitamente gli oggetti omonimi e attribuirli ad omonime proprietà del controller stesso.

In italiano: l’ide non può sapere che esiste la proprietà $mieiModelliController->Auth e che questa è un’istanza di AuthComponent; per cui se digito $this->Aut.. non mi suggerisce assolutamente nulla. Idem se digito $this->mioModel..

E’ ancora più difficile nelle viste, che non contengono definizioni di classi o altro, ma hanno variabili ed helper che vengono definiti altrove (nel controller) ed un’istanza della classe View di cake.
screenshot di marcgrabanski.com

La situazione

Diverse Ide propongono approcci diversi (che sono anche leggermente  cambiati nel tempo):
phpDesigner 7 prende tutto. Cioè suggerisce qualsiasi cosa possa coincidere con quanto digitiamo, definito in qualsiasi punto del porogetto.
Ad esempio $this->MioModello->del.. misuggerirà il metodo delete() di MioModello, ma anche tutte le funzioni omonime di qualsiasi modello, component, helper.. un po’ troppo.

Netbeans ed eclipse / zend studio si fanno “insegnare” le dipendenze usando i docblocks (es:
/**
* @var $html HtmlHelper
* (e all’interno di una classe)
* @property  mioModello  $mioModello
*/
Ovvero, si fidano di quel che gli diciamo: se scrivo $html mi suggeriranno metodi e proprietà dell’HtmlHelper, se scrivo $this->MioModello quelli del modello (e quelli ereditati su su fino a Model e Object)

Il problema è che bisognerebbe inserire queste dichiarazioni specifiche praticamentein ogni file. Creare un file generico o inserirle nell’AppController o nell’AppModel (da cui le altre classi ereditanto) non sempre funziona – sicuramente non funziona nelle viste(funzionava con netbeans fino prima della versione 6.8, bastava includere un file con tutte i docblock e le dichiarazioni venivano pescate anche nelle viste, ma ora non funziona più)

Non mi soffermo sui passi necessari – includere il core di cake nel progetto, associare i file .ctp a php / html, eccetera.

Su queste, e sulle cose precedenti i dettagli non mancano:
http://mark-story.com/posts/view/code-completion-for-cakephp-in-eclipse
http://bakery.cakephp.org/articles/index/by:ide
http://bakery.cakephp.org/articles/SymenTimmermans/2009/01/21/model-based-code-insight-and-completion-in-netbeans

4 Tutorials On Choose The Best IDE For CakePhp Development

Tante soluzioni funzionanti in buona parte, ma non una soluzione definitiva, sopratutto per le viste.

La (nuova) soluzione

Ringraziamo tutti (euro)Mark: non solo implementa la soluzione più solida (creare delle classi “finte” da cui le altre ereditano metodi e proprietà che ci interessano) ma lo fa automatimente fornendoci un utilissimo script per la console di cake.
Eh? Un passo indietro:

http://www.dereuromark.de/2010/06/28/code-completion-console-script/

Prendere il codice, metterlo in un file in {app}/vendors/shells/cc.php – e aggiungerci in cima un
define(‘LF’, PHP_EOL);

A riga di comando, dalla directory dell’applicazione, chiamare lo script
(su windows)
..\cake\console\cake cc
E il 99% è fatto!
Lo script avrà fatto una scansione della nostra applicazione e nella nostra directory APP troveremo infatti un file code_completion__.php che include le “finte” classi AppModel, AppController ed AppHelper, con tutte le proprietà al posto giusto e l’opportuna inizializzazione.
Queste non vengono mai usate o incluse (la prima riga del file è un bell’ exit()), ma servono, appunto, solo per “ingannare” l’ide con una finta ereditarietà.

Quando dico “tutte le proprietà al posto giusto” intendo:

  • il finto AppModel avrà come proprietà tutti i modelli presenti, compresi quelli di eventuali plugin
  • il finto AppController avrà come proprietà i vari component – quelli core e quelli della nostra applicazione
  • il finto AppHelper avrà come proprietà i vari Helper, core e specifici della nostra applicazione

Questo è sufficiente per ottenere il code suggest su quasi tutto – l’unica cosa che non c’è sono i metodi dei behaviours che arricchiscono i modelli, non è semplice aggiungerli..però funziona ricorsivamente coi modelli nel controller (es. $this->Post->Comment->fi..)

L’unica cosa da verificare è se nelle viste sia necessario un piccolo aiuto. E’ probabile, come accennato prima, che l’ide sia un po’ spaesata dalla mancanza di contesto delle viste, e non sappia come trattare $this..
per chiudere il cerchio è sufficiente aggiungere nella vista stessa un semplice
/** @var $this AppHelper */
Ecco fatto. Per comodità mi son fatto un codetemplate (help+Invio) per inserie la snippet, che a quersto punto è l’unica cosa che va eventualmente ripetuta in tutte le viste in cui vogliamo attivare l’autocompletamento.

Un po’ lungo, eh.. ma è veramente più semplice farlo che spiegarlo.
(provato per ora con zend studio 8, probabilmente è uguale con eclipse pdt e netbeans)