viernes, 21 de diciembre de 2012

Empezando con jQuery

Dentro de la serie de herramientas de programación web hoy le toca el turno a jQuery, que es una librería de JavaScript, que facilita el manejo de los objetos una página web (enlaces, capas, títulos, párrafos, etc), de forma que permite establecer ciertas acciones sobre ellos (movimiento, ocultación, efectos, etc). jQuery se distribuye con una licencia de software libre y código abierto, permitiendo su uso tanto en proyectos libres como privativos. jQuery ofrece una serie de funcionalidades que escritas en JavaScript requerirían de muchísimo más código. La filosofía de jQuery es que pequeñas funciones logran grandes resultados, reduciendo el tiempo de desarrollo y el tamaño del código. El autor de esta librería es John Resig que además trabaja para Mozilla Corporation.


Instalación:

Instalar jQuery es de lo más sencillo: basta con bajarse la librería del sitio web oficial y copiarla en una carpeta de nuestro proyecto. Luego hay que incluirla como una librería más de nuestro proyecto:

<script type="text/javascript" src="js/jquery.min.js"></script>

La función $:

El concepto básico de jQuery consiste en dos apartados: el primero es la captura o selección de elementos del documento HTML y el segundo en aplicar acciones o programar eventos sobre esos elementos. La función $ (dolar) es la encargada de seleccionar partes del documento HTML: etiquetas, clases, divisores, elementos, etc. Luego con esas partes capturadas haremos cosas como mostrarlas, ocultarlas, aplicarles efectos, etc. Veamos unos ejemplos:

  • $(document): Captura todo el documento HTML.
  • $("body"): Captura el cuerpo del documento HTML.
  • $("a"): Captura todos los enlaces del documento HTML.
  • $("#etiqueta"): Captura el elemento HTML con id="etiqueta".
  • $(".clase"): Captura todos los elementos HTML con class="clase".
  • $("div.clase"): Captura todos los divisores con class="clase".
  • $("#etiqueta a"): Captura todos los enlaces dentro del elemento con id="etiqueta".
  • $("div[name='nombre']"): Captura todos los divisores cuyo atributo name='nombre'.
  • $("[name!='nombre'][type='button']"): Captura todos los elementos cuyo atributo name sea distinto de "nombre" y cuyo atributo type valga "button".
  • $("input:button"): Captura todos los botones (<input type="button">).
  • $("div:hidden"): Captura todos los divisores ocultos.

Aquí podéis encontrar una referencia completa de todos los selectores jQuery. La función $ devuelve un objeto jQuery. Sobre ese objeto aplicaremos eventos, efectos y acciones.


Eventos en jQuery:

El primer evento que hay que conocer en jQuery es el evento ready() que se ejecutará cuando la selección haya acabado de cargarse. Suele usarse por ejemplo para evitar que se ejecute cualquier otra función jQuery antes de que se termine de cargar el documento HTML.

$(document).ready(function() {

  // Resto de funciones jQuery...
});

Otro evento importante es el evento click() que se ejecuta cuando el usuario pulsa sobre algún elemento de la selección. No es necesario que la selección sea un enlace o un botón; puede ser cualquier elemento HTML.

$(document).ready(function() {

  $("a").click(function() {
    alert("Se ha pulsado un enlace");
  });

  $("input:button").click(function() {
    alert("Se ha pulsado un botón");
  });
});

En este código hemos programado dos eventos dentro del evento ready(), por lo tanto no estarán disponibles hasta que no se cargue todo el documento HTML. El primer evento mostrará un mensaje cuando se pulse en cualquier enlace (<a href="...">) y el segundo hará lo propio cuando se pulse cualquier botón (<input type="button"...>). Como vemos la sintaxis de jQuery es muy sencilla aunque es relativamente fácil liarse con los paréntesis y las llaves. Pero no deja de ser código JavaScript, por lo que podemos escribirlo de forma más clara:

var d = $(document);
d.ready(onReadyDocument);

