Caution: You are browsing the legacy symfony 1.x part of this website.

Comment créer un flux de syndication de contenu

1.2
Symfony version
1.1
Language

Aperçu général

Si votre application contient des messages, des images, des questions ou des listes, et que ces informations sont mises à jour plus d'une fois par mois, vous pouvez fournir un flux de syndication (RSS, Atom, etc.). Les utilisateurs peuvent ainsi lire les dernières mises à jour de votre site depuis un agrégateur externe.

Si votre modèle de données est construit correctement, quelques lignes de code suffisent pour mettre en place un flux grâce au plugin proposé par symfony.

Introduction

L'exemple décrit dans ce chapitre est un simple gestionnaire de blog, contenant une table Post et Author.

Post Author
id id
author_id first_name
title last_name
description email
body
created_at

La classe Post est enrichie d'une méthode getStrippedTitle() qui retourne le titre correctement formaté pour une URI ( suppression des caractères spéciaux, remplacement des espaces par des underscores et conversion des majuscules en minuscules ).

public function getStrippedTitle()
{
  $text = strtolower($this->getTitle());
 
  // strip all non word chars
  $text = preg_replace('/\W/', ' ', $text);
  // replace all white space sections with a dash
  $text = preg_replace('/\ +/', '-', $text);
  // trim dashes
  $text = preg_replace('/\-$/', '', $text);
  $text = preg_replace('/^\-/', '', $text);
 
  return $text;
}

La classe Author est enrichie d'une méthode getName() :

public function getName()
{
  return $this->getFirstName().' '.$this->getLastName()
}

Pour plus d'informations sur l'extension d'un modèle, référez-vous au Chapitre 8.

Le fichier routing.yml contient la règle suivante:

post:
    url:   /permalink/:stripped_title
    param: { module: post, action: read }

Pour plus d'information sur le système de routage, référez-vous au Chapitre 9.

On crée un module spécifique nommé feed qui contiendra toutes les actions et les templates.

$ symfony init-module myapp feed

Résultats attendus

L'action feed doit retourner un flux Atom. Voici un exemple décrivant les informations que doit contenir ce type de flux:

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
  <title>The mouse blog</title>
  <link href="http://www.myblog.com/" />
  <updated>2005-12-11T16:23:51Z</updated>
  <author>
    <name>Peter Clive</name>
    <author_email>[email protected]</author_email>
  </author>
  <id>4543D55FF756G734</id>
 
  <entry>
    <title>I love mice</title>
    <link href="http://www.myblog.com/permalink/i-love-mice" />
    <id>i-love-mice</id>
    <author>
      <name>Peter Clive</name>
      <author_email>[email protected]</author_email>
    </author>
    <updated>2005-12-11T16:23:51Z</updated>
    <summary>Ever since I bought my first mouse, I can't live without one.</summary>
  </entry>
 
  <entry>
    <title>A mouse is better than a fish</title>
    <link href="http://www.myblog.com/permalink/a-mouse-is-better-than-a-fish" />
    <id>a-mouse-is-better-than-a-fish</id>
    <author>
      <name>Bob Walter</name>
      <author_email>[email protected]</author_email>
    </author>
    <updated>2005-12-09T09:11:42Z</updated>
    <summary>I had a fish for four years, and now I'm sick. They smell.</summary>
  </entry>
 
</feed>

Installation du plug-in

Symfony fournit le plugin sfFeed qui permet d'automatiser la génération de flux. Pour l'installer, tapez la ligne de commande suivante:

$ symfony plugin-install http://plugins.symfony-project.com/sfFeedPlugin

Les classes du plugin seront installé dans le répertoire plugins/. Afin que symfony les charge automatiquement pendant l'exécution, le cache doit être vidé:

$ symfony cc

Pour plus d'informations sur les plugins, sur la manière dont elles étendent le framework et comment les réutiliser au sein d'autres projets, référez-vous au Chapitre 17 du livre.

Construction manuelle du flux

Dans le module feed, créez une action lastPosts :

public function executeLastPosts()
{
  $feed = sfFeed::newInstance('atom1');
 
  $feed->setTitle('The mouse blog');
  $feed->setLink('http://www.myblog.com/');
  $feed->setAuthorEmail('[email protected]');
  $feed->setAuthorName('Peter Clive');
 
  $c = new Criteria;
  $c->addDescendingOrderByColumn(PostPeer::CREATED_AT);
  $c->setLimit(5);
  $posts = PostPeer::doSelect($c);
 
  foreach ($posts as $post)
  {
    $item = new sfFeedItem();
    $item->setFeedTitle($post->getTitle());
    $item->setFeedLink('@permalink?stripped_title='.$post->getStrippedTitle());
    $item->setFeedAuthorName($post->getAuthor()->getName());
    $item->setFeedAuthorEmail($post->getAuthor()->getEmail());
    $item->setFeedPubdate($post->getCreatedAt('U'));
    $item->setFeedUniqueId($post->getStrippedTitle());
    $item->setFeedDescription($post->getDescription());
 
    $feed->addItem($item);
  }
 
  $this->feed = $feed;
}

La méthode construit une instance de la classe sfFeed au format Atom. Les classes complémentaires sfFeed et sfFeedItem se chargent de la construction du flux. À la fin de l'exécution de l’action, la variable $feed contient une instance de ‘sfFeed’, qui contient elle-même plusieurs instances de sfFeedItem. Afin de transformer l'objet en flux Atom valide, le template lastPostsSuccess.php contient simplement la ligne suivante:

<?php echo $feed->getFeed() ?>

Le template ne doit pas être décoré par un layout. De plus, le Content-type de la page doit être déclaré en text/xml dans le fichier view.yml situé dans le répertoire config/ du module feed:

