miércoles, 11 de febrero de 2015

Improving the use of a MongoDB database with the help of Symfony'sListeners

Sometimes applications need to filter large amounts of information to show to the user a small subset of relevant data.

However, when the amount data to filter is too large, it may not be feasible to filter the information retrieving the whole data into memory.

Read this article to learn about an alternative approach using a MongoDB document and Symfony listeners to limit the amount of data that needs to be traversed in memory.


Continue reading in PHPClasses.org

miércoles, 4 de febrero de 2015

Cálculo de las distancias entre coordenadas GPS

En varios de los últimos proyectos, sobre todo aquellos relacionados con aplicaciones móviles, he tenido que implementar un sistema de cálculo de distancias entre coordenadas GPS.

Para evitar en el futuro la repetitiva tarea de incorporar el código fuente he creado un repositorio que me va a permitir disfrutar de esa característica con una simple línea en el archivo composer.json de cada proyecto.

// composer.json
{
   "require":{
      "jlaso/gps": "dev-master"
   }
}


La clase la tienes disponible en github.

El funcionamiento de momento es muy sencillo, pues sólo está incluida la función que calcula la distancia. La idea es ir añadiendo poco a poco más funciones que tengan como base el GPS.


He preparado también los tests oportunos que podrás ejecutar con PhpUnit desde la raíz del proyecto.

Si crees que esta clase te es útil sólo te pido que la descargues a través de la página de PHPClasses y que la votes en su momento.

Espero vuestros comentarios.

lunes, 2 de febrero de 2015

Cómo conseguir que gettext funcione en php

A menudo necesitamos internacionalizar los programas que desarrollamos en PHP.

En mi libro Programación profesional en PHP con Slim, París y Twig trato este tema en el capítulo 13. Los ejemplos de como usarlo los podemos encontrar en el código de la aplicación de ejemplo My-simple-web.

Una de las formas de internacionalizar más extendida en los desarrollos web consiste en el uso de la librería ICU, Intl y gettext.

La función gettext en PHP tiene un alias (subrrayado) que hace más compacta la lectura del código, por que permite concentrarse en el texto y no en la función.

Veámos un ejemplo:


   print _('This is a text that have to be translated');    
   print gettext('This line is equivalent to the line above.');    
?>


Para que gettext pueda saber la equivalencia entre el mensaje que queremos mostrar y el que le estamos pasando necesitamos crear un catálogo de traducciones. El programa más extendido para hacer esto se llama PoEdit.


Aunque se puede poner como texto clave la cadena normal tal y como he mostrado en los ejemplos anteriores (esto se hace por si falla la traducción tener un texto en un lenguaje digamos, natural), cada vez más se está extendiendo, sobre todo debido a los frameworks, el uso de etiquetas en las cadenas claves. Esta técnica permite categorizar las entradas del catálogo, y no simplemente tener amontonadas las claves.


Así, un ejemplo habitual siguiendo estas directrices sería:


  
   print _("general.button.label.home");    
?>    


El lector podrá apreciar enseguida que el uso de esa nomenclatura permite tener una estructura arbórea muy conveniente. Así es como funciona por ejemplo www.tradukoj.com, mi proyecto para ayudar a los desarrolladores a centralizar sus cadenas para traducir.

Una vez entendidos estos principios básicos, y teniendo entendido que queremos aprovecharnos de ellos en nuestros desarrollos, vamos a necesitar configurar nuestro intérprete de PHP para que acepte esta orden, ya que no forma parte de la SPL.

Lo habitual es que tengamos que instalar en el sistema la libreria ICU, INTL y el soporte para ambas en PHP.

Vamos a ver los diferentes casos que nos podemos encontrar en función del sistema operativo.

Windows

En este sistema operativo lo habitual es disponer de soporte para PHP mediante alguna de las aplicaciones que vienen como un todo en uno. También conocidas como WAMP (de su homólogo LAMP: Linux-Apache-Mysql-PHP), tenemos con el mismo nombre WAMP; AppServ y XAMPP.

En todas ellas la activación de una librería estándar suele ser un paso muy sencillo. Basta con marcar la librería en la configuración o irse directamente al archivo php.ini y descomentar la línea:
;extension=php_intl.dll


