lunes, 11 de junio de 2007

Crear un feed RSS

Me llevó todo el domingo intentar desentrañar el pequeño misterio de generar feeds RSS con CakePHP a partir de este artículo de Nate, que lo explica, pero no demasiado.

La gracia es que acabé consiguiéndolo a medias: el feed salía pero no podía controlarlo a mi gusto (¿dónde c... se pone el channel?). Más tarde (a punto de ponerme a dormir) me di cuenta de que había pasado por alto ver cómo funcionaba la plantilla por defecto para rss, la cual tendría todas las respuestas. Efectivamente. Lo malo es que, acto seguido, voy y me encuentro este breve tutorial de Jiri Kupiainen donde explica todo el proceso. :-(

Ocurrió lo de siempre, te pegas contra la documentación, el código, el API... descubres como se hace algo, y una vez que lo tienes aparecen como por encanto artículos en Google que "no estaban ayer". Lo bueno, es que el aprendizaje por la vía dura suele ser bastante eficaz.

En fin, el caso es que basándome en ambas fuentes, he aquí mi propio tutorial para generar feeds rss... ¡a partir de cualquier modelo!

Paso 1: preparar Cake

Básicamente hay que decirle a CakePHP que queremos parsear url con extensiones, de la forma: /controller/action.rss. Esto es así porque la versión 1.2 de CakePHP puede seleccionar al vuelo diferentes vistas en función de la extensión que le pasemos. Por lo tanto, en un paso posterior crearemos una vista específica para el feed y la pondremos en el sitio adecuado para que la use Cake.

En tu /app/config/routes.php tienes que añadir:

Router::parseExtensions('rss');


Esto le indica a Cake que si se encuentra una URL con la extensión 'rss' busque las vistas en la carpeta /vistas/modelos/rss.

Podemos no especificar ninguna extensión, para que parsee cualquiera, o bien dar una lista limitada de ellas 'rss', 'xml'...

Paso 2: cosas para hacer en el Controller

Hay varias, así que vayamos por partes.

Lo primero es habilitar el uso del componente RequestHandler en el controlador. Aparte de para este tema de los feeds es útil siempre que necesites obtener información sobre la petición que está recibiendo el controlador. Lo segundo es indicar que quieres utilizar el RSSHelper, claro.

var $components = array('RequestHandler');
var $helpers = array ('RSSHelper');


Una cosa opcional: El componente HandleRequest te permite comprobar si el usuario está pidiendo un RSS (u otra cosa) y actuar en consecuencia. Por ejemplo, si es para el feed obtener sólo los últimos 15 items que cumplan ciertas condiciones y si no obtener todos. En cualquier caso, usarías:

if ($this->RequestHandler->prefers('rss')) {
// Cosas para hacer si es rss
} else {
// Cosas para hacer si no lo es
}


Lo segundo es enviarle los datos a la vista del feed (que aún no hemos creado, ¡ojo!). Lo puedes hacer así:


Configure::write('debug', '0');
$data = $this->Paginate ();
$this->set('channel', array ('title' => 'Ejemplo',
'link' => 'http://localhost:8888/micake/',
'description' => 'Frases y citas célebres para que las puedas leer'
));
$this->set ('frases', $data);


Lo explico un poco:

La primera línea es para desactivar el Debug (en el caso de que lo tengas distinto de 0, como ocurre en un entorno de pruebas o de desarrollo). Con Debug, Cake añade cosas a la salida generada y los feeds no validarían de ninguna forma aunque estuviesen bien construidos. Opcionalmente puedes poner un if para controlar si es necesario hacerlo o no.

La segunda línea es para recabar los datos de la manera habitual.

La tercera línea ajusta los valores para el channel. El layout por defecto para feeds rss espera que definas una variable $channel como un array con claves que serán los elementos del channel (como title, link, description y los demás). Si no la defines, CakePHP se busca la vida para poner algunos.

La cuarta línea pasa el array de datos que serán los ítems del modelo. En este caso, la variable es $frases, pero se supone que esto ya sabías hacerlo. Aquí no vamos a hacer nada más.

Paso 3: preparando la vista, o cómo pasar tu modelo al feed

La vista tienes que ponerla en /views/nombre_del_modelo_plural/rss/nombre_de_la_action.ctp

Y el contenido tiene que ser más o menos así:


$items = $rss->items($frases, 'convertirRSS');

echo $items;
function convertirRSS($data) {
return array(
'title' => $data['Frasecita']['frasecita'],
'link' => array('action' => 'view', $data['Frasecita']['id']),
'guid' => array('action' => 'view', $data['Frasecita']['id']),
'description' => $data['Frasecita']['frasecita'],
'author' => $data['Frasecita']['autor'],
'pubDate' => $data['Frasecita']['usado']
);
}


La explicación es como sigue:

Usamos el método rssHelper::items () para convertir el array de datos en elementos items del feed. Para convertir los campos del modelo en los items con sus correspondientes subelementos, usamos una función callback que, en este caso, hemos llamado convertirRSS.

La función tiene que tomar un array asociativo simple, cuyas claves son los nombres de los campos del modelo (y cuyos valores serán los de los diferentes registros, pero de eso se encarga el método items).

La función, por otra parte, tiene que devolver un array cuyas claves sean elementos válidos de item. Tarea nuestra es decidir cómo movemos los datos entre el modelo y los elementos del item.

Algunas observaciones interesantes en el ejemplo. Link y Guid piden url y entienden que se las pasemos como arrays, tal como se explica en el artículo sobre el router.

PubDate, admite campos de tiempo y los prepara en el formato adecuado.

Aparte, mirando el API, creo que hay soporte específico para enclosures, o sea, que el camino para feeds de podcasts está abierto. Aún no lo he investigado.

Paso 4: Ya está, ¿cómo suscribirse?

Pues sí, el feed ya está listo para servir. La URL para suscribirse sería:

http://exemple.com/controller/action.rss

Sustituye lo que haga falta. Supongo que se podrían hacer rutas para que ciertas url se sirvan como feeds.

No hay comentarios: