Feed RSS con Codeigniter
Un elemento importante a tener hoy en día para un blog es un Feed RSS de las noticias que se vayan publicando. Con esta entrada, intentaré explicar cómo conseguir programar uno en Codeiniter, que es más sencillo de lo que pueda parecer.
Un feed RSS no es más que una página web con un markup característico, diferente del lenguaje XHTML, para que los diferentes lectoress de RSS puedan interpretarlo correctamente. Entonces, el elemento diferenciador clave a programar va a ser nuestra vista. La estructura básica de una página o feed RSS puedes encontrarla por ejemplo aquí:http://en.wikipedia.org/wiki/RSS#Example.
En mi caso, he creado la vista del feed de la siguiente manera:
<?php echo '<?xml version="1.0" encoding="utf-8"?>' . "\n";?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:admin="http://webns.net/mvcb/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title><?=$titulo?></title>
<link><?=$feed_url?></link>
<atom:link href="<?=$feed_url?>/feed" rel="self" type="application/rss+xml" />
<description><?=$description?></description>
<language>es-es</language>
<pubDate><?=$pubdate?></pubDate>
<sy:updatePeriod>hourly</sy:updatePeriod>
<sy:updateFrequency>1</sy:updateFrequency>
<lastBuildDate><?=$pubdate?></lastBuildDate>
<docs>http://www.rssboard.org/rss-specification</docs>
<generator>Custom dwPalma.com CMS built in Codeigniter</generator>
<managingEditor>joseluis at dwpalma dot com</managingEditor>
<webMaster>joseluis at dwpalma dot com</webMaster>
<?php foreach ($posts as $post): ?>
<item>
<title><?=$post['titulo']?></title>
<link><?=base_url()?>blog/<?=$post['url_categoria']?>/ver/<?=$post['url']?></link>
<comments><?=base_url()?>blog/<?=$post['url_categoria']?>/ver/<?=$post['url']?>#comentarios</comments>
<description><![CDATA[<?=$post['post_intro']?>]]></description>
<pubDate><?=$post['fecha']?></pubDate>
<dc:creator>dwPalma</dc:creator>
<category><![CDATA[<?=$post['categoria']?>]]></category>
<guid isPermaLink="true"><?=base_url()?>blog/<?=$post['url_categoria']?>/ver/<?=$post['url']?></guid>
<content:encoded><![CDATA[<?=$post['post_completo']?>]]></content:encoded>
</item>
<?php endforeach;?>
</channel>
</rss>
<?php
/**
* Archivo ./application/views/feed.php
*/
?>
Es una vista sencilla, aunque conviene tener un par de detalles en cuenta. Por ejemplo la primera línea, podría pensarse que no es necesario mostrarla haciendo un echo en PHP. El problema es que, como empieza con <?xml y es un archivo .php, el intérprete de PHP del servidor puede dar error al mostrarla (por ser <? el método abreviado para abrir una sentencia/archivo en PHP). Después se observa que dentro de las etiquetas description, category y content insertamos los datos entre un <![CDATA[ ]]. Se hace de esta manera para que el lector de RSS interprete correctamente éstos campos y no piense que las diferentes etiquetas que tienen (ya que forman parte del formateado del texto en HTML) corresponden a etiquetas RSS,
A la vista anterior le estamos pasando ciertas variables que, como tiene que ser en Codeigniter, hay hacerlo a través del Controlador, La clase o método para hacerlo no es tampoco muy complicada ya que tan sólo se dedica a cojer unos pocos datos. El constructor del controlador es en realidad más extenso (ya que en mi caso es el mismo que utilizo para todo lo relacionado con el Bog), pero para el caso que nos atañe basta con lo siguiente:
<?php
/**
* Archivo ./application/controllers/blog.php
*/
?>
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Blog extends Controller {
function Blog()
{//Constructor
parent::Controller();
// Cargamos los modelos, librerías, helpers y vistas necesarios
$this->load->helper('url');
$this->load->helper('text');
$this->load->model('Blog_model', 'Blog');
}
function feed()
{
$datos['titulo'] = 'RSS Feed de dwPalma: Desarrollo y Diseño Web en Mallorca';
$datos['feed_url'] = base_url().'blog';
$datos['description'] = $this->config->item('site_description');
$datos['pubdate']= $this->Blog->get_last_post_date();
$datos['posts'] = $this->Blog->get_feed_posts(8);
$this->load->view('feed', $datos);
}
}
?>
Lo último que hace falta ahora son las funciones dentro del modelo blog_model que son las encargadas de obtener la información necesaria (los artículos o posts propiamente dichos) de la Base de Datos:
<?php
/**
* Archivo ./application/models/blog_model.php
*/
?>
<?php
if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Blog_model extends Model
{
function get_last_post_date()
{
$this->db->select('noticias.fecha');
$this->db->from('noticias');
$this->db->where('noticias.estado =', 1);
$this->db->order_by('noticias.id', 'DESC');
$this->db->limit(1);
$query = $this->db->get();
if($query->num_rows() > 0)
{
$result = $query->row();
return $this->convert_datetime($result->fecha);
}
}
function get_feed_posts($numero = null)
{
$this->db->select('noticias.id, noticias.post_intro, noticias.post_completo, noticias.foto, noticias.categoria_id, noticias.fecha, noticias.titulo, noticias.url, noticias.author_id, categorias.url AS url_categoria, categorias.categoria');
$this->db->from('noticias');
$this->db->join('categorias', 'noticias.categoria_id = categorias.id');
$this->db->where('noticias.estado = 1'); //publicada?
$this->db->where('noticias.fecha <=', date('Y-m-d H:i:s'));
$this->db->order_by('fecha', 'DESC');
$this->db->order_by('id', 'DESC');
$this->db->limit($numero);
$query = $this->db->get();
if ($query->num_rows() > 0)
{
$result = $query->result_array();
foreach($result as $key => $post)
{
$result[$key]['fecha'] = $this->convert_datetime($result[$key]['fecha']);
}
return $result;
}
function convert_datetime($mysql_tatetime = null)
{
list($date, $time) = explode(' ', $mysql_tatetime);
list($year, $month, $day) = explode('-', $date);
list($hour, $minute, $second) = explode(':', $time);
$timestamp = mktime($hour, $minute, $second, $month, $day, $year);
return date('r', $timestamp);
}
}
?>
Cabe destacar la función convert_datetime. En la tabla noticias de la Base de Datos guardo las fechas en un campo timestamp propio de MySQL, y por lo tanto es necesario convertir la fecha al formato aceptado por el estándar RSS. Para los curiosos, os voy a dejar también cómo es la estructura de las dos tablas que se necesitan, tal y como las tengo yo:
-- -- Estructura de tabla para la tabla `categorias` -- CREATE TABLE IF NOT EXISTS `categorias` ( `id` int(11) NOT NULL auto_increment, `categoria` varchar(128) character set utf8 collate utf8_spanish_ci NOT NULL, `url` varchar(128) character set utf8 collate utf8_spanish_ci NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ; -- -------------------------------------------------------- -- -- Estructura de tabla para la tabla `noticias` -- CREATE TABLE IF NOT EXISTS `noticias` ( `id` int(11) NOT NULL auto_increment, `categoria_id` int(11) NOT NULL, `fecha` timestamp NOT NULL default CURRENT_TIMESTAMP, `titulo` varchar(128) character set utf8 collate utf8_spanish_ci NOT NULL, `url` varchar(128) character set utf8 collate utf8_spanish_ci NOT NULL, `post_intro` text character set utf8 collate utf8_spanish_ci NOT NULL, `post_completo` text character set utf8 collate utf8_spanish_ci, `foto` varchar(255) character set utf8 collate utf8_spanish_ci default NULL, `estado` tinyint(1) NOT NULL, `allow_comments` tinyint(1) NOT NULL default '1', `author_id` int(11) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ;
Esto es todo. Ahora, si accediésemos a la dirección miweb.com/blog/feed veríamos las últimas 8 entradas(esto es configurable fácilmente) en formato RSS.

En mi caso particular, además envié el feed al popular servicio de feedburner, haciendo así aun más sencillo el poder suscribirse y obteniendo al mismo tiempo estadísticas sobre las suscripciones. Para cualquier duda o sugerencia, los comentarios :)
La verdad que muy bueno, no pensé que fuera tan simple(la teoría) veremos en la práctica al implementar.. :P.
permalinkMuy lindo el sitio, los efectos e interesante lo del boton de previsualizar.