Más con Symfony

12.2. Creando un primer proyecto con sfFacebookConnectPlugin

12.2.1. Crear la aplicación Facebook

Para crear la aplicación es necesario disponer en primer lugar de una cuenta de Facebook con la aplicación "Developer" instalada. La única información necesaria para crear la aplicación es su nombre. Una vez creada, ya no es necesario configurar nada más.

12.2.2. Instalar y configurar sfFacebookConnectPlugin

El siguiente paso consiste en relacionar los usuarios de Facebook con los usuarios de sfGuard. Esta es la principal finalidad del plugin sfFacebookConnectPlugin creado por Fabrice Bernhard y que incluye las contribuciones de muchos otros programadores de Symfony. Después de instalar el plugin, es obligatorio configurarlo correctamente para poder usarlo. Para ello, añade en el archivo de configuración app.yml la clave de la API y el ID y el secreto de la aplicación:

# default values
all:
  facebook:
    api_key: xxx
    api_secret: xxx
    api_id: xxx
    redirect_after_connect: false
    redirect_after_connect_url: ''
    connect_signin_url: 'sfFacebookConnectAuth/signin'
    app_url: '/my-app'
    guard_adapter: ~
    js_framework: none # none, jQuery or prototype.

  sf_guard_plugin:
    profile_class: sfGuardUserProfile
    profile_field_name: user_id
    profile_facebook_uid_name: facebook_uid # WARNING this column must be of type varchar! 100000398093902 is a valid uid for example!
    profile_email_name: email
    profile_email_hash_name: email_hash

  facebook_connect:
    load_routing:     true
    user_permissions: []

Nota Si utilizas una versión antigua de Symfony, no olvides establecer la opción load_routing a false, ya que utiliza el nuevo sistema de enrutamiento.

12.2.3. Configurar una aplicación Facebook

Si el proyecto es una aplicación Facebook, el otro parámetro importante que se debe configurar es app_url que apunta a la ruta relativa de la aplicación en Facebook. Si por ejemplo la aplicación se puede acceder en http://apps.facebook.com/my-app el valor del parámetro app_url debería ser /my-app.

12.2.4. Configurar un sitio web con Facebook Connect

Si el proyecto es un sitio web con Facebook Connect, es habitual dejar los valores por defecto en el resto de las opciones:

  • redirect_after_connect permite modificar el comportamiento que se produce después de pulsar el botón "Connect with Facebook". Por defecto el plugin reproduce el comportamiento de sfGuardPlugin tras el registro de un usuario.
  • js_framework se emplea para indicar el framework JavaScript utilizado. Se recomienda utilizar un framework como jQuery en los sitios web con Facebook Connect porque el código JavaScript de Facebook es muy grande y puede causar errores en Internet Explorer 6 si no se carga bien.
  • user_permissions es un array con los permisos que se concederán a los nuevos usuarios de Facebook Connect.

12.2.5. Relacionando sfGuard con Facebook

La relación entre los usuarios de Facebook y los del plugin sfGuardPlugin se realiza mediante una columna llamada facebook_uid en la tabla Profile. El plugin supone que la relación entre sfGuardUser y su perfil se realiza mediante el método getProfile(). Aunque este es el comportamiento por defecto de sfPropelGuardPlugin, en el caso de sfDoctrineGuardPlugin es preciso configurarlo a mano. A continuación se muestra un posible archivo schema.yml:

Para Propel:

sf_guard_user_profile:
  _attributes: { phpName: UserProfile }
  id:
  user_id:            { type: integer, foreignTable: sf_guard_user, foreignReference: id, onDelete: cascade }
  first_name:         { type: varchar, size: 30 }
  last_name:          { type: varchar, size: 30 }
  facebook_uid:       { type: varchar, size: 20 }
  email:              { type: varchar, size: 255 }
  email_hash:         { type: varchar, size: 255 }
  _uniques:
    facebook_uid_index: [facebook_uid]
    email_index:        [email]
    email_hash_index:   [email_hash]

Para Doctrine:

