Recursos interesantes de Laravel (2015-12)

Misceláneos

Descargar manualmente imágenes (boxes) de Vagrant desde el catálogo de HashiCorp’s Atlas

Obtener la box

Para descagar manualmente la imágen base (box) de una máquina virtual desde el catálogo de HashiCorp’s Atlas para ser utilizada con Vagrant se deben seguir los siguientes pasos.

  1. Ubicar la box que se desea descargar en el catálogo.
  2. Identificar el URL de la box deseada.

    Por ejemplo: https://atlas.hashicorp.com/thdengops/boxes/ubuntu-14.04-dev.

  3. Teniendo en cuenta la información mostrada en el sitio web, agregar a la URL la versión y el proveedor (sistema de virtualización) de la box que se desea descargar.

    Vagrant Box Info

    De la manera URL/versions/{VERSION}/providers/{PROVEEDOR}

    Para el caso del ejemplo sería entonces: https://atlas.hashicorp.com/thdengops/boxes/ubuntu-14.04-dev/versions/0.0.7/providers/virtualbox.box

Crear una máquina virtual a partir de una box local

Se debe agregar la box local al sistema de boxes de Vagrant, este procedimiento se debe realizar una unica vez.

$ vagrant box add miBox /ruta/boxes/ubuntu-14.04-dev.virtualbox.box

Posteriormente se crea el Vagrantfile para la instancia de la máquina virtual.

$ vagrant init miBox

Finalmente, una vez configurado apropiadamente el Vagrantfile, se inicia la máquina virtual.

$ vagrant up

Recursos

Los primeros 10 minutos con Vagrant

Instalación

Instalar Virtualbox como prerequisito si aún no se cuenta con este software.

$ sudo aptitude install dkms virtualbox

Es recomendable instalar su correspondiente Extension Pack.

Para el caso de Ubuntu/Debian descargar el archivo .deb correspondiente a la arquitectura desada del sitio web de descargas e instalarlo localmente.

$ sudo dpkg -i vagrant_*.deb

Obtener la caja (box) a obtenerse

Una box hace referencia a la máquina virtual base que se utilizará para crear la que se asociará al proyecto.

$ vagrant box add ubuntu/trusty32

En este caso ubuntu/trusty32 hace referencia al nombre de la imagen que va a descargarse.  Pueden  utilizarse otras imágenes que se encuentran en linea gracias al repositorio oficial de Vagrant.

Los archivos asociados a estas imágenes son descargados bajo el directorio ~/.vagrant.d/boxes.

Crear el archivo de configuración

En el directorio del proyecto ejecutar el siguiente comando

$ vagrant init ubuntu/trusty32

Esto básicamente crea una versión base del archivo Vagrantfile para configurar la máquina virtual y asigna automáticamente la box a utilizarse en el proyecto.

$ vi VagrantFile

   ...
   config.vm.box = "ubuntu/trusty32"
   ...

Una vez se cree la máquina virtual por primera vez (con el primer vagrant up) sus archivos quedarán por defecto bajo ~/VirtualBox\ VMs/.

Reenvío de puertos

Para configurar el reenvío de puertos entre la máquina virtual y la máquina física se deben agregar una o mas opciones config.vm.network al archivo Vagrantfile.

$ vi VagrantFile

   ...
   config.vm.network :forwarded_port, guest: 80, host: 8080
   config.vm.network :forwarded_port, guest: 443, host: 80443
   ...

Configurar la interfaz de red

El acceso a la red de la máquina virtual puede configurarse de manera privada (sólo el host y otras máquinas pueden acceder a ella) o de manera pública en la cual cualquier equipo en la red puede interactúar con la máquina virtual.

Configuración privada

Es posible utilizar una dirección IP estática de la siguiente manera.

$ vi VagrantFile

   ...
   config.vm.network "private_network", ip: "192.168.1.100"
   ...

O una dirección IP dinámica.

$ vi VagrantFile

   ...
   config.vm.network "private_network", type: "dhcp"
   ...

Configuración pública

Al igual que con la configuración privada, es posible asignar una dirección IP estática.

$ vi VagrantFile

   ...
   config.vm.network "public_network", ip: "192.168.1.100"
   ...

O una dirección IP dinámica basada en el DHCP de la red.

$ vi VagrantFile

   ...
   config.vm.network "public_network"
   ...

Directorios sincronizados

Por defecto el directorio /vagrant de la máquina virtual se encuentra sincronizado con el directorio del proyecto en la máquina física, es decir, en este directorio se deberán ubicar los archivos que deberán perdurar en el tiempo.

Es posible especificar otros directorios sincronizados mediante diferentes métodos.

Gestión de la máquina virtual

Iniciar la máquina virtual

$ vagrant up

Acceder a la máquina virtual a través de una sessión de SSH

$ vagrant ssh

Suspender una máquina virtual activa

$ vagrant suspend

En este caso el estado de la máquina en memoria (además de sus archivos) es almacenado en disco.  Su reinicio es muy rápido (entre 5 y 10 segundos).

Detener una máquina virtual activa

$ vagrant halt

Este comando realiza un apagado limpio de la máquina virtual.  El reinicio de la máquina toma un poco mas de tiempo.

Destruir una máquina virtual

$ vagrant destroy

Este comando apaga abruptamente la máquina virtual si esta se encuentra activa y remueve todos los archivos asociados a la misma sin incluir las carpetas compartidas (/vagrant).

