Sufrimientos del programador

noviembre 10, 2010

Symfony admin custom filter propel

Filed under: desarrollo web — Etiquetas: , , , , — cgcerro @ 12:52 pm

Symfony es un framework cojonudo, yo así lo veo, pero en el caso de admin-generator, esos maravillos CRUD que nos ofrece sin mucho esfuerzo de programación, cuando quieres salirte un poco de lo normal te cuesta dios y ayuda encontar como hacer las cosas. A mi al menos me pasa.

La situación está en cómo poner en un listado de tu admin un filtro específico, como tú quieras.

Como caso practico queremos poner un  filtro que determine si el valor de una columna es null o no (is null, not is null). Como verás, en los filtros que estableces como campos de texto debajo te sale un chekbox is_empty. No parece existir un «not is empty», pero te recomiendo que en este caso puedes elegir los «no vacios» tal y como lo harías en la sentencia SQL. (Con un guión bajo te saca los que no tienen vacío ese campo). Pero en nuestro caso lo que queremos filtrar es el campo de una clave foranea, ejemplo: facturas corresponden a un usuario y pueden tener usuario o no, queremos poder filtrar las facturas que tienen usuario asociado y cuales no.

Yo lo he resuelto así, igual no es lo mejor, no lo se porque no he encontrado un caso similar en la documentación. Ojo, estoy hablando de symfony 1.3, creo recordar que esto se podía hacer fácilmente en symfony 1.1 usando un partial en el filtro y no se qué más.

1.- Primero en lib/model/Facturas.php vamos a crear el métido que determina si la factura tiene usuario o no. Tengo mis dudas de si esto es estrictamente necesario o no, pero no tengo tiempo para liarme y ademas el método me puede venir bien en otros casos.

public function getHasuser(){
  return $this->getUsers()?true:false;
}

2.- En apps/backend/modules/facturas/config/generator.yml declramos el nuevo filtro (que será un select con tres opciones «indiferente», «Si», «No», pero eso lo vemos luego)

filter:
  display:    [.....,hasuser,....]

3.- En lib/filter/FacturasFormFilterClass.php añadimos en el metodo configure lo siguiente (para definir el filtro)

$this->widgetSchema['hasuser'] =
  new sfWidgetFormChoice(array(
  'choices' => array('' => 'indef', 1 => 'si', 0 => 'no')));

$this->validatorSchema['hasuser'] =
  new sfValidatorChoice(array(
  'required' => false,
'choices' => array('', 1, 0)));

4.- Y por último, si eliges sí o no en el filtro, se debe ver reflejado en la query que se ejecuta al filtrar. Para ello, en el mismo fichero lib/filter/FacturasFormFilterClass.php añadimos el siguiente método:

public function addHasuserColumnCriteria(Criteria $criteria, $field, $values)
{
if (!is_array($values))
{
    $values = array($values);
}

if (!count($values))
{
    return;
}

$value = array_pop($values);

switch($value){
case 1:
    $criterion = $criteria->getNewCriterion(FacturasPeer::ID_USER, null,Criteria::ISNOTNULL);
break;
case 0:
    $criterion = $criteria->getNewCriterion(FacturasPeer::ID_USER, null,Criteria::ISNULL);
break;
}

$criteria->add($criterion);
}

Y con eso hemos terminado. Me gustaría explicar un poco más, pero hoy no hay tiempo para eso. Solo espero que con el ejemplo te sirva para resolver tu caso concreto.

May 13, 2010

Mostrar estadísticas Google analytics de tus páginas

Filed under: desarrollo web — Etiquetas: , , — cgcerro @ 9:48 am

Hola a todos de nuevo.

Hace mucho tiempo que no escribo, lo siento, pero la verdad es que estaba esperando a hacer algún desarrollo curiosillo. Para que voy a contar cosas del día a día.

En esta ocasión en masquemedicos.com nos hemos planteado la posibilidad de ofrecer estadísticas de visitas de cada una de las fichas de nuestro portal. Sobra decir que usamos Google Analytics para mantener las estadísticas de acceso a todo nuestro portal, otras alternativas ni nos las hemos planteado.

Deacuerdo, pero para mostrar en tu portal las estadísticas de una url en concreto?

Siempre se suelen barajar alternativas como:

– Mantengo yo una tabla en bbdd donde voy anotando cada acceso: Mejor deja la base de datos para lo que tenga que hacer.

– Pues lo voy anotando en ficheros y luego hago un proceso de la muerte que agrupa todo, lo recalcula y la madre que pario: Mira, te esperan varios años de sufrimiento si haces eso, lo se por experiencia.

