venerdì 30 ottobre 2009

Personalizzare l'aspetto dei form in symfony

I form per l'inserimento e la modifica dei dati sono una parte importante di un cms come di ogni altra applicazione web. Durante lo sviluppo di Lyra (vedi Lyra, inserimento e modifica articoli) abbiamo già incontrato diversi esempi delle possibilità offerte dal framework per personalizzare l'aspetto ed il funzionamento dei form. Ad esempio:

  • variare l'ordine posizione dei campi;
  • modificare l'etichetta;
  • modificare il tipo di controllo usato dall'utente per inserire i dati (widget nella terminologia di symfony);
  • aggiungere dinamicamente campi e relativi widget;
  • impostare regole di validazione.

È possibile però fare molto di più ed arrivare a personalizzare completamente il codice HTML che determina la struttura del form. Per fare questo è necessario creare una classe form formatter personalizzata, ne vedremo un esempio tra poco realizzando il form per l'inserimento di un commento ad un articolo.

Form commenti

Dopo aver sviluppato le funzioni di visualizzazione dei commenti è necessario creare il form per l'invio di un commento. La classe relativa a questo form (LyraCommentForm) è stata generata automaticamente a partire dalle informazioni contenute nello schema dati.

Poiché il form viene visualizzato dopo la lista dei commenti sulla vista a tutta pagina dell'articolo, l'oggetto relativo viene creato nell'azione show del modulo article per essere passato al template.

apps/frontend/modules/article/actions/actions.class.php

class articleActions extends sfActions
{
...
  public function executeShow(sfWebRequest $request)
  {
    ...
    $this->form = new LyraCommentForm();
    $this->form->setDefault('article_id', $this->item->getId());
  }
}

Viene impostato il valore di default del campo article_id per legare il commento all'articolo.

Come si è fatto per la lista commenti, la visualizzazione del form viene demandata ad un partial richiamato in showSuccess.php.

apps/frontend/modules/article/templates/showSuccess.php

if($form) {
  include_partial('article/comment_form', array('form'=>$form));
}
?> // fine showSuccess.php
apps/frontend/modules/article/templates/_comment_form.php

<h3 id="comment-form"><?php echo __('HEAD_SUBMIT_COMMENT') ?></h3>
<div id="form-wrapper">
  <form action="<?php echo url_for('article/comment?id=' . $form['article_id']->getValue()) ?>" method="post">
    <?php echo $form ?>
    <input type="submit" value="Submit" />
  </form>
</div>

Come si vede dal valore dell'attributo action, i dati inviati tramite il form vengono processati da un'azione comment del modulo article. In questa fase non mi è sembrato utile creare un nuovo modulo nel frontend dedicato alla gestione dei commenti, in futuro potrebbe essere necessario farlo.

apps/frontend/modules/article/actions/actions.class.php

class articleActions extends sfActions
{
...
  public function executeComment(sfWebRequest $request)
  {
    $this->forward404Unless($request->isMethod('post'));
    $this->item = Doctrine::getTable('LyraArticle')->find($request->getParameter('id'));
    $this->forward404Unless($this->item);
    $this->form = new LyraCommentForm();
    $this->processCommentForm($request, $this->form);
    $this->comments = $this->item->getActiveComments();
    $this->setTemplate('show');
  }
  ...
  protected function processCommentForm(sfWebRequest $request, sfForm $form)
  {
    $form->bind($request->getParameter($form->getName()));
    if ($form->isValid())
    {
      $comment = $form->save();
      $this->getUser()->setFlash('notice', 'MSG_COMMENT_SAVED');
      $this->redirect('article/show?id='.$comment->getArticleId());
    }
  }
}

Una volta che il form è stato salvato senza errori si viene reindirizzati alla pagina dell'articolo. Il metodo setFlash() imposta un messaggio di conferma da visualizzzare dopo il redirect

Resta da personalizzare la classe LyraCommentForm. Un primo livello di personalizzazione avviene nel metodo configure() della classe. Si tratta di operazioni in gran parte già viste e comunque ben documentate.

class LyraCommentForm extends BaseLyraCommentForm
{
  public function configure()
  {
    $this->removeFields();

Si rimuovono i campi che non devono essere visualizzati sul form. Perché si utilizzi un metodo protetto per questa operazione sarà chiaro quando svilupperemo la gestione di backend dei commenti, ma chi ha seguito il tutorial Jobeet lo sa già.

    $this->widgetSchema['article_id'] = new sfWidgetFormInputHidden();

Come si è visto, il valore del campo article_id viene impostato nell'azione e non deve essere modificato dall'utente: si cambia quindi il tipo di widget da testo a campo nascosto.

    $this->widgetSchema['author_name']->setLabel('AUTHOR_NAME');
    $this->widgetSchema['author_email']->setLabel('AUTHOR_EMAIL');
    $this->widgetSchema['author_url']->setLabel('AUTHOR_URL');
    $this->widgetSchema['content']->setLabel(false);

Si modificano le etichette dei campi. Passando false al metodo setLabel() si rimuove l'etichetta per il campo content (l'area di testo per l'inserimento del commento).

    $this->widgetSchema['content']->setAttribute('rows',12);
    $this->widgetSchema['content']->setAttribute('cols',45);
    $this->widgetSchema->setHelp('author_email','AUTHOR_EMAIL_HELP');

Si impostano attributi per l'area di testo ed un testo di aiuto che sarà visualizzato sotto il campo e-mail.

    $this->validatorSchema['author_name']->addMessage('required','AUTHOR_NAME_REQUIRED');
    $this->validatorSchema['content']->addMessage('required','CONTENT_REQUIRED');
    $this->validatorSchema['author_email'] = new sfValidatorEmail(
      array('required'=>true),
      array('required'=>'AUTHOR_EMAIL_REQUIRED','invalid'=>'AUTHOR_EMAIL_INVALID')
    );
    $this->validatorSchema['author_url'] = new sfValidatorUrl(
      array('required'=>false),
      array('invalid'=>'AUTHOR_URL_INVALID')
    );

Si impostano i messaggi di errore che sono visualizzati in caso di mancata validazione del campo. Per i campi author_email e author_url si impostano validatori standard che bloccano l'inserimento di un indirizzo e-mail o URL non validi.

    $this->widgetSchema->setFormFormatterName('LyraComment');

Imposta il nome della classe form formatter da utilizzare per il form.

  } //fine configure()
  protected function removeFields()
  {
    unset($this['created_at'], $this['updated_at'], $this['is_active']);
  }
}// fine LyraCommentForm

Form formatter personalizzati

Tornando al partial che visualizza il form (_comment_form.php), si nota che nel codice sono stati inclusi solo i tag <form> e <input> per il pulsante Invia, mentre il codice HTML per tutti campi e le etichette è generato da una singola istruzione

echo $form;

I tag (<input>, <textarea>,<select>) e gli attributi dei singoli campi sono determinati dal widget creato per il campo nella classe base del form ed eventualmente modificato nel metodo configure() della classe derivata. Anche i tag <label> sono generati grazie ad informazioni impostate nella classe del form, invece il codice HTML che 'incornicia' campi ed etichette viene generato da una classe form formatter.

Nel framework viene definita una classe base sfWidgetFormSchemaFormatter e due classi da essa derivate: sfWidgetFormSchemaFormatterTable e sfWidgetFormSchemaFormatterList.

sfWidgetFormSchemaFormatterTable verrà utilizzata per tutti i form a meno che non si indichi al framework (con il metodo setFormFormatterName() che abbiamo visto sopra) di utilizzare la classe sfWidgetFormSchemaFormatterList o una nostra classe personalizzata al posto di quella predefinita.

Questa è la classe creata per il form commenti di Lyra.

class sfWidgetFormSchemaFormatterLyraComment extends sfWidgetFormSchemaFormatter
{
  protected
      $rowFormat = '<div class="row">%error%%field%%label%%help%%hidden_fields%</div>',
      $helpFormat = '<div class="field-help">%help%</div>';
}

Il file con la definizione della classe va inserito nella cartella lib del proprio progetto. La classe deve essere derivata dal formatter base e il nome deve essere sfWidgetFormSchemaFormatter + il nome che vogliamo dare al nostro formatter e che passaremo a setFormFormatterName() nel metodo configure() della classe form.

A me serviva solo includere in tag <div> campo ed etichetta, posizionare l'etichetta a destra del campo sulla stessa riga, gli eventuali messaggi di errori sopra il campo e creare un ulteriore <div> per racchiudere il testo di aiuto che viene mostrato sotto i campi. Quindi è stato sufficiente personalizzare le proprietà $rowFormat e $helpFormat.

In questo modo il form appare così:

Il tutto è incluso nella revisione 15. Chi si allinea al repository, deve ricordarsi di eseguire subito dopo il checkout il comando

./symfony cc

Male non fa mai, ma è obbligatorio quando si aggiungono nuove classi in lib. Con queste modifiche è già possibile inserire commenti, mancando però le funzioni di moderazione nel backend, per vedere i nuovi commenti nel frontend è necessario impostare manualmente il campo is_active a 1 nel database in quanto i nuovi commenti vengono salvati con il valore predefinito 0 (cioè non pubblicato).

Nessun commento:

Posta un commento

Nota. Solo i membri di questo blog possono postare un commento.