Obtener ayuda de los comandos vagrant

Para listar los comandos disponibles utilice el siguiente comando.

$ vagrant list-commands

Para obtener ayuda adicional sobre un comando en especial utilice la siguiente variante.

$ vagrant COMANDO -h

Recursos

Instalación de Laravel 5 en Ubuntu

Instalación de Composer

$ curl -sS https://getcomposer.org/installer | php

$ sudo mv composer.phar /usr/local/bin/composer

Instalación de Laravel

$ composer global require "laravel/installer=~1.1"

Actualizar el PATH para incluir la herramienta de laravel.

$ vi ~/.bashrc

    PATH=$PATH:~/.composer/vendor/bin/

Creación de un nuevo proyecto

$ laravel new nuevo-proyecto

Verificar la versión de Laravel instalada.

$ cd nuevo-proyecto

$ php artisan -V

Ejecución del servidor web embebido para desarollo

$ php artisan serve

Acceder al proyecto desde un navegador web y el URL http://localhost:8000.

Para utilizar esta funcionalidad de artisan debe tenerse instalado mcrypt.  En caso de no contarse con esta librería seguir los pasos a continuación.

$ sudo aptitude install php5-mcrypt

$ sudo php5enmod mcrypt

Recursos

Introducción al MVC de Yii – Parte I

Introducción.

MVC es un patrón de diseño que separa de manera clara y precisa los tres componentes de una aplicación: el modelo, la vista y el controlador.  Su objetivo principal es el de separar la lógica del negocio de la lógica de la presentación para darle estructura a la implementación y facilitar con esto su posterior mantenimiento.

Como se mencionó anteriormente, el modelo consta de tres partes de acuerdo con la siguiente descripción.

  • El modelo representa la lógica de la aplicación, que se encuentra inmersa en los datos y en las reglas del negocio.
  • La vista hace referencia a la interfaz de usuario, a la presentación de la información.
  • El controlador actúa como mediador entre la solicitud del usuario y los modelos y vistas involucrados en la ejecución.
Estructura de una aplicación Yii.
Estructura de una aplicación Yii.

Aplicación.

  • Yii utiliza adicionalmente el patrón de diseño del controlador frontal.
  • Representado por el index.php y es el único punto de acceso del usuario a la aplicación web.
  • En el controlador frontal se crea la instancia de la Aplicación cuya función es la de recibir los requerimientos del usuario y remitirlos a los controladores apropiados para su posterior procesamiento.
  • Representa el contexto de ejecución del procesamiento de los requerimientos del usuario.
  • Puede accederse en cualquier momento a través del singleton Yii::app().
  • Su configuración se realiza en la ruta protected/config, especialmente en el archivo main.php.

Componente.

  • Su clase base es CComponent.
  • Implementan funcionalidades específicas con la suficiente flexibilidad para poderse compartir con otras aplicaciones.
  • Se pueden incluír y configurar facilmente en el archivo de configuración de la aplicación (arreglo components).
  • Se acceden a través de Yii::app() -> ComponenteId.
  • Su creación es perezosa, es decir, sus instancias sólo son creadas cuando son invocados por primera vez.  Es posible evitar este comportamiento listando los componentes bajo la sección preload de la configuración de la aplicación.
  • Sus propiedades pueden definirse de dos maneras.
    • Como atributos públicos de la instancia.
      • Su nombre es sensible a mayúsculas.
      • Su acceso es directo.
      • No permite definir restricciones sobre su modificación.
    • A través de los métodos de consulta y modificadores.
      • Su nombre no es sensible a mayúsculas.
      • Se deben definir los métodos getAtributo y setAtributo según se requiera el acceso.
      • La implementación de estos métodos permite filtrar el acceso a la propiedad.
  • Yii incluye en su distribución múltiples componentes listos para ser utilizados.

Controlador.

  • Su clase base es CController.
  • Su misión es la de dirigir la interacción entre los modelos (lógica del negocio) y las vistas (presentación) para producir el resultado que el usuario desea a partir de su requerimiento.
  • Contiene las acciones.
  • Determina la acción por defecto: index.  Es posible modificar su valor mediante CController::defaultAction.
  • El nombre de la clase es “{ControladorId}Controller“.
  • El nombre del archivo que lo contiene es “{ControladorId}Controller.php” y se ubica bajo la ruta protected/controllers.
  • La búsqueda para localizar una clase de un controlador se rige por las siguientes reglas.
    1. Si la aplicación define a CWebApplication::catchAllRequest se rige por esta propiedad y el controlador especificado en el requerimiento es obviado.
    2. Si se encuentra un alias para este controlador en CWebApplication::controllerMap es utilizado.
    3. Si para el requerimiento ruta/ControladorId se encuentra el archivo protected/controllers/ruta/ControladorIdController.php es utilizada la clase contenida en él.
    4. Si hay módulos definidos en la aplicación la búsqueda incluye sus rutas de manera similar a la regla anterior.
    5. El controlador no fue encontrado y se lanza una excepción CHttpException.

