Uncategorized

Decoradores en Zend Form

De cuando en cuando, tengo que lidiar con formularios Zend_Form en Zend Framework. Son maravillosos, ya que simplifican enormemente el trabajo, y bien usados pueden aumentar notablemente la calidad de los formularios. Si no estás ya convencido, deja que te cuente alguna de sus ventajas:

  • Separación del formulario de las vistas. Ahora los formularios serán un objeto sobre el que puedes realizar numerosas operaciones. La clase encargada es Zend_Form.
  • Cada elemento del formulario es también un objeto, que hereda de Zend_Form_Element. Es decir, si añades un campo de texto, éste será también un objeto que, entre otras propiedades contiene su nombre, valor, etiqueta, descripción, errores, filtros, validadores, etc. Están todos los elementos de un formulario HTML disponibles en la biblioteca de Zend_Framework
  • Filtros, gracias a Zend_Filter. Podemos hacer que un elemento filtre de forma limpia y elegante su valor, como por ejemplo conversión a mayúsculas. Zend Framework trae un gran número de filtros.
  • Validaciones, gracias a Zend_Validate. Del mismo modo que podemos filtrar, también podemos añadir validaciones. Por ejemplo, que un valor numérico se encuentre en un determinado rango, que cumpla una determinada expresión regular, o que no exceda un determinado número de caracteres.
  • Comprobar el formulario. ¿Queremos saber si el formulario que ha completado el usuario es correcto? Simplemente invocamos el método isValid() del formulario, y sabremos si ha sido correcto.
  • Definir el formulario en un archivo .ini o .xml.

Pero volviendo al hilo, cada vez que tengo que retomar Zend_Form, hay un punto en el que me atasco: los decoradores. Su funcionamiento se basa en el patrón decorador.

Diseño personalizado para mapas de Google

Con tanto revuelo que hay con la Google I/O 2010 (La apertura de Wave, Google TV, y la Chrome Web Store), poco se habla del resto de novedades. Una de ellas son los Styled Maps de la API de Google Maps.

Gracias a estas cositas, muchos diseñadores que hasta ahora se mostraban reticentes, se animarán a usar esta API para sus páginas.

Aquí tenemos un ejemplo de qué se puede hacer:


Consejos SEO de Bing

Vía Bing Community os traigo esta serie de consejos SEO que nos proponen. La mayoría me han resultado viejos conocidos, pero otros… Son sobre los enlaces, y el artículo se titula “making links work for you“. Aquí los tenéis:

  1. Canonizar la URL de inicio. ¿Con o sin www? ¿Con barra final o sin ella? Sólo puede quedar una ;)
  2. Escoger entre enlaces relativos y absolutos. Nos aconsejan usar siempre enlaces absolutos, sobre todo porque refuerzan nuestra URL.
  3. Utiliza una buena sintaxis en las URLs de los enlaces. Por ejemplo, debemos omitir el fichero por defecto de una carpeta (Típicamente index).
  4. Utiliza el atributo title en enlaces internos. En los enlaces que apuntan a tu propia página completa el atributo title con una descripción de la página de destino.
  5. Identifica la URL canónica de cada página. Ya sabéis, como en el punto 1, pero para todas las páginas.
  6. Minimiza el número de parámetros de URLs dinámicas. Suelen ser problemáticas para los bots que rastrean la página.
  7. Evita usar IDs de sesiones o cookies. Dificultarás el rastreo a los bots.
  8. Ten al menos un enlace interno a cada página. Lógico. Todos los documentos deben ser accesibles.
  9. Limita el número de enlaces de una página. Nos aconsejan que un documento no contenga más de 100 (Salvo que sea necesario).
  10. Evitar que el rastreador siga un enlace. Mediante rel=”nofollow”. Cuanto más tiempo dedique a las páginas no interesantes, menos dedicará a las que de verdad nos interesan.

Sobre los links, también hicieron unos artículos muy interesantes titulados “El bueno, el feo y el malo” (Parte 1 y Parte 2).

http://www.bing.com/community/blogs/webmaster/

Google lanza un teclado virtual