– Hay empresas que me ofrecen este servicio: Sí, pero yo lo que conozco es caro, y no está el horno pa bollos.

– Pongo en mis servidores algun cacharro que me mantenga las estadísticas y a lo mejor alguno tiene API: Pues sí, pero mantener estadisticas en tus servidores pues tambien tiene su mantenimiento, y su preocupación, está claro.

Total, que así para los pobres una buena alternativa puede ser obtener los datos de analytics. Y efectivamente parece que se puede hacer: http://code.google.com/intl/es-ES/apis/analytics/docs/gdata/gdataDeveloperGuide.html

Aunque no todo es tan bueno, esto está limitado en cuanto al numero de peticiones que puedes hacer:

  • 10,000 requests per 24 hours (this limit applies to a single web property)
  • 10 requests in any given 1-second period (this limit applies across all queries an application makes to the API)
  • 4 pending requests at any given time (i.e. you must wait until your 1st request completes before making a 5th request) Be advised that this limit is new and is subject to change.
  • A query is also limited to pagination limits of 10,000 entries per feed, with a default response of 1,000 entries

Por ahí verás algunos ejemplos en los que para ver los datos te pide login y password de tu cuenta analytics. Muy bien, pero eso no es lo que queremos. Queremos cojer los datos de nuestra cuenta sin pedirle login a nadie claro. Para eso lo suyo sería establcer la autenticación desde tu propio servidor.

Encuentro por ahí una clase php que te permite hacer esas cosas: http://www.swis.nl/ga/ pues mira que bien, trabajo hecho y no parece que haga nada raro nada más que establecer conexiones con CURL. Bueno, veo que algo raro sí hace que es cachear las peticiones en variables de sesión. Como nosotros usamos memcache, se me ocurre que igual los datos obtenidos los puedo cachear yo mismo en memcache y no usar la caché que plantea este hombre (al que estoy muy agradecido).

La clase es fácil de usar y el ejemplo que viene en su web es bastante claro y funciona. Lo único es saber que parametros indicar para obtener lo que quieres obtener:

print_r($oAnalytics->getData(
                array(   'dimensions' => 'ga:keyword',
                             'metrics'    => 'ga:visits',
                             'sort'       => 'ga:keyword'
                         )
            ));

«metrics» es qué quieres obtener, por ejemplo visitas o páginas vistas.
«dimensions» es cómo vas a dividir la información (por palabra clave, por dias, por meses…)
Una buena forma de entrenarse con estos parámetros la tienes aquí: http://code.google.com/intl/es-ES/apis/analytics/docs/gdata/gdataExplorer.html

Por lo demás, una vez que ya puedes obtener los datos lo suyo es mostrarlos de una forma bonita (gráficos). Yo me he decido por el API de google http://code.google.com/intl/es-ES/apis/charttools/, que está bien explicada, pero hay otras alternativas igual de validas.

Lo de siempre, espero que a alguien le sirva… y si te sirve pon un comentario o algo anda!!!

noviembre 10, 2009

Detectar si StreetView está disponible en una coordenada

Filed under: desarrollo web — Etiquetas: , — cgcerro @ 12:18 pm

Este va a ser un post muy cortito, porque la única dificultad que encontré a la hora de ofrecer streetView en las clínicas de masquemedicos.com fue averiguar como podía saber si streetView estaba disponible en esa coordenada o no, para así pintar el enlace correspondiente que abre el visor si sí está disponible.
Puedes ver un ejemplo aqui: http://masquemedicos.com/psicologo_madrid/jesus-perez-cazorla/ Podía haber elegido otro ejemplo pero la foto de la usuaria que ha opinado sobre esa clínica me pareció graciosa.

En fin que para poder averiguar esto lo único que hay que hacer es:

//Una vez iniciada el API de google maps
 point = new GLatLng(lat,long); //lat y long son las coordenadas que quieres testear
 streetView=new GStreetviewClient();
 streetView.getNearestPanoramaLatLng(point, 
      			function (latlng){
				if ((typeof latlng != 'undefined') && latlng!=null){
                                       //pintar el enlace como corresponda
				}
			} );

Espero que te sirva.

Hasta otra.

octubre 27, 2009

cachear queries en memcache con symfony

Filed under: desarrollo web — Etiquetas: , , — cgcerro @ 6:54 pm

Desde el principio en masquemedicos.com nos hemos preocupado lo mucho por minimizar el numero de queries que se lanzan a la base datos. Nosotros hemos optado por usar memcache, el cual nos permite acceder de forma rápida y fácil a objetos cacheados en memoria.

Nuestro framework Symfony ya cuenta de por sí con integración con memcache, lo que te permite por ejemplo cachear todo o parte del contenido de una página. Para ello te recomiendo la propia documentación de Symfony sobre cache.