Acciones.

  • Corresponden a las actividades que el controlador puede realizar.
  • Pueden definirse de dos maneras.
    • Como un método de la clase controlador.
      • El nombre del método debe corresponder con el formato “action{AcciónId}”.
      • De esta manera, la acción agregar se encuentra implementada en el método actionAgregar.
    • Como una subclase de la clase CAction.
      • Se define una clase por cada acción.
      • El nombre de la clase corresponde con el formato “{AcciónId}Action“.
      • El nombre del archivo donde se almacena la acción tiene el formato “{AcciónId}Action.php” y se almacena bajo protected/controllers/{ControladorId}.
      • De esta manera, la acción agregar se encuentra implementada en la clase AgregarAction.
      • La clase debe implementar el método run donde se implementa la lógica de la acción.
        class AgregarAction extends CAction
        {
            public function run()
            {
                // Lógica de la acción.
            }
        }
      • Es necesario sobreescribir el método actions() del controlador para hacerle saber de la existencia de la acción.
        class UsuarioController extends CController
        {
            public function actions()
            {
                return array(
                    'adicionar' => 'application.controllers.usuario.AgregarAction'
                );
            }
        }
      • En el ejemplo anterior se le especificó al controlador de Usuario que la acción adicionar se relaciona con la clase AgregarAction definida anteriormente y que se encuentra ubicada bajo protected/controllers/usuario.

Filtros.

  • Se ejecutan antes y/o después de una acción.
  • Su finalidad es la de realizar verificaciones o adecuaciones antes y/o después de procesar la solicitud del usuario.  Como es el caso de verificar la autenticación del usuario antes de realizar cualquier acción administrativa.
  • Una acción puede tener múltiples filtros.
  • El orden de ejecución de los filtros corresponde con el orden de aparición en la lista.
  • Un filtro puede evitar que los siguientes filtros se ejecuten.
  • De manera similar a las Acciones, los Filtros pueden definirse de dos maneras.
    • Como un método de la clase Controlador.
      • El nombre del método debe corresponder con el formato “filter{FiltroId}”.
      • De esta manera, el filtro verificarAutenticacion se encuentra implementado en el método filterVerificarAutenticacion.
        public function filterVerificarAutenticacion($filterChain)
        {
            // El llamado a $filterChain -> run() continúa con la invocación del siguiente filtro.
        }
    • Como una subclase de la clase CFilter.
      • Se define una clase por cada filtro.
      • El nombre de la clase corresponde con el formato “{FiltroId}Filter“.
      • El nombre del archivo donde se almacena el filtro tiene el formato “{FiltroId}Filter.php” y se almacena bajo protected/filters.
      • De esta manera, el filtro estadisticas se encuentra implementado en la clase EstadisticasFilter.
      • La clase debe implementar los métodos preFilter y postFilter donde se implementa la lógica del filtro antes y después de la ejecución de la acción respectivamente.
        class EstadisticasFilter extends CFilter
        {
            public function preFilter($filterChain)
            {
                // Lógica del filtro previo a la ejecución de la acción.
        
                // Si $respuesta es 'true' se continúa con la ejecución de los filtros
                // y eventualmente con la ejecución de la acción.  Si es 'false' la
                // la ejecución de la acción es abortada.
        
                return $respuesta;
            }
        
            public function postFilter($filterChain)
            {
                // Lógica del filtro posterior a la ejecución de la acción.
            }
        }
  • En ambos casos es necesario indicarle al controlador la existencia del filtro.  Para esto se debe sobreescribir el método CController::filters() de manera que retorne la lista de filtros a ejecutarse por cada acción.
    class UsuarioController extends CController
    {
        // ...
    
        public function filters()
        {
            return array(
                'verificarAutenticacion + crear, editar',
    
                array(
                    'application.filters.EstadisticasFilter - crear, editar',
                    'activado' => true,
                    'frecuencia' => 'diaria'
                )
            );
        }
    }
  • El ejemplo anterior especifica dos filtros para el controlador Usuario con las siguientes características.
    • El filtro verificarAutenticacion esta basado en un método mientras que el filtro estadisticas está basado en una clase.
    • Se establecieron valores iniciales para la configuración del filtro de estadisticas.  Estos valores (activado = true y frecuencia = ‘diaria’) se asignan a los atributos del filtro en el momento de su construcción.
    • Los modificadores + y determinan sobre cuales acciones se ejecutan efectivamente los filtros.
      • El filtro verificarAutenticacion se le aplicará UNICAMENTE a las acciones crear y editar.
      • El filtro estadisticas se le aplicará a TODAS las acciones EXCEPTUANDO a las acciones de crear y editar.
      • Si no se especifica ningún modificador se sobreentiende que aplica a TODAS las acciones.

Modelo.

  • Su clase base es CModel.
  • Representa una unica fuente de datos como una fila de una tabla de una base de datos o un formulario con información provista por el usuario.
  • Cada campo de la fuente de datos se convierte en un atributo del modelo.
  • Cada atributo tiene una etiqueta y puede ser validado a partir de un conjunto de reglas.
  • Hay dos tipos de modelo según la fuente de datos que representan.
    • El modelo de formularios se representa con la clase CFormModel.
      • Mantiene información recibida a partir de un formulario del usuario para ser procesada y posteriormente descartada.
    • El modelo de registro activo se representa con la clase CActiveRecord.
      • Se basa en el patrón de diseño de Active Record.
      • Representa un registro de una tabla de una base de datos.
      • Permite su manipulación de una manera orientada a objetos.
      • Los campos del registro se convierten en atributos del modelo.

