Development

Documentation/it_IT/book/1.0/14-Generators

You must first sign up to be able to contribute.

Capitolo 14 - Generatori

Molte applicazioni sono basate su dati memorizzati in un database, ed offrono interfacce per la loro gestione. Il noioso e ripetitivo lavoro di creazione di tali moduli fornendo alcune funzionalita' basate su Propel. Se il tuo modello e' definito correttamente, symfony potrebbe anche generare un intero sito di amministrazione. Questo capitolo parlera' dei due generatori inclusi in symfony: scaffolding ed administration generator. Quest'ultimo si basa su di un file di configurazione con una sintassi completa, per cui la maggior parte del presente capitolo descrivera' le varie possibilita' dell'administration generator.

Generazione di codice basata sul modello

In un'applicazione web le operazioni di accesso ai dati possono venire catalogate come segue:

  • Creazione di record
  • Selezione di record
  • Modifica di record
  • Eliminazione di record

Tali operazioni sono talmente comuni da avere un acronimo dedicato: CRUD. Ad esempio, in forum la lista degli ultimi post e' una selezione di record, mentre l'inserimento di una risposta corrisposnde ad un'operazione di creazione.

Le azioni di base e le template che implementano le operazioni CRUD per una data tabella vengono ripetute continuamente nelle applicazioni web. In symfony, il layer del modello contiene abbastanza informazioni da consentire la generazione del codice per le operazioni CRUD, cosi' da velocizzare molto lo sviluppo di backend.

Tutti i task di generazione del codice vengono effettuati tramite chiamate alla linea di comando di symfony nella forma seguente:

> symfony TASK_NAME APP_NAME MODULE_NAME CLASS_NAME

I task di generazione del codice sono propel-init-crud, propel-generate-crud, e propel-init-admin.

Scaffolding e Administration

Durante lo sviluppo, la generazione del codice puo' essere utilizzata per due scopi distinti:

  • Lo Scaffolding e' la struttura base (azioni e template) necessaria alle operazioni CRUD su una tabella. Il codice risultante e' minimale, in quanto pensato come linea guida per uno sviluppo ulteriore. E' una base di partenza che deve essere adattata ai tuoi requisiti di logica e presentazione. Gli scaffolding vengono per lo piu' usati durante lo sviluppo, per fornire un accesso web al database, per costruire un prototipo o per iniziare un modulo basato principalmente su una tabella.
  • Una Adminitrstion e' un'interfaccia sofisticata per la manipolazione dei dati, dedicata all'amministrazione di backend. Essa differisce dallo scaffolding perche' il suo codice non e' pensato per essere modificato manualmente. Una Administration puo' essere personalizzata, estesa, od assemblata tramite configurazione ed ereditarieta'. La sua presentazione e' importante, e si avvantaggia di funzionalita' quali ordinamento, paginazione, e filtri. Essa puo' essere creata e gestita come parte del software pronto per il cliente.

La linea di comando di symfony usa la parola crud per riferirsi allo scaffolding, ed admin per le administration.

Inizializzazione o generazione del codice

Symfony offre due metodi per generare il codice: per ereditarieta' (init) o per generazione (generate).

Puoi inizializzare un modulo, che significa creare delle classi vuote che ereditano dal framework. Cio' maschera il codice PHP delle azioni e delle template per evitare che venga modificato. Questo risulta utile se la tua struttura dati non e' ancora completata, o se hai semplicemente bisogno di una veloce interfaccia al db per gestirne i dati. Il codice eseguito run-time non e' situato nella tus applicazione ma nella cache. I task della linea di comando per questo tipo di generazione cominciano con propel-init-.

Il codice delle azioni inizializzate e' vuoto. Ad esempio, un modulo article inizializzato ha azioni del tipo:

class articleActions extends autoarticleActions
{
}

D'altra parte, puoi anche generare il codice delle azioni e delle template, per poi modificarli. Il modulo che ne risulta e' percio' indipendente dalle classi del framework, e non puo' essere modificato tramite file di configurazione. I task della linea di comando per questo tipo di generazione cominciano con propel-generate-.

Dato che gli scaffolding sono pensati per servire come base ad ulteriori sviluppi, spesso sono la generazione per essi risulta essere la scelta migliore. D'altra parte, una administration e' facile da aggiornare tramite una modifica della configurazione, e rimane usabile anche se il modello cambia. Ecco perche' le administration vengono inizializzate.

Esempio di modello dei dati

Durante questo capitolo i listati mostreranno le funzionalita' dei generatori basandosi su un semplice esempio, che ti ricordera' il Capitolo 8. Si tratta del noto esempio di applicazione weblog, contenente le due classi Article e Comment. Il Listato 14-1 ne mostra lo schema, illustrato in Figura 14-1.

Listato 14-1 - schema.yml di un esempio di weblog

propel:
  blog_article:
    _attributes: { phpName: Article }
    id:
    title:       varchar(255)
    content:     longvarchar
    created_at:
  blog_comment:
    _attributes: { phpName: Comment }
    id:
    article_id:
    author:      varchar(255)
    content:     longvarchar
    created_at:

Figura 14-1 - Esempio di modello dei dati

Non c'e' una regola particolare da seguire durante la creazione dello schema per permettere la generazione del codice. Symfony utilizzera' lo schema cosi' com'e' ed interpretera' i suoi attributi per generare uno scaffolding od una administration.

Per recepire il meglio di questo capitolo sarebbe meglio che tu mettessi in pratica gli esempi. Avrai una panoramica migliore del codice generato da symfony e cosa puo' essere fatto con esso se segui passo passo gli esempi dei listati. Per cui sei invitato a creare una struttura dati come quella descritta precedentemente per creare un database con le tabelle blog_article e blog_comment, ed a popolarlo con dati di esempio.

Scaffolding

Lo scaffolding e' di grande utilita' durante i primi giorni di sviluppo. Con un semplice comando, symfony crea un intero modulo basato sulla descrizione di una data tabella.

Generazione di uno scaffolding

Per generare lo scaffolding del modulo article basato sul modello della classe Article digita:

> symfony propel-generate-crud myapp article Article

Symfony legge la definizione della classe Article nello schema.yml e su di essa crea un set di template ed azioni nella cartella myapp/modules/article/.

Il modulo generato contiene tre viste. Navigando sulla pagina http://localhost/myapp_dev.php/article, la vista list, che e' quella di default, visualizza le righe della tabella blog_article come mostrato in Figura 14-2.

Figura 14-2 - Vista list del modulo article

Cliccando sull'id di un articolo viene mostrata la vista show. In pratica i dettagli di un articolo, come in Figura 14-3.

Figura 14-3 - Vista show del modulo article

Sia modificando un articolo cliccando sul link edit, sia creando un nuovo articolo tramite il link create, verrà visualizzata la vista edit, riprodotta in Figura 14-4.