En este post vamos a ver cómo cachear cualquier objeto (como objetos propel resultado de una query) mediante la clase sfMemcacheCache, lo cual no es fácil de encontrar en la documentación de Symfony. Por ejemplo veamos el caso práctico en el que dispones en tu modelo de datos de la entidad provincia en la que cada provincia tiene un id y un nombre. Suele ser un caso muy habitual el que accedas muy frecuentemente a esos datos para mostrar por ejemplo un selector <select> de esas provincias.

Vamos a declarar la funcion getCacheProvincias, la cual se encarga de obtener esos datos de memcache en caso de que existan. Si no existen los cogerá de base de datos (propel) y los guardará en cache. Así la proxima vez que se soliciten ya los obtendrá de caché sin consultar la base de datos. No te tomes al pie de la letra la función, es sólo una forma de explicar la manera de proceder.

function getCacheProvincias(){
   //Instancio la clase memcache de symfony. OJO, ni a mi se me ocurriría hacer esto aquí, ya que si usas varias veces esta funcion u otras
   //similares estarías instanciando la clase varias veces lo cual no tiene ningun sentido
    $memcache=new sfMemcacheCache();
    $memcache->initialize();
   //Defino la clave a la que se asociaran los datos de las provincias en memcache
   //Ojo, tiene que ser clave única... aseguraté de no volver a usar "provincias"
  $key=md5("provincias");
   //Cojo los datos de memcache
  $data=$memcache->get($key);
  if ($data){ //Los datos estaban presentes en memcache, asi que basta con deveolverlos deserializados (o como se diga)
    return unserialize($data);
 }else{ //Los datos no están presentes en memcache. Hacemos la consulta a bbdd y los guardamos
   //la consulta a bbdd
   $c = new Criteria();
   $c->add(lo que sea...);
   $provincias = ProvinciasPeer::doSelect($c);
    //la guardamos en memcache. Los objetos hay que serializarlos.
   $memcache->set($key,serialize($provincias),tiempo-de-vida-de-los-dato-en-segundos);
 return $provincias;
 }
}

Pues con eso ya tenemos resulta la forma de coger y guardar datos en memcache, ahora ten presente que si cambia algo en la BBDD (el nombre de una provincia por ejemplo) o bien esperas a que se pase el tiempo de vida que has indicado o te preocupas tu mismo de borrar los datos de memcache. Solo tienes que borrarlos, no regenerarlos porque ya se volverán a generar en la siguiente petición. Para realizar el borrado:

$key=md5("provincias");
$memcache->remove($key);

Por remetar el ejemplo solo indicar que para usarlo solo tienes que imaginar que estas cogiendo los datos igual que si los cogieras de la BBDD con propel, por ejemplo pasándoselos a la vista en el actions:

//en actions;
$this->provincias=getCacheProvincias();
//y en la vista, lo que sea:
echo "<select>";
foreach ($provincias as $provincia){
  echo "<option value=\"".$provincia->getId()."\">".$provincia->getNombre()."</option>";
}

echo "</select>";

Esto es todo por hoy, ya sabes no te tomes el código al pie de la letra, seguro que hay formas mejores, pero si te sirve la idea… eso que te llevas.

 

Abrazos para todos.

 

 

octubre 23, 2009

Explicación de FbConnect

Filed under: desarrollo web — Etiquetas: , , — cgcerro @ 11:42 am

Hace una una semana pasé unos días de desasperación intentándo comprender cómo usar FbConnect, esa API de facebook que te permite que los usuarios «se identifiquen» en tu web con su usuario y contraseña de Facebook. La verdad es que las APIs de Facebook son una herramienta fantástica que te dan muchas posibilidades, pero por contra en mi opinión la documentación no es muy buena y faltan ejemplos claros y sencillos.

Con el fin de que alguien pueda ahorrase unas horas de investigación voy a explicar aquí solo algunas de la funcionalidades que he usado ya que la herramienta de Facebook es bastante extensa.

Primero explicaré las funcionalidades que yo quería cubrir con FBConnect. Se trata de que el usuario pueda enviar una opinión (formulario) asociada a su usuario facebook y que si él lo desea esa opinión se publique automáticamente en su muro de Facebook. En tal caso cada vez que se muestre la opinión junto a esta aparecerá la foto del usuario (foto de Facebook).

Primer paso: Iniciar tu aplicación en Facebook.

Solo tienes que entrar en http://www.facebook.com/developers/createapp.php y dar de alta tu aplicación. Esto puede resultar confuso, porque lo que queremos es integrar nuestra web con Facebook, no crear una aplicación dentro del mismo, pero bueno, hay que hacerlo.