Enlaces.

Ejemplo 2 de Ajax con Prototype: interfaz de usuario

Introducción.

Hace un año largo que publiqué el primer ejemplo de Ajax utilizando Prototype: partes 1, 2, 3, 4 y demostración.  En esa época estaba apenas empezando a aprender acerca de los frameworks de JavaScript y había utilizado principalmente MooTools. Me gusta mucho probar diferentes métodos de hacer las cosas porque encuentro gratificante aprender cosas nuevas.  Ahora, un año después, he desarrollado dos proyectos con MooTools y estoy terminando exitosamente otro con Prototype y he aprendido bastante de estos frameworks como para mejorar sustancialmente este mini-tutorial.

Diseño.

La idea de esta nueva versión es la de implementar una aplicación similar que permita la administración de contactos.  Un contacto relaciona la siguiente información de una persona.

  • Nombres.
  • Apellidos.
  • Dirección de correo electrónico.
  • Fecha de nacimiento.

Las acciones que se podrán realizar sobre los contactos son las siguientes.

  1. Crear a un nuevo contacto.
  2. Editar a un contacto existente.
  3. Remover a un contacto existente.
  4. Listar los contactos registrados permitiendo filtrarlos por campo (nombres o apellidos) y por letra inicial del campo elegido.

Implementación.

Sin ser visualmente atractiva la interfaz de usuario se diseña para que sea funcional y que cumpla con las características del sistema propuesto. El siguiente es el esquema de lo que se desea implementar con la interfaz del usuario de la aplicación AJAX.

Esquema del UI
Esquema del UI

El diseño cuenta con las siguientes secciones.

  1. Cabecera con título, subtítulo y mensaje de estado.
  2. Pie de página.
  3. Formulario para la edición de los datos del contacto.
  4. Botones para elegir la acción a realizarse.
  5. Filtro de listado.
  6. Listado de contactos registrados.

Ese capítulo no requiere de demasiada información o esfuerzo con respecto al tema principal del tutorial, es simplemente un documento XHTML para el contenido con clases CSS para su presentación.  A continuación se referencian algunos apartes de su implementación que serán útiles tener en cuenta para los capítulos siguientes.

Referencia a el archivo con la hoja de estilos.

Los estilos se encuentran especificados en un archivo diferente ubicado en css/style.css, su referencia desde el archivo demo.php que contiene el código XHTML se realiza en la cabecera (head) de la siguiente manera.

<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>
<html xmlns='http://www.w3.org/1999/xhtml'>
<head>
    <!-- Meta Data -->
    <meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />
    <meta name='author' content='Jorge Iván Meza Martínez <jimezam@gmail.com>' />
    <meta name='description' content='Demostración del uso de Ajax con Prototype y PHP' />
    <!-- Site Title -->
    <title>Demostración de Ajax con Prototype y PHP</title>
    <!-- Link to standard Style Sheets -->
    <link href='css/style.css' type='text/css' rel='stylesheet' />
</head>
<body>
[...]
</body>
</html>

Debe tenerse en cuenta que para hechos prácticos la cadena […] reemplaza a una sección específica de código y no debe interpretarse de manera literal.

El mensaje de estado.

El mensaje de estado se utiliza para informarle al usuario que la aplicación web se encuentra realizando algún tipo de procesamiento asíncrono o para informarle el resultado del mismo.  Para esta aplicación se reservará un espacio para los mensajes en la parte inferior de la cabecera de la siguiente forma.

    <div id='mensaje'>
        &nbsp;
    </div>

El formulario de datos.

        <form id='formulario' action='#' method='post'>
            <input type='hidden' id='tId' name='tId' value='' />

            <fieldset>
                <legend class='SeccionTitulo'>Información del contacto</legend>

                <table>
                    <tr>
                        <td class='CampoTitulo'>Nombres</td>
                        <td><input class='CampoEstilo' type='text' id='tNombres' name='tNombres' size='30' maxlength='30' title='Nombres' /></td>
                    </tr>
                    <tr>
                        <td class='CampoTitulo'>Apellidos</td>
                        <td><input class='CampoEstilo' type='text' id='tApellidos' name='tApellidos' size='30' maxlength='30' title='Apellidos' /></td>
                    </tr>
                    <tr>
                        <td class='CampoTitulo'>Correo electrónico</td>
                        <td><input class='CampoEstilo' type='text' id='tCorreo' name='tCorreo' size='30' maxlength='128' title='Correo Electrónico' /></td>
                    </tr>
                    <tr>
                        <td class='CampoTitulo'>Fecha de nacimiento</td>
                        <td><input class='CampoEstilo' type='text' id='tFNacimiento' name='tFNacimiento' size='10' maxlength='10' title='Fecha de Nacimiento' /></td>
                    </tr>
                    <tr>
                        <td class='SeccionBoton' colspan='2'>
                            [...]
                        </td>
                    </tr>
                </table>
            </fieldset>
        </form>