Vía programmable web me entero de que Google acaba de lanzar un teclado virtual especialmente útil para páginas con soporte multi-idioma. ¿Qué vamos a poder hacer con esto? Podremos incluír un teclado en otro idioma directamente en nuestra web sólo con unas pocas líneas de javascript.

Actualmente están disponibles:

  • Árabe (العربية)
  • Hindi (हिन्दी)
  • Polaco (Polski)
  • Ruso (Русский)
  • Tailandés (ไทย)

Estoy seguro que tanto mi hermana Olalla como mi cuñado Samer se alegrarán de tener esta herramienta, y seguro que la utilizarán :)

Singleton serializado en sesión de PHP

Llevo un par de días liado programando desde cero una pequeña tienda online (Sin pasarelas ni florituras). Entre los hitos que tenía para hoy, el más importante es crear el carrito de la compra. He buscado por el todo poderoso, pero todas las soluciones que veía me parecían ineficientes o feas. No quería almacenar el carrito de forma “permanente” en la base de datos, de modo que lo lógico me parecía usar la memoria de las sesiones. Evidentemente el carrito debía ser un singleton, pero ¿cómo hacer que se serialice automáticamente en memoria?

Echemos un primer vistazo a un singleton en PHP:

class Example
{
    // Hold an instance of the class
    private static $instance;

    // A private constructor; prevents direct creation of object
    private function __construct()
    {
        echo 'I am constructed';
    }

    // The singleton method
    public static function singleton()
    {
        if (!isset(self::$instance)) {
            $c = __CLASS__;
            self::$instance = new $c;
        }

        return self::$instance;
    }

    // Example method
    public function bark()
    {
        echo 'Woof!';
    }

    // Prevent users to clone the instance
    public function __clone()
    {
        trigger_error('Clone is not allowed.', E_USER_ERROR);
    }

}

Sencillo, verdad? :) Pero este es el esqueleto de un singleton básico. Se declara como privado el constructor, de forma que si necesitas una instancia del objeto debas hacerlo necesariamente por el método estático Example::singleton(). Este método construye una nueva instancia, o bien devuelve la que ya había (Almacenada en el atributo estático $instance). Sin embargo la clase que yo necesito debe cargarse por defecto de memoria, y almacenar el objeto de nuevo cuando éste se destruya. La solución aquí:

class ShoppingCart
{

    private $_items;
    private static $_instance;
    private static $_namespace = "spp";

    private function __construct()
    {
        $this->_items = Array();
    }

    public function __destruct()
    {
        $shopping = new Zend_Session_Namespace(self::$_namespace);
        $shopping->object = serialize(self::$_instance);
    }

    public function __clone() {
        throw new Exception("Cannot clone singleton!");
    }

    public static function singleton()
    {
        $shopping = new Zend_Session_Namespace(self::$_namespace);

        if (!isset(self::$_instance)) {
            if(isset($shopping->object)) {
                self::$_instance = unserialize($shopping->object);
            } else {
                $c = __CLASS__;
                self::$_instance = new $c;
            }
        }

        return self::$_instance;
    }

    public function addItem($id, $quantity)
    {
        // Código que añade un nuevo item
    }

    public function removeItem($id)
    {
        // Código que elimina un item
    }

    // Resto de funciones del carrito de la compra
}

Básicamente funciona igual, excepto que antes de crear un nuevo objeto, comprueba si hay alguno en el espacio de nombres de memoria asignado en el atributo estático $_namespace. Igualmente, cuando el objeto se destruye automáticamente por el recolector de basura, se serializa para guardarse la nueva versión en memoria.

Es importante darse cuenta de esto último: El método __destruct() no funcionará si ya hemos finalizado la sesión del usuario mediante session_write_close(). Por esa razón, en algunos casos tendremos que llamar explícitamente al destructor antes de que se cierre la sesión. Resulta que el método para redirigir a otra página Zend_Controller_Action::_redirect($url) cierra la sesión. Me he vuelto loco hasta que he averiguado la razón. Se corrige simplemente llamando a ShoppingCart::__destructor() antes de hacer la redirección.