资源说明:Feed manager for reading and writing feeds (unofficial git mirror for my own changes)
` handling in RSS 1 and 2 feeds (based on a patch from Jeff Merlet)
=== 2007-02-22 | 0.9.1 Beta ===
* francois: Added the `sfRss10Feed` class and unit tests
* francois: Changed feed type detection technique to avoid building a simpleXML element twice
=== 2007-02-21 | 0.9.0 Beta ===
* francois: Added more unit tests
* francois: Improved `sfRssFeed` conversions
* francois: Moved `getLatestPostDate()` method to `sfFeed` and unit tested it.
* francois: '''BC break''' Renamed specialized RSS classes to `sfRss201Feed` and `sfRss901Feed`. These classes are not really useful anyway, since all their code was refactored to `sfRssFeed`.
=== 2007-02-19 | 0.8.1 Alpha ===
* francois: Added much more unit tests
* francois: Improved `sfAtom1Feed` conversions
* francois: Added `__toString()` and `initialize()` methods to `sfFeedEnclosure`
* francois: Added `content` property to `sfFeedItem`
* francois: '''BC break''' `fromXML()` methods now expect a string rather than a simpleXML object
* francois: '''BC break''' `sfFeedPeer::createFromObjects()` signature changed ($objects, $parameters = array())
=== 2007-02-18 | 0.8.0 Alpha ===
* francois: Initial release
= sfFeed2 plugin = The `sfFeed2Plugin` offers an object interface for feeds and feed items, feed input methods using a web feed or an array of objects as source, and feed output methods for displaying items on a page and serving feeds through a symfony application. == Possible uses == * serving a RSS/Atom feed based on model objects * Using web feeds as data source * Feed aggregator As compared with the `sfFeedPlugin`, this plugin has a cleaner code separation in classes and offers more features. The syntax differs, but many classes have the same names, therefore the two plugins are not compatible. == Contents == This plugin contains four data structure classes: * `sfFeed` * `sfFeedItem` * `sfFeedImage` * `sfFeedEnclosure` It also contains specific classes containing specific input/output methods based on specific feed formats: * `sfAtom1Feed` * `sfRssFeed` * `sfRss10Feed` * `sfRss201Feed` * `sfRss091Feed` Last but not least, the most important (and smart) class is the feed manager, which contains only static methods: * `sfFeedPeer` Unit tests are available in the SVN repository. == Installation == Installation of the plugin differs on the version of symfony you are using. * Install the plugin (symfony 1.0): {{{ $ symfony plugin-install http://plugins.symfony-project.com/sfFeed2Plugin }}} * Install the plugin (symfony 1.1+): {{{ $ symfony plugin:install sfFeed2Plugin }}} * Alternatively, if you don't have PEAR installed, you can download the latest package attached to this plugin's wiki page and extract it under your project's `plugins/` directory * Clear the cache to enable the autoloading to find the new class {{{ $ symfony cc }}} == Tutorials == === Building a feed from an array of objects === ==== Example data ==== Let's take an example of a simple blog application with a `Post` and an `Author` table: ||''Post'' || ''Author'' ||id || id ||author_id || first_name ||title || last_name ||description || email ||body || ||created_at || The `Post` class is extended by a `getStrippedTitle()` method that transforms the title into a string that can be used in an URI, replacing spaces by dashes, upper case by lower case, and removing all special characters: {{{ 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; } }}} The `Author` class is extended by a custom `->getName()` method as follows: {{{ public function getName() { return $this->getFirstName().' '.$this->getLastName(); } }}} If you need more details about the way to extend the model, refer to [http://www.symfony-project.com/book/trunk/08-Inside-the-Model-Layer#Extending%20the%20Model Chapter 8]. The `routing.yml` contains the following rule: {{{ post: url: /permalink/:stripped_title param: { module: post, action: read } }}} If you need more details about the routing system, refer to [http://www.symfony-project.com/book/trunk/09-Links-and-the-Routing-System Chapter 9]. A special `feed` module is built for the occasion, and all the actions and templates will be placed in it. {{{$ symfony init-module myapp feed}}} ==== Expected result ==== The feed action has to output an [http://en.wikipedia.org/wiki/Atom_%28standard%29 Atom] feed. As a reminder of all the information that need to be included in an Atom feed, here is an example: {{{}}} ==== Using the creators and setters ==== To build the feed, you need to initialize it with a certain format and options, and to add feed items based on the objects resulting from a database request. With the syntax of the `sfFeed` and `sfFeedItem` class, that would give: {{{ 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'); $feedImage = new sfFeedImage(); $feedImage->setFavicon('http://www.myblog.com/favicon.ico'); $feed->setImage($feedImage); $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; } }}} At the end of the action, the `$feed` variable contains a `sfAtom1Feed` object which includes several `sfFeedItem` objects. To transform the object into an actual Atom feed, the `lastPostsSuccess.php` template simply contains: {{{ asXml(ESC_RAW) ?> }}} The content type is automatically set by the `asXML()` method, depending on the feed format (Atom1 in this example). When called from a feed aggregator, the result of the action is now exactly the Atom feed described above: {{{http://www.myblog.com/feed/lastPosts}}} ==== Using the `initialize()` method ==== The use of all the setters for the feed and item construction can be a little annoying, since there is a lot of information to define. Both the `sfFeed` and the `sfFeedItem` classes provide an `initialize()` method that uses an associative array for a shorter syntax: {{{ 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; } }}} It has exactly the same effect as the previous listing, but the syntax is clearer. ==== Using the object converter ==== As the method names that are used to build a feed item based on an object are more or less always the same, the `sfFeedPeer` can try to do it on its own: {{{ 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; } }}} The rules governing the `sfFeedPeer::convertObjectsToItems` algorithm are as follows: * To set the item `title`, it looks for a `getFeedTitle()`, a `getTitle()`, a `getName()` or a `__toString()` method. In the example, the `Post` object has a `getName()` method. * To set the `link`, it uses the `routeName` option if defined in the second argument of the method call. It is supposed to be a route name for the feed items. If present, the method looks in the route url for parameters for which it could find a getter in the object methods. If not, it looks for a `getFeedLink()`, `getLink()`, `getUrl()` method in the object. In the example, the route name given as parameter is `@permalink`. The routing rule contains a `:stripped_title` parameter and the `Post` object has a `getStrippedTitle()` method, so the `convertObjectsToItems` method is able to define the URIs to link to. * To set the author's email, it looks for a `getFeedAuthorEmail` or a `getAuthorEmail`. If there is no such method, it looks for a `getAuthor()`, `getUser()` or `getPerson()` method. If the result returned is an object, it looks in this object for a `getEmail` or a `getMail` method. In the example, the `Post` object has a `getAuthor()`, and the `Author` object has a `getName()`. The same kind of rules is used for the author's name and URL. * To set the publication date, it looks for a `getFeedPubdate()`, `getPubdate()`, `getCreatedAt()` or a `getDate()` method. In the example, the `Post` object has a `getCreatedAt` The same goes for the other possible fields of an Atom feed (including the categories, the summary, the unique id, etc.), and you are advised to [browse the source of the `sfFeed` class](http://www.symfony-project.com/trac/browser/plugins/sfFeed2Plugin/lib) to discover all the deduction algorithms. All in all, the way the accessors of the `Post` and `Author` objects are built allow the built-in algorithm of the `convertObjectsToItems` method to work, and the creation of the feed to be more simple. ==== Defining custom values for the feed ==== In the list of rules presented above, you can see that the first method name that the `sfFeed` object looks for is always a `getFeedXXX()`. This allows you to specify a custom value for each of the fields of a feed item by simply extended the model. For instance, if you don't want the author's email to be published in the feed, just add the following `getFeedAuthorEmail()` method to the `Post` object: {{{ public function getFeedAuthorEmail() { return ''; } }}} This method will be found before the `getAuthor()` method, and the feed will not disclose the publishers' email addresses. The other way to use a specific method for an item property is to pass a `methods` option to the `convertObjectsToItems` method: an associative array associating item properties with object methods. So, for instance, to tell the converter to use nothing for the feed author email and the `getUserFirstName()` method for the author name, you could write: {{{ $postItems = sfFeedPeer::convertObjectsToItems($posts, array( 'routeName' => '@permalink', 'methods' => array( 'authorEmail' => '', 'authorName' => 'getUserFirstName' ) )); }}} You can also pass some arguments to the user defined method by passing an array composed of the method name and an array of arguments: {{{ $postItems = sfFeedPeer::convertObjectsToItems($posts, array( 'routeName' => '@permalink', 'methods' => array( 'authorEmail' => '', 'authorName' => 'getUserFirstName', 'pubdate' => array('getPublishedAtDate', array('U')), ) )); }}} ==== Using the sfFeedPeer static methods ==== The `sfFeedPeer` class offer helper methods that facilitate the creation and population of feed items. When the feed format is determined at runtime, create feed objects using the `sfFeedPeer::newInstance()` method, which is a factory, rather that using the `new` command: {{{ $feed = sfFeedPeer::newInstance('atom1'); // same as $feed = new sfAtom1Feed(); }}} The steps described in the `executeLastPosts` listing occur in almost every feed construction process, so the `sfFeedPeer` can reduce the code above to a simpler: {{{ 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') ) ); } }}} ==== Using other formats ==== The methods described below can be transposed to build other RSS feeds. Simply change the parameter given to the feed 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'); }}} === Fetching a feed from the web and displaying it === You may want to display the latest posts of the symfony users group in your application. The steps to retrieve this information are to fetche a feed from the Internet, create an empty feed object, and populate it with the items of the feed. You can use the `fromXML()` method and the [http://www.symfony-project.com/trac/wiki/sfWebBrowserPlugin sfWebBrowserPlugin] for that: {{{ 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($uri); $feed->fromXml($feedString); $this->feed = $feed; } }}} Thanks to the `sfFeedPeer` shortcuts, this can be reduced to a single line: {{{ public function executeLastPosts() { $this->feed = sfFeedPeer::createFromWeb('http://groups.google.com/group/symfony-users/feed/rss_v2_0_msgs.xml'); } }}} The `createFromWeb()` method first parse the response and tries to recognize a known feed format (RSS or Atom - The recognized formats are the same ones as above). Note that this method depends on the `sfWebBrowserPlugin`, so this plugin must be installed to make the method work. Once the feed is built, it is very easy to use it for the display in the template: {{{ The mouse blog 2005-12-11T16:23:51Z Peter Clive pclive@myblog.com 4543D55FF756G734 http://www.myblog.com/favicon.ico I love mice i-love-mice Peter Clive pclive@myblog.com 2005-12-11T16:23:51Z Ever since I bought my first mouse, I can't live without one. A mouse is better than a fish a-mouse-is-better-than-a-fish Bob Walter bwalter@myblog.com 2005-12-09T09:11:42Z I had a fish for four years, and now I'm sick. They smell. Latests posts from the mailing-list
-
getItems() as $post): ?>
- getPubDate(), 'd/MM H:mm') ?> - getTitle(), $post->getLink()) ?> by getAuthorName() ?>
本源码包内暂不包含可直接显示的源代码文件,请下载源码包。