Utilizzando tale modulo puoi sia creare nuovi articoli che modificarne o cancellarne esistenti. Il codice generato è una buona base per sviluppi futuri. Il Listato 14-2 elenca le azioni e le template generate per il nuovo modulo.

Figura 14-4 - Vista edit del modulo Article

Listato 14-2 - Elementi CRUD generati, in myapp/modules/article/

// In actions/actions.class.php
index           // Forward alla seguente azione list
list            // Mostra la  lista di tutti i record della tabella
show            // Mostra la lista di tutte le colonne di un record
edit            // Mostra una form per modificare le colonne di un record
update           // Azione chiamata dalla form dell'azione edit
delete           // Elimina un record
create           // Crea un nuovo record

// In templates/
editSuccess.php  // Form per la modifica di un record (Vista edit)
listSuccess.php  // Lista di tutti i record (Vista list)
showSuccess.php  // Dettagli di un record (Vista show)

La logica di tali azioni e template è abbastanza semplice ed esplicita, per cui invece di spiegarle tutte nei dettagli, il Listato 14-3 mostra giusto una parte della classe generata.

Listato 14-3 - Classe generata, in myapp/modules/article/actions/actions.class.php

class articleActions extends sfActions
{
  public function executeIndex()
  {
    return $this->forward('article', 'list');
  }
 
  public function executeList()
  {
    $this->articles = ArticlePeer::doSelect(new Criteria());
  }
 
  public function executeShow()
  {
    $this->article = ArticlePeer::retrieveByPk($this->getRequestParameter('id'));
    $this->forward404Unless($this->article);
  }
  ...

Per avere un'applicazione di base funzionante basta semplicemente che tu modifichi il codice generato per soddisfare i tuoi requisiti e che ripeta la generazione CRUD per tutte le tabelle con cui vuoi interagire. Generare uno scaffolding velocizza veramente lo sviluppo di un'applicazione: lascia a symfony il lavoro sporco e concentrati sulle specifiche e sulle interfacce.

Inizializzazione di uno scaffolding

L'inizializzazione di uno scaffolding è particolarmente utile quando devi verificare di poter accedere ai dati memorizzati nel database. E' veloce da costruire ed altrettano veloce da eliminare quando sarai sicuro del corretto funzionamento della tua applicazione.

Per inizializzare uno scaffolding Propel che creerà un modulo article che gestisca i record della classe Article, digita:

> symfony propel-init-crud myapp article Article

Potrai quindi accedere alla vista list tramite l'azione di default:

http://localhost/myapp_dev.php/article

Le pagine risultanti sono esattamente le stesse di quelle di uno scaffolding generato. Le puoi usare come una semplice interfaccia web al db.

Se dai un'occhiata all'appena creata actions.class.php del modulo Article, vedrai che essa è vuota: viene tutto ereditato da una classe auto-generata. Lo stesso succede per le template: nella cartella templates/ non troverai alcun file. Il codice delle classi e template inizializzate è lo stesso di quelle generate, ma in questo caso esso risiede nella cache dell'applicazione (myproject/cache/myapp/prod/module/autoArticle/).

Durante lo sviluppo di un'applicazione, gli sviluppatori inizializzano uno scaffolding per interagire con i dati senza pensare all'interfaccia. Il codice non è pensato per essere personalizzato; uno scaffolding inizializzato può essere visto come una semplice alternativa a phpMyAdmin per gestire i dati.

Administration

Per il backend della tua applicazione symfony può generare anche moduli più avanzati, ma sempre bassandosi sul file schema.yml. Puoi creare un intero sito di amministrazione tramite i moduli generati. Gli esempi di questa sezione descriveranno i moduli di amministrazione di un'applicazione di backend. Se il tuo progetto non ha un'applicazione di backend, creane lo scheletro adesso tramite il task init-app:

> symfony init-app backend

I moduli di amministrazione interpretano il modello tramite un file di configurazione particolare chiamato generator.yml, che può essere modificato per estendere i componenti generati ed il look and feel del modulo. Tali moduli beneficiano dei meccanismi descritti nei capitoli precedenti (layout, validation, routing, configurazioni personalizzate, autoloading, e così via). Puoi inoltre fare l'override delle azioni o template generate, per poter integrare nell'amministrazione generate le tue proprie funzionalità, ma generator.yml si dovrebbe occupare dei requisiti più comuni e restringere il codice PHP solo a necessità specifiche.

Inizializzazione di un modulo di amministrazione

In symfony costruisci un'amministrazione per modulo. Esso viene generato tramite un'oggetto Propel utilizzando il task propel-init-admin, che usa una sintassi simile a quella dell'inizializzazione di uno scaffolding:

> symfony propel-init-admin backend article Article

Questa chiamata è sufficiente a creare il modulo article nell'applicazione di backend basato sulla definizione della classe Article, accessibile tramite:

http://localhost/backend.php/article

Il look and feel del modulo generato, illustrato nelle Figure 14-5 e 14-6, è abbastanza sofisticato da essere usato così com'è in un'applicazione commerciale.

Figura 14-5 - Vista list del modulo aticle nell'applicazione di backend

Figura 14-6 - Vista edit del modulo aticle nell'applicazione di backend

La differenza tra l'interfaccia di uno scaffolding ed un'amministrazione potrebbe non essere molto significativa fino ad ora, ma la configurabilità dell'amministrazione ti permetterà di migliorare il layout di base senza scrivere una linea di PHP.

I moduli di amministrazione possono essere esclusivamente inizializzati (non generati)

Un'occhiata al codice generato

Il file del modulo di amministrazione Article, nella cartella apps/backend/modules/article/, è vuoto in quanto inizializzato. Il modo migliore per rivedere il codice generato di questo modulo è visualizzare il sorgente della pagina nel browser, per poi quindi controllare il contenuto della cartella cache/. Il Listato 14-4 elenca le azioni e template generate trovate nella cache.

Listato 14-4 - Elementi dell'amministrazione generata, in cache/backend/ENV/modules/article/

// In actions/actions.class.php
create           // Forward a edit
delete            // Elimina un record
edit             // Mostra una form per modificare i campi di un record e gestire la submit della form
index            // Forward a list
list             // Visualizza la lista di tutti i record di una tabella
save             // Forward a edit

// In templates/
_edit_actions.php
_edit_footer.php
_edit_form.php
_edit_header.php
_edit_messages.php
_filters.php
_list.php
_list_actions.php
_list_footer.php
_list_header.php
_list_messages.php
_list_td_actions.php
_list_td_stacked.php
_list_td_tabular.php
_list_th_stacked.php
_list_th_tabular.php
editSuccess.php
listSuccess.php

Ciò mette in evidenza che un modulo di amministrazione generato è composto essenzialmente di due viste, edit e list. Se dai un'occhiata al codice, lo troverai estremamente modulare, leggibile ed estendibile.

Introduzione al file di configurazione generator.yml

La differenza principale tra scaffolding e amministrazioni (a parte la mancanza dell'azione show per le amministrazioni) è che quest'ultime si basano su parametri del file di configurazione YAML generator.yml. Per vedere la configurazione di default del modulo di amministrazione appena generato, apri il file generator.yml situato nella cartella backend/modules/article/config/generator.yml e riprodotto nel Listato 14-5.

Listato 14-5 - Configurazione di default del generatore, in backend/modules/article/config/generator.yml

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Article
    theme:            default

Ciò è sufficiente per generare un'amministrazione di base. Tutte le personalizzazioni vanno aggiunte sotto la chiave param, dopo la linea theme (ciò significa che tutte le linee aggiunte al file generator.yml devono essere precedute da minimo quattro spazi bianchi, per essere indentate correttamente). Il Listato 14-6 mostra un tipico generator.yml personalizzato.

Listato 14-6 - Configurazione tipica di un generatore completo

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Article
    theme:            default

    fields:
      author_id:      { name: Article author }

    list:
      title:          Lista di tutti gli articoli
      display:        [title, author_id, category_id]
      fields:
        published_on: { params: date_format='dd/MM/yy' }
      layout:         stacked
      params:         |
        %%is_published%%<strong>%%=title%%</strong><br /><em>da %%author%%
        in %%category%% (%%published_on%%)</em><p>%%content_summary%%</p>
      filters:        [title, category_id, author_id, is_published]
      max_per_page:   2

    edit:
      title:          Modifica articolo "%%title%%"
      display:
        "Post":       [title, category_id, content]
        "Workflow":   [author_id, is_published, created_on]
      fields:
        category_id:  { params: disabled=true }
        is_published: { type: plain}
        created_on:   { type: plain, params: date_format='dd/MM/yy' }
        author_id:    { params: size=5 include_custom=>> Scegli un autore << }
        published_on: { credentials:  }
        content:      { params: rich=true tinymce_options=height:150 }

La sezione seguente spiega in dettaglio i parametri che possono essere utilizzati in questo file di configurazione.

Configurazione del generatore

La configurazione del generatore è molto potente, e ti da la possibilità di modificare l'amministrazione generata in diversi modi. Ma tali funzionalità hanno un prezzo: la sintassi è complessivamente lunga e difficile da imparare, rendendo questo capitolo uno dei più lunghi del libro. Il sito di symfony propone una risorsa alternativa che ti aiuterà ad imparare tale sintassi: il reference card, riprodotto in Figura 14-7. Scaricalo da http://www.symfony-project.com/uploads/assets/sfAdminGeneratorRefCard.pdf, e tienilo vicino a te quando leggerai gli esempi seguenti di questo capitolo.

Gli esempi di questa sezione riguarderanno i moduli di amministrazione di article e comment, basati sulla classe Comment. Crea il secondo con il task propel-init-admin:

> symfony propel-init-admin backend comment Comment

Figura 14-7 - La reference card per il generatore di amministrazione

Campi

Per default, i campi delle viste list ed edit sono le colonne definite in schema.yml. Con generator.yml, puoi scegliere quali campi visualizzare, quali nascondere, ed aggiungere campi personalizzati anche se non hanno una corrispondenza nel modello.

Settaggi dei campi

Il generatore crea un field per ogni colonna del file schema.yml. Sotto la chiave fields, puoi modificare il modo in cui il campo è visualizzato, formattato, ecc. Ad esempio, i settaggi del campo mostrati nel Listato 14-7 definiscono per il campo title un nome di classe personalizzato ed una textarea, mentre per il campo content un suggerimento ed un nome personalizato. La sezione seguente descriverà in dettaglio ogni parametro.

Listato 14-7 - Impostare un nome personalizzato per una colonna

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Article
    theme:            default

    fields:
      title:          { name: Article Title, type: textarea_tag, params: class=foo }
      content:        { name: Body, help: Fill in the article body }

In aggiunta a tale definizione di default per tutte le viste, puoi fare l'override delle impostazioni di un campo per una vista specifica (list e edit), come dimostrato dal Listato 14-8.

Listato 14-8 - Override delle impostazioni globali vista per vista

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Article
    theme:            default

    fields:
      title:          { name: Article Title }
      content:        { name: Body }

    list:
      fields:
        title:        { name: Title }

    edit:
      fields:
        content:      { name: Body of the article }

Questo è un principio generale: qualsiasi impostazione per l'intero modulo sotto la chiave fields può venire overridata da una specifica vista (list ed edit) seguente.

Aggiungere campi alla visualizzazione

I campi definiti nella sezione fields possono essere visualizzati, nascosti, ordinati e raggruppati in varie forme per ogni vista. La chiave display viene utilizzata a tale scopo. Ad esempio, per riordinare i campi del modulo comment usa il codice del listato 14-9.

Listato 14-9 - Selezionare i campi da visualizzare, in in modules/comment/config/generator.yml

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Comment
    theme:            default

    fields:
      article_id:     { name: Article }
      created_at:     { name: Published on }
      content:        { name: Body }

    list:
      display:        [id, article_id, content]

    edit:
      display:
        NONE:         [article_id]
        Editable:     [author, content, created_at]

La list mostrerà quindi tre colonne, come in Figura 14-8, e la form di edit presenterà quattro campi, assemblati in due gruppi, come in Figura 14-9.

Figura 14-8 - Selezionare colonne nella vista list del modulo comment

Figura 14-9 - Raggruppare campi nella vista edit del modulo comment

Per cui puoi utilizzare le impostazioni di display in due modi:

  • Per selezionare le colonne da visualizzare e l'ordine nel quale devono apparire, metti i campi in un semplice array, come nella precedente vista list.
  • Per raggruppare campi, usa un array associativo con il nome del gruppo come chiave, oppure NONE per un gruppo senza nome. Il valore è di nuovo un array ordinato di nomi di colonne.

Per default, la chiave primaria non appare in alcuna vista.

Campi personalizzati

In pratica, i campi configurati in generator.yml non hanno bisogno di corrispondere effettivamente alle colonne dello schema. Se la classe relativa offre un getter personalizzato, puè essere usato come campo per la vista list; se c'è un getter e/o un setter, può essere usato per la edit. Ad esempio, puoi estendere il modello di Article con un metodo getNbComments() simile a quello del Listato 14-10.

Listato 14-10 - Aggiungere un getter nel modello, in lib/model/Article.class.php

public function getNbComments()
{
  return $this->countComments();
}

Dopodichè nb_comments è disponibile come campo nel modulo generato (nota che il getter utilizza una versione camelCase del nome del campo), come nel Listato 14-11.

Listato 14-11 - Getter personallizati forniscono colonne aggiuntive nei moduli di amministrazione, in backend/modules/article/config/generator.yml

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Article
    theme:            default

    list:
      display:        [id, title, nb_comments, created_at]

La risultante vista list del modulo article mostrata in Figura 14-10.

Figura 14-10 - Campo personalizzato nella vista list del modulo article

Campi personalizzati possono anche restituire codice HTML per visualizzare qualcosa di più di dati grezzi. Ad esempio, puoi estendere la classe Comment con un metodo getArticleLink() come mostrato nel Listato 14-12.

Listato 14-12 - Aggiungere un getter personalizzato che restituisce HTML, in lib/model/Comment.class.php

public function getArticleLink()
{
  return link_to($this->getArticle()->getTitle(), 'article/edit?id='.$this->getArticleId());
}

Puoi usare questo nuovo getter come campo personalizzato nella vista comment/list con la stessa sintassi del Listato 14-11. Vedi l'esempio nel Listato 14-13, ed il risultato in Figura 14-11, dove il codice HTML in output dal getter (un link all'articolo) viene mostrato nella seconda colonna al posto della chiave primaria dell'articolo.

Listato 14-13 - Getter personalizzati che restituiscono HTML possono anche essere usati come colonne aggiuntive, in modules/comment/config/generator.yml

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Comment
    theme:            default

    list:
      display:        [id, article_link, content]

Figura 14-11 - Campo personalizzato nella vista list del modulo comment

Campi partial

Il codice situato nel modello deve essere indipendente dalla presentazione. L'esempio precedente del metodo getArticleLink() non rispetta il principio della separazione dei livelli, in quanto codice della vista compare nel modello. Per raggiungere lo stesso scopo in modo corretto, dovresti mettere il codice che stampa l'HTML per un campo personalizzato in un partial. Fortunatamente, il generatore di amministrazione ti permette di specificare il nome di un campo preceduto da un prefisso con underscore. In questo caso, il file generator.yml del Listato 14-13 verrebbe modificato come nel Listato 14-14.

Listato 14-14 - I partial possono essere usati come colonne aggiuntive - usa il _prefisso

  list:
      display:        [id, _article_link, created_at]

Per funzionare, il partial _article_link.php deve essere posizionato nella cartella modules/comment/templates/, come nel Listato 14-15.

Listato 14-15 - Esempio di partial per la vista list, in modules/comment/templates/_article_link.php

<?php echo link_to($comment->getArticle()->getTitle(), 'article/edit?id='.$comment->getArticleId()) ?>

Nota che la template partial di un campo partial ha accesso all'oggetto corrente tramite una variabile che ha lo stesso nome della classe (in questo esempio $comment). Ad esempio, per un modulo costruito per una classe chiamata UserGroup, il partial avrà accesso all'oggetto corrente tramite la variabile $user_group.

Il risultato è lo stesso della Figura 14-11, a parte che stavolta viene rispettata la separazione dei livelli. Se ti abitui a tale meccanismo, avrai applicazioni molto più manutenibili.

Se avessi bisogno di personalizzare parametri per un partial, fai lo stesso come per i campi normali, sotto la chiave field. Semplicemente non includere l'underscore iniziale, come nell'esempio del Listato 14-16.

Listato 14-16 - Le proprietà di un partial possono essere personalizzate sotto la chiave fields

  fields:
        article_link:   { name: Article }

Se il tuo partial contiene una logica complicata, probabilmente lo vorrai sostituire con un component. Cambia il prefisso da _ a ~ ed il gioco è fatto, come puoi vedere dal Listato 14-17.

Listato 14-17 - I componenti possono essere usati come colonne aggiuntive - usa il ~prefisso

...
    list:
      display:        [id, ~article_link, created_at]

Nella template generata, questo sfocierà in una chiamata al component articleLink del modulo corrente.

Campi personalizzati e partial possono essere usati nelle viste list ed edit, e per i filtri. Se usi lo stesso partial per diverse viste, il contesto ('list', 'edit', o 'filter') è memorizzato nella variabile $type.

Personalizzazione della vista

Per cambiare il modo in cui appaiono le viste edit e list potresti essere tentato di cambiarne le template. Ma dato che esse vengono generate, non è una bella idea. Invece dovresti usare il file di configurazione generator.yml in quanto può fare quasi tutto quel che ti serve senza sacrificare la modularità.

Cambiare il titolo della vista

In aggiunta ai campi personalizzati, le pagine edit e list possono avere un titolo di pagina anch'esso personalizzato. Ad esempio, se tu volessi cambiare il titolo della vista articles, dovresti fare come nel Listato 14-18. Il risultato è illustrato in Figura 14-12.

Listato 14-18 - Impostare un titolo personalizzato per ogni vista, in backend/modules/article/config/generator.yml

  list:
      title:          List of Articles
      ...

    edit:
      title:          Body of article %%title%%
      display:        [content]

Figura 14-12 - Titolo della vista edit del modulo article

Dato che i titoli di default usano il nome della classe, vanno abbastanza bene (sempre che nel tuo modello tu abbia usato nomi espliciti per le classi).

Nelle stringhe di generator.yml, il valore di un campo può essere acceduto tramite il nome del campo tra %%.

Aggiungere tooltip

Nelle viste list ed edit, puoi aggiungere tooltip per aiutare a descrivere i campi visualizzati. Ad esempio, per aggiungere un tooltip al campo article_id della vista edit del modulo comment, aggiungi una prorietà help nella definizione fields come nel Listato 14-19. Il risultato è mostrato in Figura 14-13.

Listato 14-19 - Impostare un tooltip nella vista edit, in modules/comment/config/generator.yml

   edit:
      fields:
        ...
        article_id:   { help: The current comment relates to this article }

Figura 14-13 - Tooltip nella vista edit del modulo comment

Nella vista list, i tooltip vengono visualizzati nella colonna header; nella vista edit, essi appaiono sotto l'input.

Modificare il formato delle date

Le date possono nessere visualizzate secondo un formato personalizzato, se utilizzi il parametro date_format, come mostrato dal Listato 14-20.

Listato 14-20 - Formattare una data nella vista list

 list:
      fields:
        created_at:         { name: Published, params: date_format='dd/MM' }

Esso utilizza gli stessi parametri dell'helper format_date() visto nel capitolo precedente.

Personalizzazioni specifiche per la vista list

La vista list può visualizzare i dettagli di un record in una tabella, oppure tutti in una linea. Inoltre contiene funzionalità di paginazione, filtri ed ordinamento. Tali funzionalità possono essere alterate tramite il file di configurazione, come illustrato nelle sezioni seguenti.

Cambiare il layout

Per default, il link per passare dalla list alla edit viene inserito sulla chiave primaria. Se dai un'occhiata indietro alla Figura 14-11, vedrai che la colonna id non solo rappresenta la chiave primaria di ogni commento, ma è anche cliccabile per passare alla vista edit.

Se preferisci avere tale link su un'altra colonna, metti il prefisso = su tale colonna sotto la chiave display. Il Listato 14-21 mostra come rimuovere la colonna id dalla vista list e mettere il link sul campo content. Controlla la Figura 14-14 per uno screenshot.

Listato 14-21 - Spostare il link alla vista edit nella vista list, in modules/comment/config/generator.yml

   list:
      display:    [article_link, =content]

Figura 14-14 - Spostare il link alla vista edit su un'altra colonna, nella vista list del modulo comment

Per default, la vista list usa un layout tabulare, dove i campi appaiono come colonne, come visto precedentemente. Ma puoi anche usare il layout stacked e concatenare i campi in una singola stringa per tutta la lunghezza della tabella. Se scegli tale layout, devi impostare nella chiave params il pattern che definisce come la stringa deve essere composta. Ad esempio, il Listato 14-22 definisce un layout stacked per la vista list del modulo comment. Il risultato appare nella Figura 14-15.

Listato 14-22 - Utilizzare il layout stacked nella vista list, in modules/comment/config/generator.yml

 list:
      layout:  stacked
      params:  |
        %%=content%% <br />
        (sent by %%author%% on %%created_at%% about %%article_link%%)
      display:  [created_at, author, content]

Figura 14-15 - Layout stacked per la vista list del modulo comment

Nota che il layout tabular si aspetta un array di campi sotto la chiave display, ma quello stacked usa la chiave params per il codice HTML per ogni record generato. Comunque, l'array display è usato nel layout stacked per determinare quali colonne sono disponibili per l'ordinamento interattivo.

Filtrare i risultati

Nella vista list puoi aggiungere un insieme di interazioni per i filtri. Tramite essi, l'utente può visualizzare meno risultati e trovare così quelli che cerca più velocemente. Configura il filtri sotto la chiave filters, con un array di nomi di campi. Ad esempio, per avere un risultato come quello della Figura 14-16, aggiungi un filtro sui campi article_id, author e created_at, come mostrato nel Listato 14-23. Per fare ciò, avrai anche bisogno di aggiungere un metodo __toString() alla classe Article (che restituisca, ad esempio, il titolo dell'articolo).

Listato 14-23 - Impostare filtri nella vista list, in modules/comment/config/generator.yml

 list:
      filters: [article_id, author, created_at]
      layout:  stacked
      params:  |
        %%=content%% <br />
        (sent by %%author%% on %%created_at%% about %%article_link%%)
      display:  [created_at, author, content]

Figura 14-16 - Filtri nella vista list del modulo comment