Concretamente, para el caso de Xampp puedes encontrar enlace directo a la edición de php.ini en el programa Panel de control de Xampp


xampp-config

Linux (Ubuntu/Centos y variantes)

Para instalar soporte de idiomas en el S.O Linux, si es que no lo tenemos ya, será suficiente con instalar INTL, ICU y el conector para PHP.



Ubuntu:

sudo apt-get install intl icu php5-intl


Centos:

yum install intl icu php5-intl


OsX

En función de si tenemos MacPorts o Brew instalaremos el soporte de idiomas de esta manera:


Mac Ports:

sudo port install php5-intl


Brew:

brew install php5-intl


En ambos casos para activar la extensión hay que editar php.ini y agregar o descomentar esta línea:

extension=intl.so


En todos los sistemas operativos podemos ver que php.ini usa nuestro sistema lanzando este comando desde la terminal:

php --ini


Ten en cuenta en todo caso que algunos sistemas usan un php.ini para el cli (interfaz de consola) y otro para el servidor web (apache normalmente).

Ahora sólo nos queda comprobar que funciona.

Para que el sistema operativo sepa que idioma estamos esperando cuando le mandamos gettext, necesitamos configurarlo antes que nada, por ello, de manera habitual, en el bootstrap de nuestra aplicación haremos algo como lo que tenemos aquí:


   @bindtextdomain('default', dirname(__FILE__).'/');    
   @textdomain ('default');    
   $langs = array (    
    'es' => 'ES',    
    'en' => 'GB',    
   );    
   $code = isset($_REQUEST['lang'])?$_REQUEST['lang']:'es';    
   if (isset($langs[$code]))    
     $iso_code = $code.'_'.$langs[$code];    
   else{    
     $code = "es";    
     $iso_code = 'es_ES';    
   }    
   if (isset($_SESSION['lang'])) $_SESSION['lang']=$code;    
   putenv ('LANGUAGE='.$iso_code);    
   putenv ("LC_ALL=$iso_code");    
   setlocale(LC_ALL, $iso_code);    
?>



Recuerda: si te da algún error como este:

Fatal error: Call to undefined function _() in ... on line x

Vuelve a repasar la configuración.


En todo caso, si se te resiste el uso de esta librería en local y en producción te funciona correctamente, no es nada descabellado utilizar este truco:


if(!function_exists('_')){
    function _($key){ return $key; }
}

Esto no te traducirá los textos, pero al menos no te romperá el código.

Espero vuestros comentarios.

lunes, 26 de enero de 2015

Animación estilo metro con Titanium

En ocasiones he necesitado indicar al usuario de la aplicación móvil que un proceso necesita cierto tiempo para realizarse, como la conexión a un servidor para enviar o recoger datos, o sencillamente actualizar la posición GPS.
A menudo, siguiendo las tendencias actuales, esta información se requiere de una forma limpia y no intrusiva.
Siguiendo estos parámetros he creado una utilidad que realiza una animación sobre el borde superior de una vista, utilizando un gradiente en el color de fondo (backgroundColorGradient).

La función animateGradient está incluida dentro del módulo utils.js, por tanto para usarla haremos algo así:

var utils = require('/utils');
var activity = utils.animateGradient(view,options);
// una vez terminada la actividad invocaremos
activity.destroy();


view es un contenedor de tipo Titanium.UI.createView y options puede albegar de momento las opciones que indican la velocidad de animación y los colores.

Veamos como está hecho.


animateGradient: function (view, options)
{
// in android isn't possible to include a view inside an object that is not a view
if((Titanium.Platform.osname == "android") && (view.toString() !== '[object View]')){
alert('Error on type calling animateGradient, called with '+view.toString());
setTimeout(function(){
Titanium.Android.currentActivity.finish();
},2500);
return;
}

...
}


Lo primero es comprobar que se ha llamado con el tipo de parámetro adecuado, pues en android no se puede usar un contenedor que no sea un objeto de tipo Titanium.UI.createView, en iOS he probado con un button directamente y funciona bien.


animateGradient: function (view, options)
{
// check if view parameter is right in android platform ...
if(typeof(options) === "undefined"){
options = {};
}
var timeout = options.timeout || 75;
var colors = options.colors || ['#0000aa', '#000099'];

...
}