En el formulario deben notarse varios aspectos interesantes para el trabajo con AJAX.

  • La etiqueta <form> no requiere de un action específico, por ello le indicamos la mísma página (#).
  • El identificador del formulario de los datos es formulario.  Este valor se utilizará frecuentemente desde el código JavaScript.
  • El campo para el código del contacto (tId) es oculto ya que es para el manejo interno únicamente.
  • Muchas funciones de Prototype como es el caso de Form.serialize se basan en el atributo name de la etiqueta en lugar del id habitualmente utilizado, por esto es altamente aconsejable definir los dos tal y como se realizó con los input [type=’text’] anteriormente.

Los botones de acción.

Estos botones van en la sección td.SeccionBoton justo donde se dejó una marca […] en el código anterior y se especifican de la siguiente manera.

<input class='Boton' id='bListar'  type='button' value='Listar' />
<input class='Boton' id='bNuevo'   type='button' value='Nuevo' />
<input class='Boton' id='bAccion'  type='button' value='Agregar' />
<input class='Boton' id='bRemover' type='button' value='Remover' />
  • Los botones son button y no submit porque su misión es la de iniciar a una función JavaScript, no la de enviar al formulario como normalmente se hace durante un postback.
  • La definición de los botones no incluye información del manejo de sus eventos (onClick) ya que por la separación de funciones: XHTML -> contenido, CSS -> presentación y JS -> funcionalidad; esta se definirá en el correspondiente archivo de funciones de JavaScript.
  • El botón bAccion servirá para crear registros o para editarlos de acuerdo con la selección del usuario.

El filtro de búsqueda.

Permitirá filtrar al listado de contactos registrados que se visualizarán en la sección de resultados de la búsqueda (6).  Se especifica de la siguiente manera. El primer componente de selección permite elegir la letra con la cual se filtrará el contenido o Todos si no se desea realizar un filtro.

                        <select id='sLetra' name='sLetra'>
                            <option value='todos'>Todos</option>

                        <?php

                            for($i=65; $i<=90; $i++)
                            {
                                $c = chr ($i);

                                echo "<option value='{$c}'>{$c}</option>n";
                            }

                        ?>

                        </select>

El segundo componente de selección permite especificar el campo sobre el cual se realizará el filtro, ya sea el de nombres o el de apellidos.

                        <select id='sCampo' name='sCampo'>
                            <option value='nom'>Nombres</option>
                            <option value='ape'>Apellidos</option>
                        </select>

De manera similar a lo sucedido con los botones de acción, para estos select no se define en este archivo ningún tipo de manejo de eventos.

El listado de contactos registrados.

En esta sección se mostrará el listado de contactos registrados filtrados según los criterios seleccionados en la sección anterior. Por ahora se encuentra vacío.

            <div id='Resultados'>
                &nbsp;
            </div>

Conclusiones.

En este primer capítulo se definió el proyecto a implementarse y se realizó la especificación de la interfaz de usuario sobre la cual se construirá la aplicación asíncrona. Este subproducto se encuentra separado en dos partes: la especificación del contenido (demo.php) y la especificación de su presentación (css/style.css).

En los siguientes capítulos de este tutorial se irá complementando paso a paso la aplicación de demostración, añadiéndosele diferentes funcionalidades hasta alcanzar el objetivo final aquí trazado.

Enlaces.

Autocompletador con Prototype/Scriptaculous – Parte II

Como complemento a la primera parte, en este artículo se ampliará el concepto del Autocompleter de Prototype/Scriptaculous a su versión mas flexible que hace uso del envío de mensajes asíncronos mejor conocidos como AJAX.

Para su implementación se requieren de dos partes las cuales se analizarán en el orden expuesto a continuación.

  • Un formulario HTML con el campo que incluye la autocompletación.
  • Una aplicación PHP que recibe y procesa la solicitud de información y genera los resultados de manera dinámica.

El formulario es igual al anterior, incluye el text donde el usuario escribirá el criterio que se autocompletará cuyo identificador es t_criterio y un espacio, cuyo identificador es resultado, donde se desplegarán las posibles sugerencias arrojadas por la aplicación PHP basada en el criterio antes especificado.

<div id='contenedor'>
    <div id='criterio'>
        Criterio
        <input type='text' id='t_criterio' size='50' maxlength='200' />
    </div>
    <div id='resultado'>
        No hay resultados que mostrar.
    </div>
</div>
Ejemplo de Ajax.Autocompleter
Ejemplo de Ajax.Autocompleter

Para activar el autocompletado del campo t_criterio se ejecuta el método estático Ajax.Autocompleter tan pronto como el árbol DOM de la página ha sido cargado de la siguiente manera.

new Ajax.Autocompleter('t_criterio',
                       'resultado',
                       'fuente.php',
                       {
                           paramName: 'criterio',
                           method: 'post',
                           parameters: 'estatico1=valor1&estatico2=valor2',
                           callback: function(editor, paramText) {
                               return paramText + '&time=' + new Date().getTime();
                           },
                           select: 'm_nombre',
                           afterUpdateElement: function (inputField, suggestion) {
                               inputField.value = suggestion.down('span.m_nombre').innerHTML + " / " + suggestion.down('span.m_departamento').innerHTML;
                           }
                       });

Los parámetros que recibe el método se describen a continuación, de los cuales sólo los tres primeros son obligatorios.

  • Id del text con el criterio de búsqueda para la autocompletación.
  • Id del área donde se van a mostrar las opciones sugeridas.
  • URL donde se ubica a la aplicación dinámica que va a procesar la búsqueda de sugerencias a través de AJAX.
  • Hash con opciones adicionales.
    • paramName: nombre del parámetro que se enviará a la aplicación dinámica con el contenido del texto especificado por el usuario.  Si no se especifica este parámetro, se utilizará el atributo name del campo de texto del formulario.
    • method: método a través del cual se enviará la información a la aplicación web: post o get.
    • parameters: parámetros estáticos para ser enviados adicionalmente.
    • callback: función ejecutada antes de ser invocada la aplicación PHP y que permite manipular de manera dinámica los parámetros enviados.
    • select: campo de la sugerencia elegida que es mostrado en el campo de texto cuando la sugerencia es seleccionada.  El valor de este campo se relaciona con el selector CSS donde se encuentra ubicada la información.
    • afterUpdateElement: función invocada justo después de que el usuario ha seleccionado a una de las opciones presentadas.  Permite componer de manera dinámica un valor para ser mostrado como la selección del usuario.  Es mas elaborado y sobreescribe lo especificado en el parámetro select.

Adicionalmente las opciones adicionales minChars y frequency se encuentran también disponibles y operan de igual manera que en el autocompletador local.

La aplicación dinámica (fuente.php) por su parte, desarrollada en PHP para este ejemplo, realiza las siguientes acciones.

Obtiene el valor del criterio de búsqueda especificado por el usuario.  La forma de recuperarlo depende del método (method) utilizado por el formulario.

$criterio = $_POST['criterio'];

Se realiza una consulta a la base de datos y/o un procesamiento de la información lo que constituye el carácter dinámico de la aplicación.  Para este ejemplo, se consulta la base de datos (SQLite utilizando PDO) de la división geopolítica de Colombia y se buscan los municipios cuyos nombres contengan a la subcadena especificada.

$link = new PDO('sqlite:data/database.sqlite');

$sql = "SELECT m.nombre AS nombre, d.nombre AS departamento,
               m.latitud AS latitud, m.longitud AS longitud
        FROM municipios m, departamentos d
        WHERE m.id_departamento = d.id_departamento AND
              m.nombre LIKE '%{$criterio}%'";

$result = $link -> query($sql);

El resultado enviado al formulario está constituído por la salida estándar (echo/print/etc.) de la aplicación PHP.  Su estructura es la de una lista no ordenada: <ul><li>…</li>…</ul>.

Para finalizar, se construye la lista no ordenada con la información obtenida de la base de datos.

$str = "<ul>n";

foreach ($result as $row)
{
    $str .= "<li>n";
    $str .= "<div class='titulo'>Título: <span class='m_nombre'>" . utf8_decode($row['nombre']) . "</span></div>";
    $str .= "<div class='titulo'>Departamento: <span class='m_departamento'>" . utf8_decode($row['departamento']) . "</span></div>";
    $str .= "<div class='titulo'>Latitud: <span class='m_latitud'>" . utf8_decode($row['latitud']) . "</span></div>";
    $str .= "<div class='titulo'>Longitud: <span class='m_longitud'>" . utf8_decode($row['longitud']) . "</span></div>";
    $str .= "</li>n";
}

$str .= "</ul>n";

echo utf8_encode($str);

La información almacenada e impresa en la variable $str es la que será mostrada en el área de sugerencias: #resultado.

Enlaces:

Autocompletador con Prototype/Scriptaculous – Parte I

Prototype, al igual que otros frameworks de JavaScript, permite implementar fácilmente comportamientos que que serían dispendiosos de crear a partir del JavaScript básico y convencional.  El hermanito Scriptaculous viene a ayudarnos aún mas con sus efectos y ayudantes.

Un ejemplo de estas posibilidades de rápida implementación es la simulación del autocompletar de un campo de texto.

El objeto Autocompleter acepta dos fuentes de datos diferentes.

  1. Una local como un arreglo de JavaScript.
  2. Una remota y mas dinámica a través de solicitudes AJAX.

En este artículo se va a realizar la demostración del uso de un Autocompleter.local.  Para esto se requieren dos componentes.

  • Un campo de texto donde el usuario escribe el criterio de búsqueda.
  • Una sección de sugerencias donde el Autocompleter despliega las coincidencias basadas en el criterio de búsqueda.
Ejemplo de Autocompleter.local
Ejemplo de Autocompleter.local

Para el ejemplo anterior cuenta con los siguientes elementos.

  • t_criterio: campo de texto para el criterio de búsqueda.
  • resultado: es el div donde se  desplegarán las sugerencias encontradas.
  • departamentos: es un arreglo JavaScript con la información local y estática para las opciones de búsqueda, en este caso el listado de los departamentos de Colombia.

Para activar el autocompletado sobre t_criterio basado en los valores de departamentos, se ejecuta la siguiente instrucción JavaScript.

var ac = new Autocompleter.Local('t_criterio',
                 'resultado',
                 departamentos,
                 {
                     autoSelect     : false,
                     frequency      : 0.4,
                     minChars       : 1,
                     choices        : 10,
                     paritialSearch : false,
                     paritialChars  : 2,
                     fullSearch     : true,
                     ignoreCase     : true
                 });

Los parámetros especificados al método Local son descritos a continuación.

  1. Id del campo de texto con el criterio de búsqueda.
  2. Id del contenedor de los resultados obtenidos.
  3. Arreglo con la información disponible.
  4. Hash con las opciones de configuración del autocompletador.  Todos estos valores son opcionales y los mostrados corresponden con los valores por defecto.

A continuación se describen las opciones de configuración disponibles.

  • autoSelect: Acepta automáticamente la primera sugerencia cuando sólo hay una.
  • frecuency: Tiempo en segundos entre dos intentos de autocompletar.
  • minChars: Cantidad mínima de carácteres necesarios para que se active el autocompletar.
  • choices: Cantidad máxima de opciones sugeridas a mostrarse.
  • paritialSearch: Tomar en cuenta para la búsqueda el comienzo de cada palabra o sólo el comienzo del texto.
  • paritialChars: Cantidad mínima de carácteres necesarios para que se active la búsqueda parcial.
  • fullSearch: Tomar en cuenta cualquier subcadena de las opciones o sólo los comienzos (palabra o línea).
  • ignoreCase: Tomar o no en cuenta la diferencia entre mayúsculas/minúsculas.

Enlaces:

La típica distribución de una página web

Distribución típica de una página web
Distribución típica de una página web

Esta es la distribución típica de una página web.  Esta compuesta por los siguientes elementos.

  • Cabecera.
  • Columna izquierda.
  • Contenido central.
  • Columna derecha.
  • Pies.

De acuerdo con los estándares actuales, es preferible utilizar divs en lugar de tables (donde sea humanamente posible) ya que el documento HTML debería encargarse de definir el contenido mientras que a través de la definición de estilos (CSS) se debería establecer la presentación, es decir, como se ordena y distribuye el contenido a lo largo y ancho de la página web.

Como lo he mencionado en otras ocasiones para mi CSS es todavía una ciencia oculta, hay muchas cosas que frecuentemente se me enrredan cuando utilizo CSS.  Sin embargo ultimamente lo he estado utilizando un poco mas y he adquirido mas práctica y cada vez me siento mas a gusto con utilizándolo.

Ahora implementar este diseño es algo sencillo para mi … bueno, casi.

Parto de la siguiente estructura de contenido.

        <div id='pagina'>
            <div id='cabecera'>
                Cabecera <br />
            </div>

            <div id='colizq'>
                Columna Izquierda <br />
            </div>

            <div id='contenido'>
                Contenido <br />
            </div>

            <div id='colder'>
                Columna Derecha <br />
            </div>

            <div id='pies'>
                Pies
            </div>
        </div>

El resultado inicial es claramente inaceptable.

Resultado #1
Resultado #1

Agregamos un poco de estilo para enrriquecer a la presentación.

    #pagina
    {
        width: 1024px;
        margin: auto;
        background-color: pink;
    }

Con esto le damos un ancho suficiente para visualizar correctamente el contenido del sitio y lo centramos para que se vea bonito en monitores mas anchos.

    #cabecera
    {
        padding: 5px;
        background-color: orange;
    }

    #colizq
    {
        padding: 5px;
        background-color: yellow;
        width: 300px;
    }

    #contenido
    {
        padding: 5px;
        background-color: blue;
        width: 394px;
    }

    #colder
    {
        padding: 5px;
        background-color: red;
        width: 300px;
    }

    #pies
    {
        padding: 5px;
        background-color: green;
    }

Les damos colores diferentes a cada una de las partes para diferenciarlas, un padding para separar un poco el borde del contenido de cada uno de los divs y en el caso de la parte central, el ancho específico que se requiera.

Resultado #2
Resultado #2

Ya tiene cara de algo, pero no necesariamente lo que estamos buscando.  Es necesario ordenar las columnas izquirda, centro y derecha para que se ubiquen de la forma como sus mismos nombres lo indican.

En CSS esto equivale a decirle a los divs que floten (float) en la dirección que queramos.

    #colizq
    {
        float: left;
        padding: 5px;
        background-color: yellow;
        width: 300px;
    }

    #contenido
    {
        float: left;
        padding: 5px;
        background-color: blue;
        width: 394px;
    }

    #colder
    {
        float: left;
        padding: 5px;
        background-color: red;
        width: 300px;
    }

El atributo adicionado les indica a los divs que vayan flotando y arreglándose a la izquierda cada vez que son agregados a la presentación.

Resultado #3
Resultado #3

En algunos casos, cuando continúan mas divs y necesitamos que se alineen en una la siguiente fila, es necesario indicarle a un div específico que no permita que otros divs floten a uno/ninguno de sus lados.  Para esto se utiliza el atributo clear (left, right, none, both).

Para el caso de la sección de los pies se complementaría de la siguiente manera.

    #pies
    {
        padding: 5px;
        background-color: green;
        clear: left;
    }

Hasta ahí todo es muy fácil: un pequeño paso para el lector, pero hace un año fue un gran paso para mi.  Pero aún me queda una duda por solucionar.

Qué pasa cuando las columnas -patriotas- tienen altos de diferente longitud ?

Resultado #4.
Resultado #4.

Se nota ese sobrante rosado bajo las columnas izquierda y derecha ?

Si revisamos las clases CSS, la rosada es la #pagina, esto significa que #colizq y #colder no están ocupando el mismo espacio vertical que #contenido hasta #pies y está dejando ver la capa inmediatamente inferior.

Para los “diseños” que comúnmente utilizo esto no es un problema ya que me gustan los diseños minimalistas y el blanco es mi fondo preferido.  Pero que va a pasar el día en que necesite que una columna sea de un fondo diferente del resto ?

Mi pregunta es: cómo hago para que las columnas ocupen la totalidad del espacio vertical disponible entre #cabecera y #pies ?  La duda persiste.

Enlace: código de demostración.

Primer ejemplo de XML-RPC

Estimando que probablemente uno de los proyectos de la Fundación requiera la consulta de información de uno de los proyectos anteriores ubicado en uno de los servicios de hosting, decidí realizar algunos experimientos con XML-RPC para determinar la viabilidad de utilizarlo para esta tarea en caso de que sea realmente necesaria. Debido a que el hosting en el que se encuentra el proyecto tiene una versión bastante antigua de PHP4 se decidió utilizar la librería de http://phpxmlrpc.sourceforge.net/.

Este es mi primer contacto con XML-RPC. Había leido un poco del tema en la documentación del framework CodeIgniter que planeo utilizar dentro de poco en mis próximos proyectos, sin embargo como está basado en PHP5 y el hosting soporta únicamente PHP4 decidí probar con la librería mencionada anteriormente, la cual es bastante mas enrredada, pero por suerte bien documentada, aunque hacen falta ejemplos.

En este pequeño demo que implementé se envían por parte del cliente un nombre (“Pepe”) y la solicitud de ejecución de un procedimiento remoto (“HelloWorld.show”). El servidor relaciona este método remoto con un procedimiento local (“show”) que retorna la cadena resultante (“Hello Pepe”).

El Cliente.

Se realiza el include de la librería de XML-RPC.

include_once (“../lib/xmlrpc-2.2/lib/xmlrpc.inc”);

Se define la ubicación del servidor y se instancia un objeto de la clase cliente.

$host = “localhost”;
$location = “xmlrpc/d1”;
$server = “ServerHello.php”;

$client = new xmlrpc_client(“http://{$host}/{$location}/{$server}”);

Se prepara el mensaje que enviará el cliente. El mensaje define cual es el procedimiento remoto a ejecutarse y la información relacionada con este (parámetros). Para el ejemplo se invocará al procedimiento HelloWorld.show y se le enviará “Pepe” que es de tipo string.

$message = new xmlrpcmsg(“HelloWorld.show”, array(new xmlrpcval(“Pepe”, “string”)));

Si se desea consultar los mensajes de depuración se debe activar la siguiente expresión.

// $client -> setDebug(3);

Con todo listo, se envía la solicitud al servidor y se captura su respuesta.

$response = $client -> send($message);

Si la respuesta referencia a un error, este viene con su respectivo código de fallo, en caso contrario se puede consultar su contenido.

if ($response -> faultCode())
echo “<br>KO. Error: ” . $response -> faultString();
else
echo “<br>OK. got … ” . $response -> value() -> scalarVal();

El Servidor.

Se realiza el include de la librería de XML-RPC, se incluyen los dos archivos inc: cliente y servidor.

include_once (“../lib/xmlrpc-2.2/lib/xmlrpc.inc”);
include_once (“../lib/xmlrpc-2.2/lib/xmlrpcs.inc”);

Se instancia un objeto de clase servidor en la que se especifica la siguiente información de los procedimientos remotos expuestos como disponibles:

  • Relación entre nombre de procedimiento remoto y función PHP a ejecutarse en su llamado.
  • La firma de la función (signature): tipo de retorno y de parámetros requeridos. Una función puede tener varias firmas.
  • Documentación de la función (docstring).

$server = new xmlrpc_server(array(
“HelloWorld.show” => array(“function” => “show”,
“signature” => array(array($xmlrpcValue, $xmlrpcString)),
“docstring” => “Shows a greeting message with the specified name.”)
), false);

Si se desea consultar los mensajes de depuración se debe activar la siguiente expresión.

// $server -> SetDebug(3);

Cuando todo está listo se envía la respuesta al cliente.

$server -> service();

Como siguiente paso se debe definir la función PHP show que será ejecutada cuando el procedimiento remoto HelloWorld.show sea solicitado.

La función recibe la información enviada por el cliente ($xmlrpcmsg) y tiene acceso a la variable $xmlrpcerruser la cual indica el número del error a partir del cual se encuentran disponibles para los errores definidos por el usuario.

function show($xmlrpcmsg)
{
global $xmlrpcerruser;

Verifico que el número de parámetros enviados por el cliente sea de uno, en caso contrario envío un código de error.

if ($xmlrpcmsg -> getNumParams() != 1)
{
return new xmlrpcresp(0, $xmlrpcerruser+1, “I am expecting a parameter for the name!”);
}

Obtengo el valor del parámetro disponible.

$val0 = $xmlrpcmsg -> getParam(0);

if($val0 -> kindOf() != “scalar”)
{
return new xmlrpcresp(0, $xmlrpcerruser+2, “I am expecting a string as parameter for the name!”);
}

$name = $val0 -> scalarval();

Compongo la cadena resultante: “Hello ” + valor del parámetro y la empaqueto en un mensaje de respuesta (xmlrpcresp) el cual es de tipo string y es enviado hacia el cliente.

return new xmlrpcresp(new xmlrpcval(“Hello ” . $name, “string”));
}

El demo #1 en ejecución puede ser consultado en http://demo.jorgeivanmeza.com/PHP/XMLRPCDemo/0.1/ClientHello.php

La documentación en línea de XML-RPC for PHP puede ser consultada en http://phpxmlrpc.sourceforge.net/doc-2/.