I filtri visualizzati da symfony dipendono dal tipo di colonna:

  • Per colonne di testo (come il campo author del modulo comment), il filtro è una casella di testo che permette ricerche con wildcard (*).
  • Per le chiavi importate (come il campo article_id del modulo comment), il filtro è un menu a tendina con i record della tabella relativa. Come per l'helper object_select_tag(), le options del menu a tendina sono le stringhe restituite dal metodo __toString() della classe relativa.
  • Per le date (come il campo created_at del modulo comment), il filtro è una coppia di tag date rich (casella di testo e widget calendario) che permettono la selezione di intervalli di tempo.
  • Per colonne boolane, il filtro è un menu a tendina con le opzioni true, false e true or false (quest'ultimo valore reinizializza il filtro).

Esattamente come usi campi partial nelle liste, puoi creare filtri partial per averne un non gestito automaticamente da symfony. Ad esempio, immagina un campo state che possa contenere solo due valori (open e closed), ma per qualche ragione memorizzi il valore direttamente nel campo invece che in una tabella. Un semplice filtro su questo campo (di tipo string) sarebbe una ricerca di testo, ma probabilmente tu vorresti un menu a tendina. Questo diventa semplice da realizzare con un filtro partial. Vedi il Listato 14-24 per un esempio di implementazione.

Listato 14-24 - Utilizzare un filtro partial

// Definizione del partial, in templates/_state.php
<?php echo select_tag('filters[state]', options_for_select(array(
  '' => '',
  'open' => 'open',
  'closed' => 'closed',
), isset($filters['state']) ? $filters['state'] : '')) ?>
 
// Aggiunta del partial nella lista dei filtri, in config/generator.yml
    list:
      filters:        [date, _state]

Nota che il partial ha accesso alla variabile $filters, utile per recuperare il valore corrente del filtro.

C'è un'ultima opzione che può essere molto utile nella ricerca di valori vuoti. Immagina di voler filtrare la lista dei commenti per visualizzare solo quelli senza autore. Il problema è che se lasci il filtro author vuoto, verrà semplicemente ignorato. La soluzione è impostare il campo filter_is_empty a true, come nel listato 14-25, ed il filtro mostrerà un checkbox addizionale che ti permetterà di cercare valori vuoti, come mostrato in Figura 14-17.

Listato 14-25 - Aggiungere un filtro per valori vuoti sul campo author nella vista list

list:
      fields:
        author:   { filter_is_empty: true }
      filters:    [article_id, author, created_at]

Figura 14-17 - Permettere il filtro di valori vuoti

Ordinamento della lista

Nella vista list, gli header della tabella sono link che possono essere usati per riordinare la lista, come mostrato in Figura 14-18. Tali header vengono mostrati in entrambi i layout, tabular e stacked. Cliccarne uno significa ricaricare la pagina con un parametro sort che ordina la lista di conseguenza.

Figura 14-18 - Gli header nella tabella della vista list controllano l'ordinamento

Puoi riutilizzare la sintassi per indirizzare un'elenco già ordinato secondo una colonna:

<?php echo link_to('Comment list by date', 'comment/list?sort=created_at&type=desc' ) ?>

Puoi infine definire un ordinamento di default direttamente nel file di configurazione generator.yml. La sintassi è mostrata nell'esempio del Listato 14-26.

Listato 14-26 - Impostare un ordinamento di default nella vista list

    list:
      sort:   created_at
      # Sintassi alternativa, per specificare un ordine
      sort:   [created_at, desc]

Solo i campi corrispondenti a colonne effettive sono trasformati in controlli di ordinamento, non i partial od i campi personalizzati.

Personalizzare la paginazione

Le amministrazioni generate gestiscono bene anche tabelle molto grandi, perchè per default utilizzano la paginazione. Quando il numero effettivo di righe eccede il numero massimo di righe per pagina, i controlli della paginazione appaiono nell'angolo in basso a destra. Ad esempio, la Figura 14-19 mostra la lista dei commenti con sei commenti di test nella tabella ma un limite di cinque per pagina. La paginazione assicura buone prestazioni, in quanto solo le righe da visualizzare sono effettivamente recuperate dal database, ed una buona usabilità, perchè anche tabelle con milioni di righe possono essere gestite da un modulo di amministrazione.

Figura 14-19 - Appaiono i controlli di paginazione in elenchi lunghi

Puoi personalizzare il numero di record da visualizzare per pagina tramite il parametro max_per_page:

 list:
      max_per_page:   5

Usare un join per velocizzare la spedizione delle pagine

Per default, un generatore di administration usa una semplice doSelect() per recuperare una lista di record dal db. Ma se gli oggetti della lista hanno delle relazioni, il numero di query necessarie alla sua costruzione potrebbe aumentare velocemente. Ad esempio, se vuoi vedere il nome dell'articolo in una lista di commenti, necessaria una query addizionale per ogni record per trovare il relativo Article. Per cui potresti voler forzare il pager ad utilizzare il metodo doSelectJoinXXX() per ottimizzare il numero di query. Ciò può essere specificato tramite il parametro peer_method.

list:
      peer_method:   doSelectJoinArticle

Il Capitolo 18 spiega più estensivamente il concetto di join.

Personalizzazioni specifiche per la vista edit

Nella vista edit, l'utente può modificare il valore di ogni colonna per un dato record. Symfony determina il tipo di input da visualizzare secondo il data type della colonna. Quindi genera un helper object_*_tag(), e gli passa l'oggetto e la proprietà da modificare. Ad esempio, se nella configurazione della vista edit per l'articolo è specificato che l'utente può modificare il campo title:

edit:
      display: [title, ...]

allora nella pagina edit verrà visualizzato una casella di testo per la modifica di title, in quanto nello schema esso è definito di tipo varchar.

<?php echo object_input_tag($article, 'getTitle') ?>

Cambiare il tipo di input

Le regole di conversione type-to-field di default sono le seguenti:

  • Una colonna definita come integer, float, char, varchar(size) appare nella vista edit come un object_input_tag().
  • Una colonna definita come longvarchar appare come object_textarea_tag().
  • Una chiave importata appare come object_select_tag().
  • Una colonna definita come boolean appare come object_checkbox_tag().
  • Una colonna definita come timestamp appare come object_input_date_tag().

Potresti voler fare l'override di queste regole per specificare un tipo di input personalizzato per un certo campo. Per fare ciò, imposta il parametro type ad uno specifico nome di helper nella definizione fields. Come per le opzioni del object_*_tag() generato, le puoi cambiare tramite il parametro params. Un esempio nel Listato 14-27.

Listato 14-27 - Impostare un tipo di input personalizzato per la vista edit

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Comment
    theme:            default

    edit:
      fields:
                      ## Drop the input, just display plain text
        id:           { type: plain }
                      ## The input is not editable
        author:       { params: disabled=true }
                      ## The input is a textarea (object_textarea_tag)
        content:      { type: textarea_tag, params: rich=true css=user.css tinymce_options=width:330 }
                      ## The input is a select (object_select_tag)
        article_id:   { params: include_custom=Choose an article }
         ...

I parametri params vengono passati come opzioni al object_*_tag() generato. Ad esempio, la definizione di params per il precedente article_id produce nella template:

<?php echo object_select_tag($comment, 'getArticleId', 'related_class=Article', 'include_custom=Choose an article') ?>

Ciò significa che tutte le opzioni normalmente disponibili negli helper delle form possono essere personalizzate nella vista edit.

Gestire campi partial

I campi partial possono essere usati nella vista edit proprio come nella list. La differenza sta nel fatto che devi gestire manualmente, nell'azione, l'aggiornamento della colonna secondo il valore del parametro di richiesta spedito dal partial. Symfony sa come gestire i campi normali (corrispondenti alle colonne effettive), ma non può immaginare come gestire input che tu potresti inserire in partial.

Ad esempio, immagina un modulo di amministrazione per una classe User dove sono disponibili i campi id, nickname, e password. L'amministratore del sito deve poter cambiare la password di un utente ma la vista edit non deve visualizzarne il valore per motivi di sicurezza. Invece, la form deve mostrare una casella di testo vuota che l'amministratore può riempire per cambiare la password. Le impostazioni per il generatore per tale tipo di vista sono simili a quelli del Listato 14-28.

Listato 14-28 - Includere un partial nella lista edit

   edit:
      display:        [id, nickname, _newpassword]
      fields:
        newpassword:  { name: Password, help: Enter a password to change it, leave the field blank to keep the current one }

Il partial templates/_newpassword.php contiente qualcosa tipo

<?php echo input_password_tag('newpassword', '') ?>

Nota che questo partial utilizza un helper delle form normale, non un object, in quanto non è desiderabile recuperare il valore della password dall'oggetto User corrente per popolare una casella di testo.

Ora, per utilizzare il valore di tale input per poter aggiornare l'oggetto nell'azione, ne devi estendere il metodo updateUserFromRequest(). Per fare ciò, crea un metodo con lo stesso nome nella classe dell'azione, come mostrato nel Listato 14-29.

Listato 14-29 - Gestire un partial nell'azione, in modules/user/actions/actions.class.php

class userActions extends sfActions
{
  protected function updateUserFromRequest()
  {
    // Handle the input of the partial field
    $password = $this->getRequestParameter('newpassword');
 
    if ($password)
    {
      $this->user->setPassword($password);
    }
 
    // Let symfony handle the other fields
    parent::updateUserFromRequest();
  }
}

Nel mondo reale, una vista user/edit contiene due campi per la password, il secondo dei quali deve contenere lo stesso valore del primo per controllare che non ci siano errori di digitazione. In pratica, come hai visto nel Capitolo 10, questo viene fatto tramite validazione. Le amministrazioni generate beneficiano dello stesso meccanismo come gli altri moduli.

Gestire chiavi importate

Se il tuo schema definisce relazioni tra le tabelle, i moduli di amministrazione generati se ne avvantaggiano offrendo controlli automatizzati, semplificando così enormemente il sistema di gestione delle relazioni.

Relazioni uno-a-molti

Il generatore di amministrazioni si occupa di relazioni 1-n. Come illustrato precedentemente nella Figura 14-1, la tabella blog_comment legata a blog_article tramite il campo article_id. Se inizializzi il modulo della classe Comment con il generatore di amministrazioni, l'azione comment/edit visualizzerà automaticamente article_id come un menu a tendina con gli ID dei record disponibili della tabella blog_article (controlla di nuovo la Figura 14-9 per un esempio).

Inoltre, se definisci un metodo __toString() nell'oggetto Article, la tendina mostrerà nelle proprie opzioni invece delle chiavi primarie il valore restituito da tale metodo.

Se hai bisogno di mostrare la lista dei commenti relativi ad un articolo nel modulo article (relazione n-1), avrai bisogno di personalizzare un pochino il modulo tramite un partial.

=== Relazioni molti-a-molti ====

Symfony si occupa anche delle relazioni n-n, ma dato che non le puoi definire nello schema, devi aggiungere qualche parametro nel file generator.yml.

L'implementazione delle relazioni n-n richiede una tabella intermedia. Se ad esempio ci fosse una relazione n-n fra le tabelle blog_article e blog_author (un articolo può essere scritto da più di un autore e, ovviamente, un autore può scrivere più articoli), il tuo database avrà una tabella blog_article_author o simile, come mostrato in Figura 14-20.

Figura 14-20 - Implementazione delle relazioni molti-a-molti

Il modello avrà quindi una classe chiamata ArticleAuthor, e questa è la sola cosa di cui il generatore di amministrazioni abbia bisogno; ma devi passarglielo come parametro through_class.

Ad esempio, in un modulo generato basato sulla classe Article, puoi aggiungere un campo per creare una relazione n-n con la classe Author se scrivi il file generator.yml come nel Listato 14-30.

Listato 14-30 - Gestire relazioni molti-a-molti tramite il parametro through_class

edit:
      fields:
        article_author: { type: admin_double_list, params: through_class=ArticleAuthor }

Tale campo gestisce i collegamenti tra gli oggetti esistenti, per cui un menu a tendina non è sufficiente. Devi usare un tipo di input speciale in questo caso. Symfony offre tre widget per aiutare a mettere in relazione i membri di due liste (illustrati nella Figura 14-21):

  • Un admin_double_list è un insieme di due menu a tendina espansi, entrambi con pulsanti per spostare elementi dalla prima lista (disponibili) alla seconda (selezionati).
  • Un admin_select_list è un menu a tendina multiplo, in cui puoi selezionare più di un elemento.
  • Un admin_check_list è una lista di checkbox.

Figura 14-21 - Controlli disponibili per relazioni molti-a-molti

Aggiungere interazioni

I moduli di amministrazione permettono le normali operazioni CRUD, ma puoi anche aggiungere le tue personali interazioni o limitazioni. Ad esempio, l'interazione definita nel Listato 14-31 fornisce accesso a tutte le azioni CRUD di default sul modulo article.

Listato 14-31 - Definire interazioni per ogni vista, in backend/modules/article/config/generator.yml

  list:
      title:          List of Articles
      object_actions:
        _edit:         ~
        _delete:       ~
      actions:
        _create:       ~

    edit:
      title:          Body of article %%title%%
      actions:
        _list:         ~
        _save:         ~
        _save_and_add: ~
        _delete:       ~

In una vista list ci sono due impostazioni di azione: la lista delle azioni disponibili per ogni oggetto, e quelle per l'intera pagina. L'effetto delle interazioni definite nel Listato 14-31 è mostrato in Figura 14-22. Ogni linea mostra un pulsante per modificare il record ed uno per eliminarlo. Alla ifne della pagina, un pulsante permette di crearne di nuovi.

Figura 14-22 - Interazioni nella vista list

In una vista edit, dato che c'è un solo record da modificare, è disponibile un solo set di azioni da definire. L'effetto delle interazioni edit definite nel Listato 14-31 è mostrato in Figura 14-23. Entrambe le azioni save e save_and_add salvano il record corrente; a differenza è che la prima mostra i dati salvati, la seconda campi vuoti per aggiungere un nuovo record. Tale azione risulterà molto utile quando devi aggiungere molti record in rapida successione. Riguardo la posizione del pulsante delete, esso è distanziato dagli altri perchè non venga cliccato per errore.

I nomi delle interazioni che cominciano con un underscore (_) indicano a symfony di usare le icone ed azioni di default corrispondenti; il generatore di amministrazioni capisce _edit, _delete, _create, _list, _save, _save_and_add, e _create.

Figura 14-23 - Interazioni nella vista edit

Ma puoi anche aggiungere un'interazione personalizzata, nel qual caso devi specificare un nome che non cominci con underscore, come nel Listato 14-32.

Listato 14-32 - Definire un'interazione personalizzata

list:
      title:          List of Articles
      object_actions:
        _edit:        -
        _delete:      -
        addcomment:   { name: Add a comment, action: addComment, icon: backend/addcomment.png }

Ogni articolo della lista visualizzerà ora un pulsante addcomment.png, come mostrato in Figura 14-24. Cliccandolo viene chiamata l'azione addComment del modulo corrente. La chiave primaria dell'oggetto corrente viene aggiunta automaticamente ai parametri di richiesta.

Figura 14-24 - Interazione personalizzata nella vista list

L'azione addComment può essere implementata come nel Listato 14-33.

Listato 14-33 - Implementare l'azione dell'interazione personalizzata, in actions/actions.class.php

public function executeAddComment()
{
  $comment = new Comment();
  $comment->setArticleId($this->getRequestParameter('id'));
  $comment->save();
 
  $this->redirect('comment/edit?id='.$comment->getId());
}

Un'ultima cosa a proposito delle azioni: se vuoi sopprimere completamente le azioni per una categoria, usa una lista vuota, come nel Listato 14-34.

Listato 14-34 - Rimuovere tutte le azioni nella vista list

list:
      title:          List of Articles
      actions:        {}

Validazione delle form

Se dai un'occhiata alla template generata _edit_form.php nella cartella cache/ del tuo progetto, vedrai che i nomi dei campi della form usano una convenzione speciale. In una vista edit generata, i nomi dei campi sono la concatenazione del nome del modulo e del campo tra parentesi quadre.

Ad esempio, se la vista edit per il modulo article ha un campo title, la template sarà come quella del Listato 14-35 ed il campo sarà identificato come article[title].

Listato 14-35 - Sintassi dei nomi degli input generati

// generator.yml
generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Article
    theme:            default
    edit:
      display: [title]

// Resulting _edit_form.php template
<?php echo object_input_tag($article, 'getTitle', array('control_name' => 'article[title]')) ?>

// Resulting HTML
<input type="text" name="article[title]" id="article[title]" value="My Title" />

Ciò ha molti vantaggi durante il processo di gestione interna della form. Come spiegato nel Capitolo 10, ciò ha anche come conseguenza l'aggiunta di un pizzico di complessità nella configurazione della validazione, per cui devi sostituire le parentesi quadre con le graffe nella definizione fields. Inoltre, quando usi il nome di un campo come parametro per la validazione, lo devi specificare come appare nel codice HTML generato (ovvero con le parentesi quadre ma tra doppi apici). Consulta il Listato 14-36 per dettagli sulla sintassi speciale della validazione per form generate.

Listato 14-36 - Sintassi per la validazione di form generate nelle amministrazioni

## Sostituzione delle parentesi quadre con le graffe nella definizione fields
fields:
  article{title}:
    required:
      msg: You must provide a title
    ## Per i parametri di validazione usa i doppi apici
    sfCompareValidator:
      check:        "user[newpassword]"
      compare_error: The password confirmation does not match the password.

Limitare le azioni dell'utente utilizzando credenziali

Per un dato modulo di amministrazione, i campi disponibili e le interazioni possono variare a seconda delle credenziali dell'utente corrente (consulta il Capitolo 6 per una descrizione delle funzionalità di sicurezza di symfony).

I campi nel generatore possono avere un parametro credentials in modo da apparire solo agli utenti che hanno le credenziali corrette. Ciò funziona per le viste list ed edit. Inoltre, il generatore può anche nascondere interazioni secondo le credenziali. Il Listato 14-37 mostra tali funzionalità.

Listato 14-37 - Utilizzo delle credenziali in generator.yml

## La colonna id viene mostrata solo ad utenti con credenziali admin
    list:
      title:          List of Articles
      layout:         tabular
      display:        [id, =title, content, nb_comments]
      fields:
        id:           { credentials: [admin] }

## L'interazione addcomment è limitata agli utenti con credenziali admin
    list:
      title:          List of Articles
      object_actions:
        _edit:        -
        _delete:      -
        addcomment:   { credentials: [admin], name: Add a comment, action: addComment, icon: backend/addcomment.png }

Modificare la presentazione dei moduli generati

Puoi modificare la presentazione dei moduli generati in modo che rispetti lo stile dell'applicazione, non solo applicando il tuo stylesheet ma anche facendo l'override delle template di default.

Utilizzare un foglio di stile personalizzato

Dato che l'HTML generato è contenuto strutturato, puoi fare ciò che ti piace con la presentazione.

Puoi definire un CSS alternativo da usare con un modulo di amministrazione al posto di quello di default aggiungendo un parametro css alla configurazione del generatore, come mostrato nel Listato 14-38.

Listato 14-38 - Usare uno stylesheet personalizzato invece di quello di default

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      Comment
    theme:            default
    css:              mystylesheet

Alternativamente puoi usare i meccanismi forniti dal modulo view.yml per fare l'override degli stili per ogni vista.

Creare un header ed un footer personalizzati

Le viste list ed edit includono sistematicamente partial per l'header ed il footer. Per default non c'è alcun partial nella cartella templates/ di un modulo di amministrazione, ma per averlo incluso automaticamente ne devi semplicemente aggiungere uno con uno dei seguenti nomi:

_list_header.php
_list_footer.php
_edit_header.php
_edit_footer.php

Ad esempio, se vuoi aggiungere un header personalizzato per la vista article/edit, crea un file chiamato _edit_header.php come nel Listato 14-39. Funzionerà senza ulteriori configurazioni.

Listato 14-39 - Esempio di header partial edit, in modules/articles/template/_edit_header.php

<?php if ($article->getNbComments() > 0): ?>
  <h2>This article has <?php echo $article->getNbComments() ?> comments.</h2>
<?php endif; ?>

Nota che un partial edit ha sempre accesso all'oggetto corrente tramite una variabile con lo stesso nome del modulo, e che un partial list ha sempre accesso al pager corrente tramite la variabile $pager.

Personalizzare il tema

Ci sono altri partial ereditati dal framework di cui puoi fare l'override nella cartella templates/ del modulo perchè rispondano alle tue esigenze.

Le template del generatore sono divise in piccole parti che possono essere overridate indipendentemente, ed anche le azioni possono essere cambiate ad una ad una.

Comunque, se ne volessi fare l'override per diversi moduli nello stesso modo, dovresti probabilmente creare un tema. Si tratta di un set completo di template ed azioni che possono essere usate in un modulo di amministrazione se specificato nel valore theme all'inizio del file generator.yml. Con il tema di default, symfony usa i file definiti in $sf_symfony_data_dir/generator/sfPropelAdmin/default/.

I file del tema devono essere situati in una struttura ad albero del progetto, nella cartella data/generator/sfPropelAdmin/[theme_name]/template/, e potresti iniziare un nuovo tema copiando i file di quello di default (situati in $sf_symfony_data_dir/generator/sfPropelAdmin/default/template/). In questo modo, sei sicuro che tutti i file necessari ad un tema saranno presenti:

// Partials, in [theme_name]/template/templates/
_edit_actions.php
_edit_footer.php
_edit_form.php
_edit_header.php
_edit_messages.php
_filters.php
_list.php
_list_actions.php
_list_footer.php
_list_header.php
_list_messages.php
_list_td_actions.php
_list_td_stacked.php
_list_td_tabular.php
_list_th_stacked.php
_list_th_tabular.php

// Actions, in [theme_name]/template/actions/actions.class.php
processFilters()     // Process the request filters
addFiltersCriteria() // Adds a filter to the Criteria object
processSort()
addSortCriteria()

Stai attento al fatto che le template in effetti sono template di template, ovvero file PHP di cui verrà fatto il parsing tramite una utility speciale per generare le template secondo le impostazioni del generatore (questa viene chiamata fase di compilazione). Le template generate devono ancora contenere codice PHP da eseguire durante il browsing effettivo, per cui le template di template usano una sintassi alternativa per mantenere il codice PHP non eseguibile al primo passo. Il Listato 14-40 mostra un estratto di una template di template di default.

Listato 14-40 - Sintassi di template di template

<?php foreach ($this->getPrimaryKey() as $pk): ?>
[?php echo object_input_hidden_tag($<?php echo $this->getSingularName() ?>,'get<?php echo $pk->getPhpName() ?>') ?]
<?php endforeach; ?>

In questo listato, il codice PHP introdotto da <? eseguito immediatamente (in compilazione), quello che comincia con [? viene solo eseguito in esecuzione, ma il template engine trasforma infine i tag in modo che la template finale risulta:

<?php echo object_input_hidden_tag($article, 'getId') ?>

Gestire template di template è difficoltoso, per cui se vuoi creare il tuo tema il consiglio migliore è quello di partire da quello di default e modificarlo passo passo, e testarlo estensivamente.

Puoi anche pacchettizzare un tema in un plugin, che lo rende più riutilizzabile e facile da distribuire su più applicazioni. Consulta il Capitolo 17 per maggiori informazioni.

Sommario

Per inizializzare i tuoi moduli o generare automaticamente applicazioni di backend, la base sono uno schema ed un modello di oggetti ben definiti. Puoi modificare il codice PHP degli scaffolding, ma i moduli di amministrazione generati sono da modificare per lo più tramite configurazione.

Il file generator.yml è il cuore della programmazione di backend generati. Permette una personalizzazione completa di contenuti, funzionalità e del look and feel delle viste list ed edit. Puoi gestire label, campi, suggerimenti, filtri, ordinamenti, dimensioni di pagine, tipi di input, relazioni, interazioni personalizzate e credenziali direttamente in YAML, senza una linea di codice PHP.

Se il generatore non supporta nativamente la funzionalità di cui hai bisogno, i partial e la possibilità di fare l'override delle azioni forniscono un'estendibilità completa. Inoltre, puoi riusare i tuoi adattamenti ai meccanismi del generatore grazie ai temi.