Ahora se establecen las opciones por defecto para el caso de no indicarlas, luego explicaré para que hacen falta dos colores.


animateGradient: function (view, options)
{
// check if view parameter is right in android platform ...
// establish default options and set it to local variables ...
/**
* the view that host the activity bar
*/
var linearGradient = Titanium.UI.createView({
top: 0,
left: 0,
width: '100%',
height: '3dp',
zIndex: 1000
};
view.add(linearGradient);

...
}


Lo primero es crear una vista que va a albergar nuestra animación, como puedes ver estará en el borde superior, con ancho total y una altura de 3dp, el valor de zIndex es como medida de precaución para que sea visible en cualquier caso.


animateGradient: function (view, options)
{
// check if view parameter is right in android platform ...
// establish default options and set it to local variables ...
// create animation view container inside the main view ...
var gradient = {
inc: 0.02,
x: 0.05,
colorIndex: 0,
colors: colors,
baseColor: view.backgroundColor,
color: function(){
this.colorIndex = (this.colorIndex+1)%2;
return this.colors[this.colorIndex];
}
};

...
}


Creamos un objeto auxiliar para encapsular toda las variables que necesitamos. Fíjate como el color de fondo es exactamente el mismo que la vista principal.


animateGradient: function (view, options)
{
// check if view parameter is right in android platform ...
// establish default options and set it to local variables ...
// create animation view container inside the main view ...
// auxiliary object in order to encapsulate all variables ...
/**
* timer that simulates the activity though changing gradient color
*/
var interval = setInterval(function(){
if((gradient.x > 0.95) || (gradient.x<0.05)){
gradient.inc = -gradient.inc;
}
gradient.x += gradient.inc;
linearGradient.backgroundGradient = {
type: 'linear',
startPoint: { x: '0%', y: '50%' },
endPoint: { x: '100%', y: '50%' },
colors: [
{ color: gradient.baseColor, offset: 0.0},
{ color: gradient.baseColor, offset: gradient.x-0.05},
{ color: gradient.color(), offset: gradient.x },
{ color: gradient.baseColor, offset: gradient.x+0.05 },
{ color: gradient.baseColor, offset: 1.0 }
]
};
}, timeout);

...
}


Ahora viene la parte que realmente hace la animación, un intervalo que en cada iteración crea la ilusión de que el gradiente del color de fondo se mueve. Para forzar a Titanium a que pinte la vista de nuevo hay que indicar cada vez un color diferente, por eso se utilizan dos colores, para que el efecto sea más bonito y auténtico no deben distar mucho ambos colores, fíjate en los que se usan por defecto.


animateGradient: function (view, options)
{
// check if view parameter is right in android platform ...
// establish default options and set it to local variables ...
// create animation view container inside the main view ...
// auxiliary object in order to encapsulate all variables ...
// timer that simulates the activity though changing gradient color ...
return {
linearGradient: linearGradient,
view: view,
destroy: function(){
clearInterval(this.interval);
view.remove(linearGradient);
},
interval: interval
};
}


Al final se devuelve un objeto con los datos necesarios, el más importante es la función que nos va a permitir deshacernos de la vista temporal usada para crear la animación.



El código fuente completo de la animación incluido en una pequeña aplicación de ejemplo lo tienes a tu disposición en mi cuenta de github.

Vamos a ver el ejemplo de uso completo:


...
var testBtnView = Titanium.UI.createView({
backgroundColor: "#aaa",
top: "50%",
height: '50dp',
width: "225dp"
});
win.add(testBtnView);

var testBtn = Titanium.UI.createButton({
color: 'white',
backgroundColor: '#aaa',
title: "TEST",
font: {fontSize:'18dp',fontFamily:'Helvetica Neue',fontWeight:'bold'},
textAlign: 'center'
});
testBtnView.add(testBtn);
...
function testBtnHandler(){
console.info("test button clicked");
activity = utils.animateGradient(testBtnView);
// simulate an action that lasts 5 seconds
setTimeout(function(){
activity.destroy();
}, 5000);
}
...


Espero vuestros comentarios.

sábado, 24 de enero de 2015

Publiqué mi primer libro

Hace un par de años se me ocurrió la locura de plasmar por escrito mis conocimientos adquiridos sobre programación en PHP.