Sigue los pasos que te indican y sobre todo apuntate las claves que te proporcionen (APIKey y SecretKey)

También es importante que si tienes varios subdominios en tu web solo indiques el dominio base (Ej: miweb.com), por lo demás esto no tiene más misterio.

Segundo paso: Preparar tu web

Lo primero es crear un fichero de comunicación entre tu web y Facebook. No es fácil averiguar a priori para que va a servir esto, pero la idea básica es que vas a poner un fichero html en tu web el cual va a ser llamado por Facebook para hacer sabe dios qué cosas (Se intuye que meter cookies en el cliente con tu dominio). El fichero debe llamarse xd_receiver.htm y puedes colgarlo de cualquier directorio, por ejemplo, en /facebook/xd_receiver.htm. El contenido del fichero debe ser como el que sigue:

<!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" >
<body>
  <script src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/XdCommReceiver.js" type="text/javascript"></script>
</body>
</html>

Como ves lo único que hace es incluir un JS que es el que hace esas cosas raras de las que antes hablábamos.

Ojo: Yo no he encontrado la forma de que esto me funcione en un entorno local en el que el fichero xd_receiber.htm se ofrezca realmente. Supongo que es algo lógico, pero si alguien sabe la manera de hacerlo bienvenido sea.

Lo siguiente sería incluir un nuevo XML schema (xmlns:fb=»http://www.facebook.com/2008/fbml) en la cabecera de tus documentos html. Por ejemplo:

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:fb="http://www.facebook.com/2008/fbml">

¿Para qué sirve esto? Pues para posteriormente poder usar en tu código unas etiquetas muy majas que te proporciona facebook como por ejemplo <fb:login-button></fb:login-button> que te muestra el boton de azul de «Conectarte con facebook» o <fb:profile-pic> para mostrar la foto de perfil de un usuario. Si te preguntas cómo es posible que funcione eso, pues resulta que posteriormente cuando inicias la API JavaScript provoca que se parseen esos tags y se sustituyan por lo que corresponde (la imagen o lo que sea). Realmente no es obligatorio indicar ese XML schema si no vas a usar esos tags, y es que hay otras formas de obtener, por ejemplo, la foto de un usuario, pero este método es el más común.

Y por último debes iniciar la API JavaScript en aquellas páginas en las que desees usar cualquier funcionalidad Facebook (Aunque sea para usar los tags mágicos <fb:xxx>) indicando tu APIKey y el path donde se encuentra el fichero xd_receiver.htm.

<script type="text/javascript">
FB.init("TU-NUMERO-DE-API", "/facebook/xd_receiver.htm");
</script>

Tercer paso: Mostrar información del usuario en el formulario.

Como ya he dicho la funcionalidad que queremos conseguir es que el usuario envie un formulario asociando el mismo a su perfil de facebook. Para ello disponemos de un espacio donde vamos a mostrar la información del estado actual del usuario. Si está logado mostraremos su nombre e imagen dándole la opción de deslogarse. Si no lo está mostraremos el botón que le permita logarse en Facebook.

Para pintar el botón de facebook que te permite logarte solo tienes que usar el tag:

<fb:login-button onlogin="putUserInfo()"></fb:login-button>

Donde la putUserInfo será la función JavaScript que se encargará de dibujar la información del usuario una vez se haya logado en Facebook. Si prefieres poner otra imagen o simplemente un enlace puedes hacerlo simplemento añadiédole al evento onclick la función

FB.Connect.requireSession(putUserInfo);

Si el usuario ya está logado no le vamos a mostrar el botón de login, si no que directamente vamos a dibujar la información de su perfil en Facebook. Para ello vamos a modificar la linea dónde hemos iniciado la API del siguiente modo:

FB.init("TU-NUMERO-DE-API", "/facebook/xd_receiver.htm",{"ifUserConnected":putUserInfo});

De este modo, tanto si el usuario termina de logarse, como si ya está logado en el momento de terminar la carga de la página se ejecutará la función putUserInfo.

La funcion putUserInfo debe ser la encargada de dibujar donde corresponda (sustituyendo al botón de login) la infomación del usuario ya logado. Existen varias alternativas para realizar esta función, pero yo me decidí por realizar una petición AJAX a mi servidor en la que se devuelve el src de la imagen y el nombre del usuario logado. Los detalles de implemantación de la función los dejo de tu mano, porque no son relevantes, pero vamos a ver como usar el API php de facebook para obtener la información del usuario. Previamente debes haber descargado el API PHP de facebook en el siguiente link: http://svn.facebook.com/svnroot/platform/clients/packages/facebook-platform.tar.gz

//Instanciar la clase facebook (dependiendo de dónde la hayas descomprimido)
require_once('TU-PATH/facebook-platform/php/facebook.php');
$conector=new Facebook('TU-API-KEY','TU-SECRET-KEY');

//Obtener el identificador facebook del usuario que esté logado es tan fácil como
$idUsuarioLogado=$conector->user;
//Obtener nombre, apellido e imagen del usuario
$usersInfo=$conector->api_client->users_getInfo($idUsuarioLogado, 'last_name, first_name, pic_square_with_logo');
 $user=$usersInfo[0];
//Formo la información que voy a devolver
 $ret=array();
 $ret['facebookId']=$fbid;
 $ret['nombre']=$user['first_name'];
 $ret['apellidos']=$user['last_name'];
 $ret['imagen']=$user['pic_square_with_logo'];
 //Escribo el resultado de la petición AJAX. Como yo uso JSON, pues eso:
  echo json_encode($ret);

Por último a la hora de guardar la información del formulario cuando tu servidor lo recibes recuerda asociarlo al id del usuario de facebook. Para obtener el id del usuario ya sabes que solo tienes que hacer:

$IdFacebook=$conector->user;

Paso 4: Subir información al muro del usuario

Suponemos que en el momento de recibir el formulario que ha enviado el usuario deseamos publicar el contenido en su muro de Facebook. En ese punto es importante destacar que el usuario debe dar un permido especial a tu aplicación para que publique información en su muro. Para ello te recomiendo que en el mismo formulario incluyas un checkbox dónde le pidas permiso con una frase adecuada como «Permitir subir este comentario a mi muro de facebook» y le asocies (onchange) la función del API que le solicitará el permiso adecuado en una ventanita. Si el usuario ya ha dado ese permiso anteriormente la ventanita no se mostrará:

//Comprobar si ya tenemos permiso del usuario o no. Si no los tenemos se los pedimos
FB.Facebook.apiClient.users_hasAppPermission('publish_stream', function(result){
														if (!result){ FB.Connect.showPermissionDialog("publish_stream"); }
													} );

Para subir la el contenido al muro del usuario puedes usar la función del API php «stream_Publish»:

$conector->api_client->stream_publish($mensaje, $attachment, $action_links);

Paso 5: mostrar información del usuario

Suponiendo que has almacenado el idFacebook de cada usuario junto con la información que publicó es muy fácil mostrar información de ese usuario. Por ejemplo para mostrar su imagen o su nombre

<fb:profile-pic uid="IDENTIFICADOR-DEL-USUARIO" size="square" linked="false" facebook-logo="true">
<fb:profile-name uid="IDENTIFICADOR-DEL-USUARIO">

Recuerda que para que la imagén se muestre debes iniciar la API JavaScript al final del documento.

Con esto creo que puedes hacerte una idea del funcionamiento de FBConnect, pero si aun así puedo ayudar en algo no dudes en hacer cualquier comentario en esta entrada.

Abrazos para todos.

octubre 15, 2009

Arrancando

Filed under: Personal — cgcerro @ 9:03 am

Hola!! Saludos a toooodo el mundo.

Llevo planteándome la idea de tener un blog varias semanas, pero siempre me había planteado si tengo algo interesante que aportar al mundo o no y este auntorechazo me había echado atrás siempre. Claro, hablar sobre las cosas cotidianas de la vida o sobre reflexiones personales es algo que no va mucho conmigo además de que dudo dudo que os pueda interesar. Así que despues de una semana sufriendo para realizar un trabajillo en el portal que estoy realizando junto con tres amigos (masquemedicos.com) me dije: joder, como me gustaría que alguien me hubiera explicado esto de forma clara!!! Pues ya está, si lo consigo puedo escribir sobre el tema y seguro que a alguien le sirve de ayuda!!! Esta será la finalidad del blog, ahora, si encontrais algo sobre cualquier otro tema pues bienvenido sea, no?

Y en esa estamos, que voy a intentar explicar aquí como hacer algunas cosas que a mi me han supuesto un problema y así poder ayudar a quien le interese.

Que nadie espere encontrar aquí artículos formales o rimbonbantes con mogollón de tecnicismos. No, intentaré explicar las cosas como se las explicaría a un compañero de trabajo, lo más claro y sencillo posible.

Así que espero que no sea uno de esos blogs que hacen la presentación y no vuelven a escribir nunca más… pero ahora tengo que trabajar y no me queda más remedio que aplazar el primer articulo «Explicación de FbConnect»

Abrazos para todos.

Blog de WordPress.com.