sfGuardUserProfile:
  tableName:     sf_guard_user_profile
  columns:
    user_id:          { type: integer(4), notnull: true }
    first_name:       { type: string(30) }
    last_name:        { type: string(30) }
    facebook_uid:     { type: string(20) }
    email:            { type: string(255) }
    email_hash:       { type: string(255) }
  indexes:
    facebook_uid_index:
      fields: [facebook_uid]
      unique: true
    email_index:
      fields: [email]
      unique: true
    email_hash_index:
      fields: [email_hash]
      unique: true
  relations:
    sfGuardUser:
      type: one
      foreignType: one
      class: sfGuardUser
      local: user_id
      foreign: id
      onDelete: cascade
      foreignAlias: Profile

Nota ¿Qué sucede si el proyecto utiliza Doctrine y el valor de la opción foreignAlias no es Profile. En ese caso, el plugin simplemente no funciona. Afortunadamente, el problema se puede resolver añadiendo un método getProfile() sencillo en la clase sfGuardUser.class.php y que apunte a la tabla Profile.

También es importante que la columna facebook_uid sea de tipo varchar, ya que los nuevos perfiles de Facebook tienen uids con valores superiores a 10^15. Resulta más sencillo utilizar una columna de tipo varchar con un índice asociado en vez de intentar hacer funcionar las columnas de tipo bigint con los diferentes ORM.

Las otras dos columnas (email y email_hash) son menos importantes y sólo son necesarias en el caso de los sitios web con Facebook Connect que ya tenían usuarios registrados previamente. En ese caso, Facebook realia un proceso un poco complicado para tratar de asociar las cuentas existentes con las nuevas cuentas de Facebook mediante el hash de un email. El plugin sfFacebookConnectPlugin facilita este proceso con una de las tareas que incluye, tal y como se describe al final de este capítulo.

12.2.6. Symfony evita el problema de elegir entre FBML y XFBML

Ahora que ya está todo preparado, es posible empezar a programar la aplicación. Facebook ofrece muchas etiquetas especiales que permiten mostrar funcionalidades completas, como un formulario para invitar a amigos o un sistema completo de comentarios. Estas etiquetas se denominan FBML o XFBML. Los dos tipos de etiquetas son muy similares y la elección depende de si la aplicación se muestra dentro de Facebook o no. Si el proyecto es un sitio web de tipo Facebook Connect, sólo se pueden utilizar las etiquetas XFBML. Si se trata de una aplicación Facebook se pueden seleccionar cualquiera de las dos opciones:

  • Si se embebe la aplicación dentro de un <iframe> de la página de la aplicación de Facebook, se emplea XFBML.
  • Si dejamos que Facebook embeba la aplicación de forma transparente, se utiliza FBML.

Facebook aconseja a los programadores que embeban sus aplicaciones de forma transparente, por lo que fomenta el uso de las aplicaciones FBML. En efecto, esta estrategia tiene algunas características muy interesantes:

  • No se utiliza ningún <iframe>, que siempre es más complicado de gestionar porque tienes que tener en cuenta si los enlaces de la aplicación deben apuntar al <iframe> o a la ventana contenedora.
  • El servidor de FBML interpreta las etiquetas especiales FBML, por lo que es posible mostrar información privada del usuario sin tener que realizar una comunicación previa con el servidor de Facebook.
  • No es necesario pasar la sesión de Facebook de una página a otra manualmente.

FBML también tiene algunas desventajas importantes:

  • Todos los archivos JavaScript se incluyen desde un sandbox, por lo que no pueden utilizar librerías externas como las de Google Maps, jQuery o cualquier otro sistema de estadísticas que no sea Google Analytics (soportado oficialmente por Facebook).
  • Facebook asegura que es mucho más rápido porque alguna de las peticiones de la API se pueden sustituir por etiquetas FBML. No obstante, si la aplicación es sencilla, resulta mucho más rápido que disponga de su propio sitio web.
  • Resulta más difícil depurar las aplicaciones, sobre todo los errores de tipo 500, que son interceptados por Facebook y se reemplazan por errores estándar.

