lunedì 12 ottobre 2009

Lyra, azioni del modulo article

Fino a questo punto abbiamo modificato il layout globale dell'applicazione frontend di Lyra. È il momento di iniziare a scrivere il codice che dovrà gestire le varie azioni del modulo article.

Quando abbiamo creato il modulo article con il comando doctrine:generate-module è stata creata una classe articleActions in apps/frontend/modules/article/actions/actions.class.php e sono stati generati alcuni metodi per processare un certo numero di azioni base: tra queste, index viene di regola utilizzata per mostrare un elenco di record, nel nostro caso l'elenco degli articoli pubblicati sulla prima pagina del sito.

La prima pagina in Lyra

In questa fase di sviluppo iniziale la prima pagina del sito è costituita semplicemente da un elenco di articoli ordinati per data decrescente in "stile blog": titolo, data, autore (quando ci sarà perché non abbiamo ancora la gestione utenti), sommario o testo introduttivo ed eventuale link 'leggi tutto'.

Come si è visto nell'articolo precedente, l'azione index è processata da un metodo executeIndex() della classe articleActions.

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

class articleActions extends sfActions
{
   public function executeIndex(sfWebRequest $request)
   {
      $this->items = Doctrine::getTable('LyraArticle')->getFrontPageItems();
   }
  ...
}

Per ottenere l'elenco degli articoli in prima pagina è necessaria ovviamente un'interrogazione del database e, per le considerazioni fatte la volta scorsa, questo è il compito di un oggetto Model. Otteniamo quindi un'istanza della classe LyraArticleTable e ne invochiamo il metodo getFrontPageItems() che scriveremo tra poco.

Il risultato è un array di record che viene salvato come proprietà items. Quando creiamo una proprietà in questo modo nel codice di un'azione il valore assegnato sarà visibile dalla view (template) come variabile.

Si è visto (symfony, generazione del Modello) che in lib/model/doctrine esistono due classi per ogni tabella dello schema. Per quanto riguarda gli articoli:

  • LyraArticleTable che rappresenta la tabella;
  • LyraArticle che rappresenta un record della tabella.

Di solito la prima contiene i metodi che ritornano i risultati delle interrogazioni sulla tabella, è lì che implementeremo getFrontPageItems().
lib/model/doctrine/LyraArticleTable.class.php

class LyraArticleTable extends Doctrine_Table
{
  public function getFrontPageItems()
  {
    $q = $this->getActiveItemsQuery()
      ->andWhere('a.is_featured = ?', true);
    return $q->execute();
  }
  
  public function getActiveItems()
  {
    $q = $this->getActiveItemsQuery();
    return $q->execute();
  }

  public function getActiveItemsQuery()
  {
    return $this->createQuery('a')
      ->where('a.is_active = ?', true)
      ->addOrderBy('a.is_sticky DESC, a.created_at DESC');
  }
}

Il metodo getActiveItemsQuery() ritorna una query per la selezione degli articoli pubblicati (is_active è true) ordinati secondo il criterio di ordinamento predefinito (is_sticky a true indica che l'articolo sta sempre in testa alla lista).

Il metodo getFrontPageItems() aggiunge un proprio criterio di selezione (is_featured è true per gli articoli da visualizzare in prima pagina) ed esegue la query.

Le query sono scritte utilizzando Doctrine Query Language (DQL) su cui si possono trovare tutte le informazioni nella documentazione ufficiale. Faccio notare soltanto:

  • al metodo createQuery() basta passare un alias ('a'), non serve sia specificato il nome della tabella in quanto implicitamente è quello della tabella di riferimento della classe (nel nostro caso articles);
  • il '?' nelle condizioni where funge da segnaposto per un parametro che viene passato come argomento successivo;
  • Il risultato dell'esecuzione della query con il metodo execute() è un array di oggetti istanze della classe LyraArticle.

Resta da creare il template. La prima considerazione da fare è che il formato di visualizzazione degli articoli in prima pagina (titolo, sommario con link 'leggi tutto') sarà utilizzato anche in altre parti del sito, ad esempio nelle pagine che mostrano gli articoli appartenenti ad una categoria. Conviene da subito mettersi in condizione di evitare duplicazioni di codice: quindi per prima cosa creiamo un partial, cioè un blocco di codice che può essere richiamato da più di un template.

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

<?php foreach ($items as $item): ?>
  <h2 class="article-title">
    <?php echo link_to($item->getTitle(), 'article/show?id='.$item->getId())?>
  </h2>
  <div class="article-date">
    <?php echo $item->getCreatedAt() ?>
  </div>
  <div class="article-summary">
    <?php
    echo $item->getSummary(ESC_RAW);
    if($item->showReadmore()): ?>
      <span class="article-readmore">
      <?php echo link_to(__('LINK_READMORE'), 'article/show?id='.$item->getId(), array('title'=>$item->getTitle()))?>
      </span>
    <?php endif ?>
  </div>
  <div class="article-separator"></div>
<?php endforeach; ?>

Un partial ha sempre un nome di file che inizia con il carattere sottolineato:_list.php viene incluso nel template indexSuccess.php con questa sintassi:

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

<?php include_partial('article/list', array('items'=>$items)); ?>

Alcune cose da notare. L'helper link_to() serve a generare un link HTML: il primo parametro è il testo da usare per l'ancora, il secondo la rotta in base alla quale generare la URL per il link; il formato della rotta (modulo/azione/parametri) è quello già visto nell'articolo precedente. L'azione show servirà a mostrare l'articolo a tutta pagina, i parametri (nel nostro caso solo l'id dell'articolo da visualizzare) sono passati nel formato usato per una query string: coppie chiave=valore, separate dal carattere '&' se sono più di una.

Come accennato, il valore della proprietà items del controller (assegnato con $this->items in executeIndex()), viene visto come variabile ($items) nel template e passato al partial con lo stesso identificatore items (dal codice array('items'=>$items)).

Nel partial il contenuto dei singoli campi di ogni record è letto tramite metodi getter. Il parametro ESC_RAW si usa quando si vuole il contenuto del campo senza mascheramento delle entità HTML, questo è necessario quando il campo (nel nostro caso il sommario dell'articolo) può contenere codice HTML.

Richiamo anche l'attenzione sul modo in cui viene creata l'ancora per il link 'leggi tutto': si utilizza una costante simbolica che sarà tradotta in base ai file di lingua. Questo modo di procedere è diverso da quello del tutorial ufficiale (Internazionalizzazione e Localizzazione) dove si utilizza una lingua predefinita nei template e poi si generano i file di traduzione solo per le lingue diverse da quella predefinita.

Io preferisco utilizzare delle costanti nei template per poi generare i file di traduzione per tutte le lingue (vedremo in seguito come). In questo modo la personalizzazione dell'interfaccia è più semplice: ad esempio se si preferisce avere non 'leggi tutto', ma 'Continua ...' o altro non sarà necessario modificare il sorgente dell'applicazione, ma solo il file di traduzione.

__() è un helper che serve appunto a tradurre una stringa in diversi lingue: perché possa funzionare è necessario che le funzioni multilingua siano attivate nel file delle impostazioni.

apps/frontend/config/settings.yml
...
all:
  .settings:
    standard_helpers: [Partial, Cache, I18N]
    i18n: on
    default_culture: it
...

Ho eseguito il commit della revisione 9 su Google Code. Se si è configurato un proprio ambiente di sviluppo secondo le indicazioni date negli articoli precedenti, si possono vedere i risultati del lavoro facendo il checkout della revisione e digitando nella barra indirizzi del browser

http://lyra/frontend_dev.php/article

Poiché index è l'azione predefinita, non serve indicarla nella URL. L'indirizzo seguente produrrebbe lo stesso risultato

http://lyra/frontend_dev.php/article/index

Visto che non abbiamo generato i file delle traduzioni, come ancora del link leggi tutto sarà visualizzata la costante.

Nessun commento:

Posta un commento

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