all:
  has_layout: off

  http_metas:
    content-type: text/xml

Dans un agrégateur, le résultat de l'action correspond maintenant au flux Atom décrit ci-dessus:

http://www.myblog.com/feed/lastPosts

Utilisation d'un syntaxe plus courte

Vu le nombre important d'informations à définir pour un élément, l'utilisation des mutateurs peut vite devenir rébarbative. Symfony propose une syntaxe abrégée en utilisant un tableau associatif:

public function executeLastPosts()
{
 
  $feed = sfFeed::newInstance('atom1');
 
  $feed->setTitle('The mouse blog');
  $feed->setLink('http://www.myblog.com/');
  $feed->setAuthorEmail('[email protected]');
  $feed->setAuthorName('Peter Clive');
 
  $c = new Criteria;
  $c->addDescendingOrderByColumn(PostPeer::CREATED_AT);
  $c->setLimit(5);
  $posts = PostPeer::doSelect($c);
 
  foreach ($posts as $post)
  {
    $item = array(
      'title'       => $post->getTitle(),
      'link'        => '@permalink?stripped_title='.$post->getStrippedTitle(),
      'authorName'  => $post->getAuthor()->getName(),
      'authorEmail' => $post->getAuthor()->getEmail(),
      'pubdate'     => $post->getCreatedAt(),
      'uniqueId'    => $post->getStrippedTitle(),
      'description' => $post->getDescription(),
    );
 
    $feed->addItemFromArray($item);
  }
 
  $this->feed = $feed;
}

L'effet produit est exactement le même. La syntaxe, elle, est bien plus claire.

Laissez symfony travailler pour vous !

Les méthodes utilisées pour construire les champs étant toujours quasiment identiques, symfony peut produire le même résultat juste avec le code suivant:

public function executeLastPosts()
{
 
  $feed = sfFeed::newInstance('atom1');
 
  $feed->setTitle('The mouse blog');
  $feed->setLink('http://www.myblog.com/');
  $feed->setAuthorEmail('[email protected]');
  $feed->setAuthorName('Peter Clive');
 
  $c = new Criteria;
  $c->addDescendingOrderByColumn(PostPeer::CREATED_AT);
  $c->setLimit(5);
  $posts = PostPeer::doSelect($c);
 
  $feed->setFeedItemsRouteName('@permalink');
  $feed->setItems($posts);
 
  $this->feed = $feed;
}

Magique n'est-ce-pas ?

La magie de l'objet sfFeed

Les noms des accesseurs de l'objet Post sont suffisamment explicites pour que symfony les comprenne. La classe sfFeed contient des mécanismes internes pour extraire les informations pertinentes des classes bien structurées :

  • Pour définir le champs titre de l'élément, la classe essaye d'appeler une de ces méthodes : getFeedTitle(), getTile(), getName() ou __toString().

    Dans cet exemple, l'objet Post a bien une méthode getName().

  • Pour définir le champs link, la classe cherche une route vers un element (définie par un appel de setFeedItemsRouteName()). Si une route est présente, la classe cherche dans l'URL des paramètres pour lesquelles il existe des accesseurs. Si aucune route n'est définie, la classe cherche alors à appeler une de ces méthodes : getFeedLink(), getLink() ou getUrl().

    Dans cet exemple, une route est définie dans l'action (@permalink). Cette règle de routage contient un paramètre :stripped_title, et l'objet Post a bien une méthode getStripped_Title(). L'objet sfFeed sait donc définir l'URL du lien.

  • Pour définir l'adresse email de l'auteur, la classe cherche a appeller la méthode getFeedAuthorEmail ou getAuthorEmail. Si aucune de ces méthode n'est définie, elle essaye getAuthor(), getUser() ou getPerson(). Si le résultat est un object, elle essaye d'appeler la méthode getEmail ou getMail de cet objet.

    Dans cet exemple, l'objet Post a une méthode getAuthor(), et l'objet Author a une méthode getName(). Le même genre de règle est utilisé pour le nom de l'auteur, ainsi que son URL.

  • Pour définir la date de publication, la classe cherche à appeler une de ces méthodes : getFeedPubDate(), getPubDate(), getCreatedAt() ou getDate().

    Dans cet exemple, l'objet Post a une méthode getCreatedAt()

Le principe est le même pour les autres champs possibles du flux (catégorie, résumé, identifiant etc...). Vous êtes fortement invité parcourir le code la classe "sfFeed" pour découvrir par vous même tout les mécanismes de déduction.

Les objets Post et Author, de part la construction de leur accesseurs, fournissent nativement les raccourcis nécéssaires à sfFeed, rendant la création d'un flux si simple.

Definition personnalisée des valeurs du flux

Dans la liste ci-dessus, vous noterez que la première méthode que sfFeed essaye d'appeler est toujours sous la forme ‘getFeedXXX()’. Cela vous permet de spécifier une valeur personnalisée pour chaque champ d'un élément en enrichissant simplement le modèle.

Par exemple, si vous ne voulez pas que l'adresse email de l'auteur soit publiée dans le flux, ajoutez simplement une méthode getFeedAuthorEmail() dans l'objet Post:

public function getFeedAuthorEmail()
{
  return '';
}

Cette méthode sera trouvé et utilisé avant getAuthor(), et le flux n'affichera pas les adresses des rédacteurs.

Utilisation d'autres formats

Les méthodes décrites ci-dessous peuvent être transposé à d'autres types de flux RSS. Changez simplement le paramètre de la fabrique de flux:

// RSS 0.91
$feed = sfFeed::newInstance('rssUserland091');
// RSS 2.01
$feed = sfFeed::newInstance('rss201rev2');