function onReadyDocument() {

  var anchors = $("a");
  var buttons = $("input:button");

  anchors.click(onClickAnchors);
  buttons.click(onClickButtons);
}

function onClickAnchors() {
    alert("Se ha pulsado un enlace");
}

function onClickButtons() {
  alert("Se ha pulsado un botón");
}

Otros eventos comunes en jQuery son los siguientes. Aquí podéis encontrar una referencia completa de todos los eventos jQuery.

  • click() Se ejecuta al hacer clic sobre un elemento de la selección.
  • dbclick() Se ejecuta al hacer doble clic sobre un elemento de la selección.
  • hover() Se ejecuta cuando el ratón está encima de un elemento de la selección.
  • mousemove() Se ejecuta cuando el ratón se mueve estando encima de un elemento de la selección.
  • mouseleave() Se ejecuta cuando el ratón sale de un elemento de la selección.
  • keydown() Se ejecuta cuando el usuario pulsa una tecla si el foco lo tiene un elemento de la selección.
  • keyup() Se ejecuta cuando el usuario suelta una tecla si el foco lo tiene un elemento de la selección.
  • keypress() Igual que keydown() pero se va repitiendo periódicamente hasta que el usuario suelte la tecla.
  • focus() Se ejecuta cuando un elemento de la selección recibe el foco. Generalmente se usa en formularios.
  • blur() Se ejecuta cuando un elemento de la selección pierde el foco. Generalmente se usa en formularios.
  • select() Se ejecuta cuando el usuario selecciona un texto. Generalmente se usa en formularios.
  • change() Se ejecuta cuando el contenido cambia. Generalmente se usa en formularios. Por ejemplo en un control <input> o <select>.
  • submit() Se ejecuta cuando cuando el usuario envía un formulario. Se usa para validar el contenido del formulario antes de enviarlo.

Acciones jQuery:

Existen multitud de acciones en jQuery. Unas permiten cambiar el aspecto de algún elemento HTML, otras sirven para aplicar efectos o animaciones y hay otras aun más complejas. Una de las primeras que se aprenden es la función css() que permite cambiar cualquier propiedad CSS de los elementos de la selección. Vamos a verlo con un ejemplo:

$(document).ready(function() {

  var theBody = $("body");
  var anchors = $("a");

  theBody.css("background-color": "white");

  anchors.mouseenter(function() {
    theBody.css("background-color", "red");
  });

  anchors.mouseleave(function() {
    theBody.css("background-color", "white");
  });
});

Lo que hace este código jQuery es poner el color de fondo de la página en blanco y luego programar dos eventos: El primero pondrá en rojo el color de fondo cuando el ratón esté sobre cualquier enlace. El segundo evento restaura el color de fondo blanco cuando el ratón sale de un enlace. No es que sea un código muy útil, pero nos sirve de ejemplo.

Otras acciones comunes en jQuery son los siguientes. Para una referencia completa de acciones podéis visitar referencia efectos jQuery y referencia métodos jQuery.

  • css() Modifica una propiedad CSS de todos los elementos de la selección.
  • addClass() Añade una clase CSS a todos los elementos de la selección.
  • removeClass() Elimina una clase CSS de todos los elementos de la selección.
  • height(), innerHeight(), outerHeight() Devuelven la altura actual del primer elemento de la selección.
  • width(), innerWidth(), outerWidth() Devuelven la anchura actual del primer elemento de la selección.
  • hide() Oculta todos los elementos de la selección.
  • show() Muestra todos los elementos de la selección.
  • fadeOff() Modifica la opacidad de los elementos de la selección haciendo que desaparezcan progresivamente.
  • fadeIn() Modifica la opacidad de los elementos de la selección haciendo que aparezcan progresivamente.
  • slideDown() Muestra los elementos de la selección de forma progresiva deslizando hacia abajo.
  • slideUp() Oculta los elementos de la selección de forma progresiva deslizando hacia arriba.
  • animate() Permite crear efectos más complejos aplicables sobre las propiedades numéricas de todos los elementos de la selección.
  • text() Sirve tanto para obtener como para establecer el texto de los elementos de la selección.
  • attr() Sirve tanto para obtener el valor de una propiedad del primer elemento de la selección como para establecer el valor de una propiedad de todos los elementos de la selección. Por ejemplo la propiedad "alt" de un elemento <img>.
  • removeAttr() Elimina una propiedad de los elementos de la selección. Por ejemplo la propiedad "alt" de un elemento <img>.
  • val() Sirve tanto para obtener como para establecer el valor de los elementos de la selección.
  • html() Sirve tanto para obtener como para establecer el contenido HTML de los elementos de la selección. Equivale a la propiedad innerHTML del DOM.