¿Cuál es entonces la opción recomendada? La buena noticia es que gracias a Symfony y al plugin sfFacebookConnectPlugin, no debes tomar ninguna decisión. Se pueden crear aplicaciones agnósticas cuyo código sirva tanto para las aplicaciones ejecutadas en un <iframe>, como para las aplicaciones embebidas de forma transparente y también para los sitios con Facebook Connect. Esto es posible ya que técnicamente, la principal diferencia entre ellas reside en el layout, que en Symfony se puede modificar fácilmente. Estos son los ejemplos de los dos tipos de layouts:

El layout de una aplicación FBML:

<?php sfConfig::set('sf_web_debug', false); ?>
<fb:title><?php echo sfContext::getInstance()->getResponse()->getTitle() ?></fb:title>
<?php echo $sf_content ?>

El layout de una aplicación XFBML o de tipo Facebook Connect:

<?php use_helper('sfFacebookConnect')?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:fb="http://www.facebook.com/2008/fbml">
  <head>
    <?php include_http_metas() ?>
    <?php include_metas() ?>
    <?php include_title() ?>
    <script type="text/javascript" src="/sfFacebookConnectPlugin/js/animation/animation.js"></script>
  </head>
  <body>
    <?php echo $sf_content ?>
    <?php echo include_facebook_connect_script() ?>
  </body>
</html>

Para alternar entre uno y otro layout, simplemente añade el siguiente código dentro del archivo actions.class.php:

public function preExecute()
{
  if (sfFacebook::isInsideFacebook())
  {
    $this->setLayout('layout_fbml');
  }
  else
  {
    $this->setLayout('layout_connect');
  }
}

Nota Existe una pequeña diferencia entre FBML y XFBML que no se encuentra en el layout: las etiquetas FBML se pueden cerrar y las etiquetas XFBML no se pueden cerrar. Así que sustituye este tipo de etiquetas:

<fb:profile-pic uid="12345" size="normal" width="400" />

por esta otra etiqueta equivalente:

<fb:profile-pic uid="12345" size="normal" width="400"></fb:profile-pic>

Para que este cambio sea efectivo, la aplicación también debe ser configurada como de tipo Facebook Connect dentro de las opciones de la aplicación de Facebook, incluso aunque la aplicación sólo esté pensada para FBML. No obstante, la enorme ventaja de hacerlo es que se puede probar la aplicación de forma local.

Si vas a crear una aplicación de Facebook que utilice etiquetas FBML, algo casi inevitable, la única forma de ver el resultado final es publicar el código de la aplicación y ver cómo muestra el resultado Facebook. El uso de Facebook Connect permite utilizar las etiquetas XFBML fuera del sitio web facebook.com y como se ha explicado anteriormente, la única diferencia entre FBML y XFBML es el layout.

Por tanto, la solución presentada permite mostrar las etiquetas FBML de forma local, siempre que dispongas de conexión a Internet. Además, con un entorno de desarrollo accesible desde Internet (como por ejemplo un servidor o un simple ordenador con el puerto 80 abierto) incluso las partes que dependen de la autenticación de Facebook funcionan fuera del dominio facebook.com, gracias al sistema Facebook Connect. De esta forma, puedes probar la aplicación completa antes de subirla a Facebook.

12.2.7. La aplicación sencilla Hola, tu

Si añades el siguiente código en la plantilla de la portada del sitio, ya dispones de una aplicación que muestra el mensaje Hola, [tu nombre]:

<?php $sfGuardUser = sfFacebook::getSfGuardUserByFacebookSession(); ?>
Hello <fb:name uid="<?php echo $sfGuardUser?$sfGuardUser->getProfile()->getFacebookUid():'' ?>"></fb:name>

El plugin sfFacebookConnectPlugin convierte automáticamente a cualquier usuario que sea miembro de Facebook en un usuario de tipo sfGuard. Así es muy sencillo integrar Facebook con el código Symfony existente que haga uso del plugin sfGuardPlugin.