sfFeed2 plugin – le rss fitn
http://www.symfony-project.org/plugins/sfFeed2Plugin
Le plugin sfFeed2Plugin met à disposition une interface objet pour gérer des flux d’informations, des méthodes d’alimentation utilisant des flux d’informations existants ou un tableau d’objet comme source, et des méthodes de sorties pour afficher les informations sur une page et les sevrir au sein d’une application symfony
Usages possibles
- mettre à disposition un flux RSS/Atom feed basé sur objet
- Utiliser des flux RSS comme sour
- agréger des flux d’informaitons
Par rapport à sfFeedPlugin, ce plugin a une meilleure séparation du code et offre plus de fonctionnalité. La syntaxe diffère, mais beaucoup de classes ont le même nom, de toute façon ces deux plugins ne sont pas compatibles.
contenu
Ce plugin contient 3 classes de structure de données
- sfFeed
- sfFeedItem
- sfFeedEnclosure
Il possède aussi des classes spécifiques d’entrée/sortie , ainsi que des méthodes basées sur différents format d’alimentation
- sfAtom1Feed
- sfRssFeed
- sfRss10Feed
- sfRss201Feed
- sfRss091Feed
Enfin, enfin la classe la plus importante est la classe de gestion de flux, qui contient une seule méthode statique:
- sfFeedPeer
Les tests unitaires sont disponibles sur le dépot SVN
Installation
Installer le plugin
symfony plugin-install http://plugins.symfony-project.com/sfFeed2Plugin
Si vous n’avez pas PEAR, vous pouvez télécharger la dernière archive sur la page wiki de ce plugin et l’extraire dans le répertoire plugins.
Nettoyer le cache pour permettre au chargement automatique de trouver lkes nouvelles classes
symfony cc
Tutorials
Construire un flux à partir d’un tableau d’objets
exemple de données
Soient un exemple de blog simple avec une table post et une table author
||Post || Author ||id || id ||author_id || first_name ||title || last_name ||description || email ||body || ||created_at ||
La classe Post est étendue par une méthode getSTrippedTitle() qui transforme le titre en une chaîne de caractère qui peut être utilisée dans une URI, en remplaçant les espaces par des tirets, les majuscules par des minuscules, et en enlevant les caractères spéciaux.
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 class Author elle est étendue par une méthode ->getBame() comme suit:
public function getName()
{
return $this->getFirstName().' '.$this->getLastName()
}
Pour plus de documentation sur comment étendre un modèle, référez vous au chapitre 8.
le fichier routing.yml contient les règles suivantes
post:
url: /permalink/:stripped_title
param: { module: post, action: read }
Pour plus de détail sur le système de routage référez vous au chapitre 9.
Un module dédié au flux va être créé pour l’occasion, et toutes les actions et les templates seront placés à l’intérieur.
symfony init-module myapp feed
Résultat attendu
L’action feed doit donner un flux Atom en sortie. Pour se remémorer les informations nécessaires à un flus Atom, voici un exemple
<?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>pclive@myblog.com</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>pclive@myblog.com</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>bwalter@myblog.com</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>
Création et configuration
Pour construire le flux, v ous devez l’initialiser avec un certain format et différentes options, et ajouter des éléments aux flus basés sur des objets issus de requêtes à la base de données. avec la syntaxe des classe sfFeed et de sfFeedItems, ca donnerait:
public function executeLastPosts()
{
$feed = new sfAtom1Feed();
$feed->setTitle('The mouse blog');
$feed->setLink('http://www.myblog.com/');
$feed->setAuthorEmail('pclive@myblog.com');
$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->setTitle($post->getTitle());
$item->setLink('@permalink?stripped_title='.$post->getStrippedTitle());
$item->setAuthorName($post->getAuthor()->getName());
$item->setAuthorEmail($post->getAuthor()->getEmail());
$item->setPubdate($post->getCreatedAt('U'));
$item->setUniqueId($post->getStrippedTitle());
$item->setDescription($post->getDescription());
$feed->addItem($item);
}
$this->feed = $feed;
}
à la fin de l’action, la variable ^feed contient un objet sfAtom1Feed qui intègre des objets sfFeedItem. Pour trnasformer l’obet en un flux Atom, le template lastPostSuccess.php doit simplement contenir:
<?php decorate_with(false) ?> <?php echo $feed->asXml() ?>
Le type de contenu est automatiquement défini par la méthode asXML(), qui dépend du format voulu (Atom1 dans notre exemple)
Quand elle est appelé d’un lecteur de flux, le résultat de l’action est maintenant un flus Atom comme décrit ci après:
http://www.myblog.com/feed/lastPosts
Utilisation de la méthode initialize()
L’utilisation de tous les paramètres pour le flux et ces éléments peut être assez pénible, attendu qu’il y a beaucoup d’informations à fournir. C’est pourquoi les deux classe sfFeed et sfFeedItems possèdent une méthode initialize() qui utilise un tableau associatif pour une syntaxe plus concise:
public function executeLastPosts()
{
$feed = new sfAtom1Feed();
$feed->initialize(array(
'title' => 'The mouse blog',
'link' => 'http://www.myblog.com/',
'authorEmail' => 'pclive@myblog.com',
'authorName' => '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->initialize(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->addItem($item);
}
$this->feed = $feed;
}
L’effet produit est exactement le même que précédemment, mais la syntaxe est plus claire.
Utilisation du convertisseur d’objets
Attendu que les noms des méthodes qui construisent les éléments issus d’un objet sont toujours plus ou moins les mêmes, la classe sfFeedPeer va essayer de les deviner:
public function executeLastPosts()
{
$feed = new sfAtom1Feed();
$feed->initialize(array(
'title' => 'The mouse blog',
'link' => 'http://www.myblog.com/',
'authorEmail' => 'pclive@myblog.com',
'authorName' => 'Peter Clive'
));
$c = new Criteria;
$c->addDescendingOrderByColumn(PostPeer::CREATED_AT);
$c->setLimit(5);
$posts = PostPeer::doSelect($c);
$postItems = sfFeedPeer::convertObjectsToItems($posts, array('routeName' => '@permalink'))
$feed->addItems($postItems);
$this->feed = $feed;
}
- La logique de l’algorythme de sfFeedPeer::convertObjectsToItems est la suivante:
- Pour définir un élément titre, elle vérifie l’éxistence des méthodes getFeedTitle(), getTitle(), getName(), __toString(). Dans l’exemple l’objet Post possède une méthode getName().
- Pour définir un lien, elle utilise l’option routeName si elle est définie comme second argument lors de ‘lappel de la méthode. Elle est sensée être une règle de routage pour l’élément du flux. S’il y en a une, la méthode regarde dans les règle de routage les paramètres nécessaires. S’il n’y en a pas elle vérifie l’éxistence des méthodes getFeedLink(), getLink(), getUrl(). Dans l’exemple, la règle de routage donnée en paramètre est @permalink. La règle de routage nécessite donc : un paramètre stripped_title et l’objet Post a une méthode getStrippedTitle(), donc la méthode convertObjectsToItems est capable de définir les URI pour générer les liens.
- Pour définir l’email de l’auteur, elle vérifie l’existence des méthodes getFeedAuthorEmail ou a getAuthorEmail. Si ces méthodes n’existent pas elle vérifie la présence des méthodes getAuthor(), getUser() ou getPerson(). Si le résultat retournait est un objet, elle vérifie la présence des méthodes getEmail ou getMail. Dans l’exemple, l’objet Post a une méthode getAuthor(), et l’objet Author une méthode getNAme(). Le même genre de règle est utilisée pour le nom de l’auteur et son URL.
- Pour définir une date de publication, elle vérifie la présence des méthodes getFeedPubdate(), getPubdate(), getCreatedAt() ou getDate(). Dans l’exemple, l’objet Post a une méthode getCreatedAt()
C’est la même chose pour tous les autres champs possible d’un flus Atom (les catégories, le résumé, l’identifiant unique, etc.), et vous pouvez jeter un oeil au code de la classe sfFeed (http://www.symfony-project.com/trac/browser/plugins/sfFeed2Plugin/lib) pour approfondir les algorythmes de déduction.
L’un dans l’autre, la façon dont les accesseurs des objets Posts et Author sont construits permettent à l’algorithme de la méthode convertObjectsToItems de fonctionner, et à la création du flux d’être plus simple.
Définir des valeurs personnalisées pour le flux
Dans la liste des règles présentées ci dessu, vous pouvez voir que le premier nom de méthode que l’objet sfFeed vérifie est toujours getFeedXXX(). Cela permet de spécifier une valeur personnalisée pour chacun des champs des éléments du flux par une simple extension de modèle.
Par exemple, si vous ne voulez pas que l’email de l’auteur soit publié dans le flux, ajoutez simplement la méthode getFeedAuthorEmail() à l’objet Post:
public function getFeedAuthorEmail()
{
return *;
}
Cette méthode sera trouvée avant la méthode getAuthor(), et le flux ne mentionnera pas l’adresse mail.
L’autre façon d’utiliser une méthode personnalisée pour un élément de flux est de passer un paramètre à la méthode convertObjectsToItems: un tableau associatif associant les propriétés de l’élément aux méthodes de l’objet. Donc, par exemple, pour dire au convertisseur de ne rien utiliser pour l’email de l’auteuret la méthode getUserFirstName() pour le nom de l’auteur, vous pouvez écrire:
$postItems = sfFeedPeer::convertObjectsToItems($posts, array( 'routeName' => '@permalink', 'methods' => array( 'authorEmail' => *, 'authorName' => 'getUserFirstName' ) ));
Utilisation de la méthode statique sfFeedPeer
La classe sfFeedPeer met à disposition des helpers qui facilite la creation et la population des flux.
Une fois que le format est défini, crééer des objets (éléments de flux) en utilisant la méthode sfFeedPeer::newInstance(), qui est une factory, plutot que du’tiliser uyne nouvelle commande:
$feed = sfFeedPeer::newInstance('atom1');
// same as
$feed = new sfAtom1Feed();
Les étapes décrites dans le code de executeLastPosts apparaitront dans toute construciton de flux, donc sfFeedPeer permet de réduire le code à un simple:
public function executeLastPosts()
{
$c = new Criteria;
$c->addDescendingOrderByColumn(PostPeer::CREATED_AT);
$c->setLimit(5);
$posts = PostPeer::doSelect($c);
$this->feed = sfFeedPeer::createFromObjects(
$posts,
array(
'format' => 'atom1',
'title' => 'The mouse blog',
'link' => 'http://www.myblog.com/',
'authorEmail' => 'pclive@myblog.com',
'authorName' => 'Peter Clive'
'routeName' => '@permalink',
'methods' => array('authorEmail' => '', 'authorName' => 'getUserFirstName')
)
);
}
Utilisation des autres formats
Les méthodes décrites ci dessus peuvent être transposées pour la construction d’autres flux RSS. Il faut simplement changer le paramètre passé à la factory:
// Atom 1
$feed = sfFeedPeer::newInstance('atom1');
// RSS 1.0 RDF Site Summary
$feed = sfFeedPeer::newInstance('rss10');
// RSS 0.91 Userland
$feed = sfFeedPeer::newInstance('rss091');
// RSS 2.01 rev6
$feed = sfFeedPeer::newInstance('rss201');
Parcourir un flux provenant du web et l’afficher
Vous allez vouloir afficher les derniers posts des utilisateurs du groupe symfony dans votre application. Les étapes pour faire ceci sont parcourir le flux en provenance du web, créer un objet feed vide, et le peupler avec des éléments du flux. Vous pouvez utiliser la méthode fromXML() de sfWebBrowserPlugin comme ceci:
public function executeLastPosts()
{
$uri = 'http://groups.google.com/group/symfony-users/feed/rss_v2_0_msgs.xml';
$browser = new sfWebBrowser(array(
'user_agent' => 'sfFeedReader/0.9',
'timeout' => 5
));
$feedString = $browser->get($uri)->getResponseText();
$feed = new sfRssFeed();
$feed->setUrl($feedString);
$feed->fromXml($uri);
$this->feed = $feed;
}
Merci au raccourcie de sfFeedPeer, qui permet de reéduire le tout à une seule ligne:
public function executeLastPosts()
{
$this->feed = sfFeedPeer::createFromWeb('http://groups.google.com/group/symfony-users/feed/rss_v2_0_msgs.xml');
}
La méthode createFromWeb() commence par parser la réponse et tente de reconnaître un format RSS connu (RSS ou Atom – les formats de reconnaissance sont les mêmes que cités précédemment). Notez bien que cette méthode dépend de sfWebBrowserPlugin, donc ce plugin doit être installé pour que la méthode fonctionne.
Une fois que le flux est construit, il est trés facile de l’afficher dans un template:
<h2>Latests posts from the mailing-list</h2> <ul> <?php foreach($feed->getItems() as $post): ?> <li> <?php echo format_date($post->getPubDate(), 'd/MM H:mm') ?> - <?php echo link_to($post->getTitle(), $post->getLink()) ?> by <?php echo $post->getAuthorName() ?> </li> <?php endforeach; ?> </ul>
Agréger différents flux
La classe sfFeedPeer contient une méthode aggrégate(), qui mélange différents flux et réordonne les éléments par ordre chronologique. L’utilisation est très simple: il y a juste un tableau de flux à passer en paramètre, et vous allez obtenir un nouvel objet feed (un flux) avec tous les éléments à l’intérieur.
Par exemple, voici comment vous pourriez afficher un flux de 10 posts peuplé avec les derniers posts provenant à la fois du groupe des utilisateurs et à la fois du groupe des développeur
public function executeLastPosts()
{
$feed1 = sfFeedPeer::createFromWeb('http://groups.google.com/group/symfony-users/feed/rss_v2_0_msgs.xml');
$feed2 = sfFeedPeer::createFromWeb('http://groups.google.com/group/symfony-devs/feed/rss_v2_0_msgs.xml');
$this->feed = sfFeedPeer::aggregate(array($feed1, $feed2), array('limit' => 10));
}