Hasta aquí este minitutorial de jQuery. En un artículo posterior hablaré de jQuery avanzado, ajax en jQuery, etc.


Enlaces:

jueves, 20 de diciembre de 2012

Tabla de países 2.0

Hace tiempo necesitamos una tabla de países para un proyecto web. Aprovechamos en ese momento para ofrecer de forma libre y gratuita un archivo SQL que generaba una tabla de países para una base de datos MySQL. Dicha tabla tenía el código del país en formato ISO 3166-1 alfa-2 y el nombre oficial del país en Español.

Actualmente hemos necesitado ampliar esa tabla de países con los códigos ISO 3166-1 alfa-3 y el nombre internacional (en Inglés) y hemos aprovechado para añadir el código ISO 3166-1 numérico y el código FIPS por si en un futuro los necesitamos en otro proyecto. Todos esos códigos definen de forma única un país o territorio:

  • Códigos ISO 3166-1: Se trata de distintos códigos de países otras dependencias administrativas definidos como parte de la norma ISO 3166:
    • alfa-2: Código de 2 letras.
    • alfa-3: Código de 3 letras.
    • numérico: Código numérico de 3 dígitos.
  • Código FIPS: FIPS son las siglas de (Federal Information Processing Standard) y son un conjunto de estándares definidos por el gobierno de los Estados Unidos para la utilización por parte de todas las agencias del gobierno no militares y por los contratistas del gobierno. Los códigos de países FIPS son una versión modificada del código ISO 3166-1 alfa-2.

Ejemplo:

alfa-2alfa-3num-3FIPSNombre [ES]Nombre [INT]
ESESP724SPEspañaSpain
PTPRT620POPortugalPortugal
FRFRA250FRFrancia France
GBGBR826UKReino UnidoUnited Kingdom
USUSA840USEstados UnidosUnited States

Tabla de países:

CREATE TABLE IF NOT EXISTS `countries` (
  `iso_a2` varchar(2) NOT NULL,
  `iso_a3` varchar(3) DEFAULT NULL,
  `iso_n3` varchar(3) DEFAULT NULL,
  `fips` varchar(10) DEFAULT NULL,
  `name_es` varchar(64) DEFAULT NULL,
  `name_int` varchar(64) DEFAULT NULL,
  PRIMARY KEY (`iso_a2`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

Como de costumbre ponemos a disposición de quien lo necesite, de forma libre y gratuita, un archivo que genera la versión 2.0 de la tabla de países en formato MySQL. Se permite explícitamente usar y alterar los datos contenidos en ese archivo para cualesquier fin, aunque no garantizamos que los datos sean correctos o estén totalmente actualizados.

Descargar paises-v2.0.zip (6,31 KB).


Fuentes:

martes, 18 de diciembre de 2012

Como extender clases en CodeIgniter (II)

El otro día hablábamos de Como extender clases del modelo en CodeIgniter sin recibir el temible error "Class not found". El método comentado consistía en cargar manualmente la clase base antes que la clase hija. Desgraciadamente este método no puede usarse para los controladores pues es el propio framework el que se encarga de cargar los controladores. Afortunadamente en PHP 5 hay un método adecuado para extender cualquier clase si recibir ese fatal error: La función __autoload()

Dicha función se diseñó para evitar que los desarrolladores de aplicaciones orientadas a objetos que escriben cada clase en un fichero fuente PHP separado tuvieran que poner una larga lista de includes al comienzo de cada script. La función __autoload() es invocada automáticamente por el motor PHP (si está definida) en caso de que se intente usar una clase o interfaz que todavía no hayan sido definidos. Es como una última oportunidad.

En el caso de CodeIgniter la siguiente función cargaría automáticamente cualquier clase del controlador o del modelo.

function __autoload($class_name) {

  $class_folders = array(
    'controllers',
    'models'
  );

  foreach ($class_folders as $folder_name) {

    $full_address = APPPATH . $folder_name . '/' . strtolower($class_name) . EXT;
    if (file_exists($full_address)) {
      require_once($full_address);
    }
  }
}

Ya solamente queda poner esa función en un lugar del código fuente de la aplicación que se ejecute siempre. Algunos autores recomiendan añadirla al final del archivo de configuraciones "/application/config/config.php". Yo he probado con el fichero "/application/config/autoload.php" porque me parecía un buen sitio pero el controlador se invoca antes, con lo que no funciona.

viernes, 14 de diciembre de 2012

Como extender clases del modelo en CodeIgniter

Una de las características de la Programación Orienta a Objetos es que las clases pueden heredar características de una clase base. En el patrón de diseño MVC que implementa CodeIgniter todas las clases que usamos al diseñar nuestra aplicación derivarán de alguna clase base del framework (CI_Model, CI_Controller, etc.) pero también es posible crear nuestras propias clases base.

Para ello la clase base derivará de alguna del framework y la clase definitiva derivará de nuestra clase base. Como en el siguiente ejemplo:

class Mybaseclass_model extends CI_Model {

  public function __construct() {
    parent::__construct();
  }

  // ...
}

class Myclass_model extends Mybaseclass_model {

  public function __construct() {
    parent::__construct();
  }

  // ...
}

class Myclass2_model extends Mybaseclass_model {

  public function __construct() {
    parent::__construct();
  }

  // ...
}

El problema de hacer esto es que las diferentes clases Mybaseclass_model, Myclass_model y Myclass2_model estarán definidas en archivos distintos, con lo que al intentar cargar una de las clases derivadas nos encontraremos con el error:

Fatal error: Class 'Mybaseclass_model' not found in
W:\www\test\application\models\myclass_model.php on line 17.

Para solucionarlo es necesario cargar la clase base antes que la derivada, ya sea manualmente, como vemos abajo, a sea agregando la clase base a la carga automática de componentes del modelo en el archivo "/application/config/autoload.php"

$this->load->model('Mybaseclass_model');
$this->load->model('Myclass_model');

ACTUALIZACIÓN:

El método descrito en este artículo para extender clases del modelo sin recibir el temible error "Class not found" no es válido para extender clases del controlador, pues éstas son cargadas por el propio framework. Por suerte en PHP 5 tenemos la función __autoload() que permite extender clases del modelo y del controlador en CodeIgniter.

Accediendo a web services desde PHP vía socket

Según la Wikipedia, un web service (o servicio web) es una tecnología que utiliza un conjunto de protocolos y estándares que sirven para intercambiar datos entre aplicaciones que pueden estar desarrolladas en lenguajes de programación diferentes, y ejecutadas sobre plataforma distintas.

El lenguaje PHP tiene diversas formas de acceder a un web service, siendo la más usada SOAP (Simple Object Access Protocol). Para tener una visión completa recomiendo el repaso de la sección Web Services del manual de PHP. Pero hay otra forma de acceder a un servicio web, una de muy bajo nivel. Al fin y al cabo un web service no es más que un protocolo basado en direcciones web (URLs), por lo tanto si conocemos los parámetros del servicio podemos acceder al servicio web usando un simple socket y la siguiente secuencia de comandos:

  • Abrir el socket con el servidor (host).
  • Enviar una petición HTTP a través del socket.
  • Leer la respuesta del servidor en un buffer.
  • Extraer el mensaje del buffer.

Algunos de los puristas de los estándares dirán que esa no es la forma adecuada de acceder a un web service, pero eso no quiere decir que no se pueda hacer. En cualquier caso voy a poner un ejemplo de como hacerlo.

function readWebService($url) {

  // Se elimina de la URL el nombre del protocolo
  $url = str_replace("http://", "", $url);

  // Se extrae el host de la URL
  $pos = strpos($url, "/");
  if($pos === false) {
    $host = $url;
    $page = "/";
  }
  else {
    $page = substr($url, $pos);
    $host = substr($url, 0, $pos);
  }

  // Se obtiene la IP del host
  $ip = gethostbyname($host);

  // Se abre un socket de comunicación con el host
  $socket = @fsockopen($ip, 80, $errno, $errstr, 60);
  if($socket === false) {
    return false;
  }

  // Preparación de la petición HTTP a enviar al host
  $send = "GET " . $page . " HTTP/1.0\r\n";
  $send .= "Host: " . $host . "\r\n";
  $send .= "Accept-Language: es-ES\r\n";
  $send .= "Content-Type: application/json; charset=UTF-8\r\n";
  $send .= "User-Agent: " . $_SERVER["HTTP_USER_AGENT"] . "\r\n";
  $send .= "Connection: Close\r\n\r\n";

  // Se envía la petición HTTP al host
  fputs($socket, $send);

  // Se lee la respuesta del host
  $buffer = "";
  while(!feof($socket)) {
    $buffer .= fgets($socket, 4096);
  }

  // Se cierra el socket
  fclose($socket);

  // Separación de la cabecera HTTP y el contenido.
  $response = @explode("\r\n\r\n", $buffer, 2);

  // Se devuelve el resultado
  return $response;
}

Si la función falla, bien sea porque no encuentra el host o porque expira el tiempo máximo de 60 segundos que hemos puesto, devuelve FALSE. Si la función se ejecuta correctamente devuelve un array donde la primera posición contendrá la respuesta HTTP del servidor y la segunda el contenido de la respuesta. La cabecera HTTP a enviar variará dependiendo de las necesidades del momento. Es recomendable entender bien el protocolo HTTP para hacer uso de esta función. Además la respuesta HTTP del servidor también debería ser interpretada para ver lo que ha ocurrido.

Para probarlo podemos usar un servicio web para desarrolladores de Yahoo que permite obtener el timestamp (hora actual en formato UNIX).

$url = "http://developer.yahooapis.com/TimeService/V1/getTime/";
$url .= "?appid=YahooDemo";
$url .= "&output=json";
$response = readWebService($url);
if(!$response)
  echo "ERROR";
else {
  $header = $response[0];
  $content = $response[1];
  echo $content;
}
El resultado será:
{"Result":{"Timestamp":1355482897}} 

Esta técnica la podemos usar tanto para obtener datos de un web service, ya sea en formato JSON, XML u otro, como para cargar una página HTML y explorar su contenido.

martes, 11 de diciembre de 2012

Tratamiento de idiomas en CodeIgniter

Siguiendo con la serie de artículos sobre la programación del framework CodeIgniter hoy hablaré de las posibilidades de CodeIgniter respecto al tratamiento de idiomas: Veremos como convertir todo el framework al español, como hacer un proyecto multiidioma y hablaremos de la clase Language y del helper Language.


Como poner CodeIgniter en Español.

CodeIgniter como la mayoría de frameworks PHP proporciona un mecanismo para poder cambiar el idioma en que se muestra un página web. Los archivos de idioma del framework se encuentran en la carpeta "/system/language/". Dentro de dicha carpeta habrá una carpeta para cada idioma: "english" para Inglés, "spanish" para español, etc. Inicialmente sólo se proporcionan traducciones para el idioma Inglés. Si queremos el framework en español tendremos que crear la carpeta "/system/language/spanish/", copiar los archivos de la versión inglesa y traducir los textos.

El formato de los archivos de idioma lo vemos en el siguiente ejemplo:

$lang['cal_sunday'] = "Sunday";
$lang['cal_monday'] = "Monday";
$lang['cal_tuesday'] = "Tuesday";
$lang['cal_wednesday'] = "Wednesday";
$lang['cal_thursday'] = "Thursday";
$lang['cal_friday'] = "Friday";
$lang['cal_saturday'] = "Saturday";

Este fragmento es parte del archivo "calendar_lang.php". Para crear la versión en español crearíamos el archivo "/system/languge/spanish/calendar_lang.php" y traduciríamos los textos, pero no los literales:

$lang['cal_sunday'] = "Domingo";
$lang['cal_monday'] = "Lunes";
$lang['cal_tuesday'] = "Martes";
$lang['cal_wednesday'] = "Miércoles";
$lang['cal_thursday'] = "Jueves";
$lang['cal_friday'] = "Viernes";
$lang['cal_saturday'] = "Sábado";

En Internet se pueden encontrar archivos con las traducciones para los distintos idiomas. Por ejemplo en una búsqueda rápida he encontrado en github un proyecto de traducción de CodeIgniter al Español, pero los packs de idiomas no están actualizados. Lo que sí hay es está actualizado a la última versión en un manual de CodeIgniter en Español (PDF). También he encontrado un "CodeIgniter 2.0.2 Spanish Pack" en el blog de César de la Cruz. A partir de ese trabajo que creado mi propio CodeIgniter Spanish Pack actualizado a la versión 2.1.3, que es la versión oficial de CodeIgniter en el momento de escribir este artículo.

CodeIgniter 2.1.3 Spanish Pack (ZIP/8Kb). Licencia libre.


Cambiando el idioma de un proyecto.

Además de los archivos de idioma del sistema, también podemos definir nuestros propios archivos de idioma para el proyecto PHP. Estos se guardarán en la carpeta "/application/language/{lang}/" donde {lang} es el nombre del idioma (english, spanish, etc). El nombre de los archivos de idioma debe acabar en "_lang.php". Por ejemplo un archivo con mensajes de error se llamaría "error_lang.php". El formato del archivo será el mismo que se ha comentado más arriba:

$lang['error_resource_not_found'] = "Recurso no encontrado.";
$lang['error_resource_unavailable'] = "Recurso no disponible.";
//... etc.

Para poder usar un archivo de idioma primero tendremos que cargarlo. Para ello se utiliza la clase Language. Esto se hará en el controlador:

$this->lang->load('error', 'spanish');

Si no se especifica idioma, se usará el idioma por defecto, definido como una variable dentro del archivo "/application/config/config.php". Generalmente su valor es "english", pero podemos modificarlo si hemos creado la carpeta de idiomas correspondiente:

$config['language'] = 'spanish';
Luego para obtener una cadena traducida hay que llamar a la función line():
$var = $this->lang->line('error_resource_unavailable');

Esto es todo lo que se necesita para cambiar de idioma en CodeIgniter. Si queremos hacer una web multi-idioma podemos crear una cookie con el idioma del usuario y usar ese valor al cargar los archivos de idioma.


El helper Language.

En CodeIgniter un helper es un conjunto de funciones que ayudan a realizar una tarea. Podríamos traducir helper como ayudante o asistente. En concreto el helper Language contiene funciones que ayudan al programador en el tratamiento con archivos de idioma. Para poder usar este helper lo primero será cargarlo, cosa que haremos en el controlador:

$this->load->helper('language');

Una ver cargado podemos usar la función global lang() para cargar los textos de los ficheros de idiomas en lugar de $this->lang->line(). Se aconseja el uso de este helper en las vistas para favorecer la lectura de los archivos fuente. Ejemplo:

<div class="error">
<h1><?= lang('error_heading'); ?></h1>
<p><?= lang('error_resource_unavailable'); ?></p>
</div>

Fuentes y referencias: