3.2. Breve guía para crear una pantalla

3.2.1. Introducción

A continuación vamos a explicar los pasos a seguir para crear una pantalla. Antes de llegar a este punto se tiene que tener claro el tipo de pantalla que se quiere crear.

Dependiendo del tipo de ventana seleccionado, el contenido de los ficheros será diferente. En el directorio igep/doc/plantillasTipo hay un modelo base a seguir de cada uno de los ficheros. En la siguiente tabla aparecen los diferentes patrones con los nombres de ficheros que deberemos buscar.

patrón plantilla
Patrón Tabular / Tabular sin búsqueda P1M2(FIL-LIS)
Patrón Tabular sin búsqueda P1M1(LIS)
Patrón Registro P1M2(FIL-EDI)
Patrón Registro sin búsqueda P1M1(EDI)
Patrón Tabular-Registro P1M2(FIL-LIS-EDI)
Patrón MD Maestro Tabular - Detalle Registro P2M2(FIL-LIS)M1(EDI)
Patrón MD Maestro Registro - Detalle Registro P2M2(FIL-EDI)M1(EDI)
Patrón MD Maestro Registro - Detalle Tabular P2M2(FIL-EDI)M1(LIS)
Patrón MD Maestro Registro - Detalle Tabular-Registro P2M2(FIL-EDI)M1(LIS-EDI)
Patrón Árbol  

gvHidra está basado en la arquitectura MVC, por tanto al crear una pantalla tenemos que crear y modificar varios ficheros que controlen las diferentes partes de la arquitectura:

  • Fichero/s del Modelo (directorio actions): se creará un fichero que controla toda la lógica de negocio del panel.

  • Fichero de la Vista (directorios views y plantillas): se crearán unos ficheros que, por un lado controlan la interfaz antes de presentar los datos, y por otro lado, la distribución de los componentes en la pantalla.

  • Fichero de Controlador (ficheros mappings.php y menuModulos.xml): se modificarán estos ficheros para indicar qué acción se resuelve con qué clase.

Dentro del modelo, cabe tener en cuenta que el programador interacturá con el usuario a través de dos flujos: flujo de entrada y flujo de salida. El flujo de entrada se compone de la información que viene de la pantalla y se gestiona con:

  • Tablas de trabajo: Son las tablas con las que el framework va a trabajar. Es decir, las tablas sobre las que vamos a realizar operaciones con los datos que recibamos.

  • Matching: Este método nos sirve para indicarle al framework en que se traduce cierto campo de la pantalla. Se utiliza únicamente en los flujos de entrada: construcción de la where de la operación, del Insert, Update, ...

El flujo de salida se compone de la información a mostrar por pantalla y se gestiona a través de las consultas que definimos en el constructor de la clase:

  • SearchQuery: es la consulta que se lanzará al ejecutar la acción "buscar".

  • EditQuery: es la consulta que se lanzará al ejecutar la acción "editar".

Conviene refrescar estos dos conceptos una vez concluido el siguiente ejemplo.

3.2.2. Genaro: Generación automática en gvHidra

Desde las versiones 3.1.x, gvHidra cuenta con una herramienta de generación de código que nos permite generar de forma sencilla y rápida un mantenimiento. Esta herramienta, Genaro, a través de 5 parámetros básicos, se conecta a la base de datos y construye una ventana funcional. Los mantenimientos tienen la lógica necesaria para realizar la gestión de búsqueda, altas, bajas y modificaciones al 100%; pero no tienen lógica específica de la aplicación (validaciones específicas,...). Por esta razón, en la mayoría de los casos, debe entenderse como un punto de partida para nuestras ventanas.

3.2.2.1. ¿Qué genera?

La versión actual del genaro realiza por nosotros la generación de los siguientes ficheros:

  • actions: genera las clases manejadoras (1 o N dependiendo del patrón). Estas clases tienen las consultas, las asociaciones con los campos, las asociaciones entre los maestros y sus detalles (en el caso de estos patrones) y los tipos de datos (con las validaciones de los mismos).

  • views: genera el fichero de la vista.

  • plantillas: genara la plantilla tpl de la ventana.

  • include: edita el fichero include para incluir en el proyecto las nuevas clases.

  • mappings: edita el fichero de mapeos para asociar las acciones con la clase que las gestiona (las nuevas clases manejadoras).

  • menuModulos.xml: edita el menu para que aparezca la nueva entrada. Agrupa esta entrada por el módulo.

3.2.2.2. ¿Cómo lo instalo?

En primer lugar tenemos que descargar el paquete de la web de gvHidra o directamente la última versión desde la forja de OSOR.

Una vez descargado, lo dejamos en el directorio include de nuestra aplicación gvHidra. Si no tenemos aun la estructura de la misma consultar el punto "Instalación del entorno" en este mismo manual.

El genaro, recoge la información de las conexiones de nuestra aplicación, por lo que necesitamos que estén definidas en el fichero gvHidraConfig.inc.xml de la aplicación (ubicado en la raiz del proyecto).

Una vez definidas, debemos acceder a la herramienta http://<ubicacionAplicacion>/include/genaro. Por comodidad, se recomienda añadir esta entrada de menu en el menu de herramientas. Al acceder a esta url nos aparecerá el siguiente formulario.

Finalmente, para poder utilizar la herramienta tenemos que dar una serie de permisos de lectura/escritura ya que va a ficheros en nuestra estructura. Por ello, se debe dar permisos de lectura/escritura al usuario del servidor web (generalmente el usuario de Apache) a todo nuestro proyecto.

NOTA IMPORTANTE: Por seguridad, tanto el tema de permisos como el acceso a esta herramienta deben revisarse antes de sacar a producción. Se trata de una herramienta de desarrollo y SOLO debería estar disponible en dichos entornos.

3.2.2.3. ¿Cómo utilizarlo?

En primer lugar, tenemos que escoger la conexión de base de datos que queremos utilizar. Para ello, en el desplegable de la parte superior derecha (al lado de las solapa de Patrón Maestro detalle), seleccionamos el dsn deseado. Los ids que aparecen, son los que hemos configurado en nuestra aplicación.

Una vez seleccionada la conexión, debemos decidir si vamos a generar un patrón simple o un patrón maestro detalle. Vamos a revisar cada una de las ventanas.

Para genarar un patrón simple tenemos la siguiente ventana

Debemos indicar los siguientes parámetros:

  • Nombre del módulo: es un agrupador de mantenimientos. Nos permite agrupar los mantenimiento de nuestra aplicación de forma que sea más sencillo su manejo.

  • Clase Manejadora: el nombre de la clase.

  • Tabla de la bd: tabla sobre la que queremos realizar el mantenimiento.

  • Patrón: seleccionamos la forma de visulización de la información (Tabular, Registro o Tabular-Registro).

Pulsamos generar y aparece la siguiente ventana:

Vamos a nuestra aplicación. Entramos de nuevo desde la validación (es importante porque borramos caches y cookies). Vemos que se ha creado una nueva entrada de menú y el resultado será algo como esto:

Nota: recomendamos ver en el código fuente todos los ficheros generados para entender la estructura del código.

Para generar patrones maestro detalle tenemos la siguiente ventana

Debemos indicar los siguientes parámetros:

  • Nombre del módulo: es un agrupador de mantenimientos. Nos permite agrupar los mantenimientos de nuestra aplicación de forma que sea más sencillo su manejo.

  • Seccion Maestro:

    • Clase Manejadora: el nombre de la clase del maestro

    • Tabla: tabla de la base de datos del maestro.

    • Clave Primaria: campos que componen la clave primaria del maestro.

    • Patrón: Seleccionamos la forma de visualización del maestro (Tabular o Registro).

    • Numero de detalles: número de detalles que va a tener nuestra ventana.

  • Seccion Detalle:

    • Clase Manejadora: el nombre de la clase del detalle

    • Tabla: tabla de la base de datos del detalle.

    • Clave Ajena: campos que relacionan la tabla con el maestro. Corresponde con los campos que son clave ajena referenciada a los campos de la clave primaria de la tabla maestro. Si son varios, debe respetarse el orden que se ha indicado en el maestro.

    • Patrón del detalle: Seleccionamos la forma de visualización del detalle (Tabular, Registro o Tabular-Registro).

Pulsamos generar y aparece la siguiente ventana

Vamos a nuestra aplicación. Entramos de nuevo desde la validación (es importante porque borramos caches y cookies). Vemos que se ha creado una nueva entrada de menú y el resultado es el siguiente:

3.2.3. Creación de un mantenimiento de forma manual

Vamos a hacer un pequeño recorrido sobre como crear una pantalla de forma manual. Esto puede ser de mucha utilidad para todos los que estan empezando, para aprender la estructura del proyecto.

  1. En primer lugar tenemos que saber que tipo de pantalla necesitamos (ver tabla anterior), con ello sabremos exactamente cuantos ficheros necesitamos para la parte de control de negocio (directorio actions). Este fichero php (de aquí en adelante le llamaremos clase manejadora) será el que controlará la lógica de negocio, toda ella o puede compartirla apoyándose en clases de negocio adicionales cuando la aplicación lo requiera. Estas clases adicionales, por organización, las localizaremos en un directorio llamado class al nivel de actions.

    Los patrones los clasificamos en dos, simples y complejos. En los primeros entra el patrón tabular, patrón registro y el tabular-registro, y en los segundos, los maestro-detalle y el árbol. Esta diferencia se basa en que para los simples solamente es necesaria una clase manejadora, y para los complejos es necesaria más de una. Concretamente, en los maestro-detalle necesitaremos una clase manejadora para el maestro y una por cada detalle que tengamos, y para el árbol, se necesitará una por cada opción que vaya a tener el menú lateral.

    La clase manejadora heredará de gvHidraForm_DB, que contiene el comportamiento general de un mantenimiento común (inserciones, modificaciones y borrados) contra un SGBD relacional. Un ejemplo tipo de para un patrón Tabular podría ser el siguiente:

    <?php
    
    /* CLASE MANEJADORA */
    
    class TinvEstados extends gvHidraForm_DB 
    {
    
       public function __construct() 
       {
    
          /*manejador de conexión*/
          $conf = ConfigFramework::getConfig();
          // dsnAplicacion: será el que se haya definido en gvHidraConfig.xml
          $dsn = $conf->getDSN('dsnAplicacion');
    
          //Las tablas sobre las que trabaja
          $nombreTablas= array('tinv_estados');
          parent::__construct($dsn,$nombreTablas);
    
          //La select que mostramos
          $this->setSelectForSearchQuery("SELECT cestado as \"lisCodigoEstado\", 
                                                     destado as \"lisDescEstado\" FROM tinv_estados");
          //El orden de presentación de los datos
          $this->setOrderByForSearchQuery("cestado");
    
          /* Añadimos los Matching - Correspondecias elemento TPL <-> campo de BD */
          $this->addMatching("filCodigoEstado","cestado","tinv_estados");
          $this->addMatching("filDescEstado","destado","tinv_estados");
    
          $this->addMatching("lisCodigoEstado","cestado","tinv_estados");
          $this->addMatching("lisDescEstado","destado","tinv_estados");
       }
    }
    ?>

    Del contenido de la clase podemos destacar:

    • La clase hereda de gvHidraForm_DB (línea 5). Esto se debe a que, tal y como hemos comentado, dicha clase es la que nos proporciona el comportamiento genérico de gvHidra en relación a las acciones contra una BBDD relacional. En otros casos se puede heredar directamente de gvHidraForm.

    • Una vez en el constructor debemos indicar la fuente de datos (líneas 12-13). Primero cargamos el fichero (gvHidraConfig.inc.xml) de configuración del framework, del custom y el de la propia aplicación. De esa configuración que nos ofrece el fichero cargamos el dsn que hemos definido para la aplicación, en la variable $dsn tendremos una referencia a la fuente de datos sobre la que se quiere trabajar. Si tenemos más de una fuente de datos, ver el capítulo Fuente de datos.

    • Debemos llamar al constructor del padre (línea 17) e instanciar una serie de propiedades que marcarán el comportamiento del panel. Se le pasa como parámetro la conexión definida anteriormente y el nombre de las tablas que va a mantener el panel ($nombreTablas en el ejemplo).

    • Debemos inicializar la consulta de la base de datos mediante el método setSelectForSearchQuery(). Es importante que en la select los nombres de los campos correspondan con los que luego utilizaremos en la plantilla (fichero tpl) En el ejemplo se utilizan alias para asegurar esa concordancia de nombres, esta es una medida aconsejable para evitar confusiones.

      NOTA: Ver que el alias se ha definido con un prefijo "lis" porque estamos en un patrón Tabular, la consulta nos devolverá los datos que se mostrarán en el tabular. Aconsejamos el uso de esta notación, prefijo "fil" si el campo corresponde a un panel de búsqueda, "edi" si corresponde a uno de un panel registro y "lis" si corresponde a uno de un panel tabular.

      IMPORTANTE: No se recomienda usar en la select la palabra clave "DISTINCT", ya que cuando el gestor de base de datos es Oracle, gvHidra introduce una condición en el WHERE (usando la pseudocolumna ROWNUM) para limitar el número de filas, y el resultado sería incorrecto ya que primero se aplica el ROWNUM y después el DISTINCT.

    • Opcionalmente se puede modificar el WHERE y el ORDERBY de la consulta anterior mediante los métodos correspondientes. En el ejemplo vemos que se modifica el ORDERBY (línea 23), si quisiéramos modificar el WHERE que gvHidra realiza por defecto utilizaríamos el siguiente método setWhereForSearchQuery().

    • El siguiente paso a realizar es dar la correspondencia entre los nombres de los campos (en la consulta) y los nombres en la plantilla (tpl). Cuando los datos son devueltos desde el panel a la capa de negocio, se necesita realizar una conversión, ya que los nombres no tienen porque coincidir (independizamos la presentación de la fuente de datos). Para realizar este proceso (que hemos denominado matching) se debe utilizar un método heredado llamado addMatching. En la llamada a este método le hemos de indicar 3 parámetros, que por este orden, corresponden a: nombre del campo en la TPL, nombre del campo en la consulta (el nombre del campo o el alias utilizado en la SELECT) y nombre de la tabla de la BD a la que pertenece. Para evitar problemas con los nombres de columnas entre distintos SGBD (oracle y postgresql por ejemplo), conviene usar siempre el 'as' en los nombres de columna, usando un alias preferiblemente en minúsculas.

    ATENCIÓN: Todo lo expuesto se corresponde con un panel con sólo dos modos (tabular o registro). Si se tiene un panel con tres modos (tabular-registro) se deben inicializar además las siguientes propiedades:

    • Utilizar el método setPKForQueries() para indicar que campos forman parte de la clave primaria del mantenimiento. Estos campos son los que se utilizarán para realizar el paso del modo de trabajo TABULAR al modo FICHA (registro).

    • Inicializar la SELECT que contiene la consulta a realizar en el panel de FICHA con el método setSetSelectForEditQuery(). Es decir, la select que se lanzará cuando tras seleccionar una o más tuplas en tabular y pulsamos el botón editar, nos pasará a la FICHA.

    • Opcionalmente se puede rellenar la WHERE y el ORDERBY de la edición con los métodos correspondientes, setWhereForEditQuery() y setOrderByForEditQuery().

    Hasta aquí tenemos una clase manejadora muy sencillita para una pantalla muy básica. Podemos tener la necesidad de que la interfaz sea un poco más dinámica, por ejemplo, tener listas y campos dependientes, explicado al final de este capítulo y en el capítulo 4 Elementos de pantalla avanzados.

    También es importante que el programador sepa de la existencia de algunos métodos que puede sobrecargar para dotar de cierto comportamiento particular a las funciones genéricas de gvHidra. Por ejemplo, puede ser muy útil realizar validaciones previas a una operación en la base de datos o especificar restricciones complejas en la WHERE de una consulta dependiendo de los valores seleccionados por el usuario. Estos métodos son los explicados en el capítulo 1, punto 2 Lógica de negocio.

    Antes de pasar al siguiente paso vamos a dar un ejemplo de estos métodos. Concretamente, vamos a forzar que cuando se inserten tuplas en nuestro ejemplo, la descripción aparezca siempre en mayúsculas.

    public function preInsertar($objDatos) 
    {
       while($v_datos=$objDatos->fetchTupla()) 
       {
          $v_datos['descEstado'] = strtoupper($v_datos['descEstado']);
          $objDatos->setTupla($v_datos);
       }
       return 0;
    }//Fin de PreInsertar
  2. El siguiente paso es indicar al controlador que clase es la encargada de realizar las distintas operaciones del panel y donde se debe ir dependiendo del resultado de la operación realizada. Para ello editamos el fichero mappings.php (directorio include de la aplicación) y añadimos las operaciones que correspondan a dicho panel.

    Para cada operación necesitamos definir quien gestiona cada operación, y donde debe redirigirse en cada posible respuesta de esa operación, si es correcta o no la respuesta.

    Con la función _AddMapping indicamos que clase se encarga de gestionar cada operación. De esta función los parámetros que nos interesan son los dos primeros. Con el primero indicamos el nombre de la operación, este nombre debe seguir el siguiente patrón nombreClaseManejadora__nombreAccion, los dos nombres están separados por dos subguiones. El segundo parámetro indica el nombre de la clase manejadora que tratará las operaciones (en el ejemplo TinvEstados).

    En nombreAccion nos podemos encontrar las siguientes palabras reservadas:

    • iniciarVentana: Acción que se lanzará cuando se quiera incluir en un panel algo que tenga que ser cargado desde BD o alguna comprobación previa a la carga de la ventana. Si se produce algún error (gvHidraError) en este momento nos redirigirá a la pantalla de entrada de la aplicación (igep/views/aplicacion.php).

      Por ejemplo poner en un panel de búsqueda una lista desplegable cargada desde BD.

    • buscar: Acción que normalmente se dispara desde los paneles de filtro. Comprueba si la búsqueda tiene parámetros y lanza la SELECT definida en la clase manejadora para la búsqueda.

    • operarBD: Acción que realiza las tres operaciones básicas de un mantenimiento: Insertar, Borrar y Modificar. Esta acción es exclusiva para trabajar con patrones de un solo modo, patrón Tabular o patrón Registro con o sin búsqueda.

    • nuevo: Acción que se lanza al pulsar al botón de nuevo registro en un panel. Se utiliza para cargar listas u otros componentes, inicializar datos antes de que el usuario empiece la inserción de datos.

    • editar: Acción que se lanza al pulsar el botón modificar de un panel, concretamente cuando nos encontramos en un Tabular-Registro. Se lanzará la consulta definida para la edición en la clase manejadora correspondiente.

    • insertar: Acción exclusiva para cuando se trabaja con un patrón Tabular-Registro. Se lanza cuando se va a insertar el registro nuevo.

    • modificar: Acción exclusiva para cuando se trabaja con un patrón Tabular-Registro. Se lanza cuando se van a guardar las modificaciones hechas en el panel.

    • borrar: Acción exclusiva para cuando se trabaja con un patrón Tabular-Registro. Se lanza cuando se va a efectuar la operación de borrado.

    • cancelarTodo: Acción que, como su propio nombre indica, cancela todo, es decir, elimina el contenido de la última consulta y de la última edición.

    • cancelarEdicion: En este caso, esta acción solamente elimina el contenido de la última edición.

    • recargar: Acción exclusiva para ventanas maestro-detalle. Acción que se lanza al paginar en un maestro para recargar los detalles.

    Ahora tenemos que definir las redirecciones dependientes de las respuestas a la operación, para ello tenemos el método _AddForward. Este método necesita de tres parámetros, siendo el primero el mismo que para la función _AddMapping, el segundo parámetro será una palabra clave que identifica la respuesta a la operación, y el tercer parámetro será la ruta donde se redirigirá la respuesta, normalmente será a un fichero de la presentación del directorio views.

    Las acciones genéricas, que corresponden con las acciones enumeradas antes, tienen unos retornos fijos que dependerán del resultado de la operación, las palabras clave de respuesta para estas acciones son gvHidraSuccess, gvHidraError, gvHidraNoData, significando: todo correcto, ha habido error, no hay datos, respectivamente. Las acciones particulares tendrán tantos retornos como el programador crea conveniente. A continuación mostramos la parte del mappings que correspondería a nuestro ejemplo:

    <?php
    
    class ComponentesMap extends gvHidraMaps 
    {
            /**
             *      constructor function
             *      @return void
             */
      
            function ComponentesMap () 
            {
                parent::gvHidraMaps();
                ...
                $this->_AddMapping('TinvEstados__iniciarVentana', 'TinvEstados', '', 'IgepForm', 0);
                $this->_AddForward('TinvEstados__iniciarVentana', 'gvHidraSuccess', 'index.php?view=views/tablasMaestras/p_estados.php');
                //
                $this->_AddForward('TinvEstados__iniciarVentana', 'gvHidraError', 'index.php?view=igep/views/aplicacion.php');
    
                $this->_AddMapping('TinvEstados__buscar', 'TinvEstados', '', 'IgepForm', 0);
                $this->_AddForward('TinvEstados__buscar', 'gvHidraSuccess', 'index.php?view=views/tablasMaestras/p_estados.php&panel=listar');
                $this->_AddForward('TinvEstados__buscar', 'gvHidraError', 'index.php?view=views/tablasMaestras/p_estados.php&panel=buscar');
                $this->_AddForward('TinvEstados__buscar', 'gvHidraNoData', 'index.php?view=views/tablasMaestras/p_estados.php&panel=buscar');
    
                $this->_AddMapping('TinvEstados__operarBD', 'TinvEstados', '', 'IgepForm', 0);
                $this->_AddForward('TinvEstados__operarBD', 'gvHidraSuccess', 'index.php?view=views/tablasMaestras/p_estados.php&panel=listar');
                $this->_AddForward('TinvEstados__operarBD', 'gvHidraError', 'index.php?view=views/tablasMaestras/p_estados.php&panel=listar');
                $this->_AddForward('TinvEstados__operarBD', 'gvHidraNoData', 'index.php?view=views/tablasMaestras/p_estados.php&panel=buscar');
                ...
             }
    }
  3. A continuación debemos crear un archivo php en el directorio views que controle la presentación. En él se definen las asignaciones de los datos a la plantilla (tpl). Si se trata de un comportamiento genérico, hay que instanciar la clase IgepPantalla (línea 3) que define el comportamiento general de una pantalla. En la línea 5 se carga la clase IgepPanel, que nos permite tener accesibles las funciones de acceso al panel, pasándole dos parámetros, el primero será el nombre de la clase manejadora que corresponde a ese panel, y el segundo, smty_datosTabla, es el nombre de una variable smarty que se habrá definido en la tpl correspondiente al panel, en el siguiente punto se explica. Una vez aquí tenemos que activar las pestañas laterales que nos permitirán cambiar de modo (en el ejemplo búsqueda/tabular), esto lo haremos con el método activarModo, al que se le pasan dos parámetros, el primero es el panel al que hará referencia ("fil" -> panel búsqueda, "lis"-> panel tabular, "edi"-> panel registro), y el segundo parámetro es nombre de un parámetro que indica el estado de la pestaña ("estado_fil"->pestaña filtro, "estado_lis"->pestaña tabular, "estado_edi"->pestaña registro), si está activa o no.

    Una vez definido todo tenemos que agregar el panel a la ventana, esto lo haremos con el método agregarPanel(). Y por último, ya solo nos queda mostrar la tpl en pantalla, para ello utilizamos el método display(), que es un método propio de smarty, siendo la variable $s una instancia de la clase Smarty_Phrame.

    A continuación mostramos un fichero de ejemplo de una ventana con un panel de dos modos:

    <?php
    //Creamos una pantalla
    $comportamientoVentana = new IgepPantalla();
    
    //Creamos un panel
    $panel = new IgepPanel('TinvEstados',"smty_datosTabla");
    
    //Activamos las pestañas de los modos que tendremos en el panel
    $panel->activarModo("fil","estado_fil");
    $panel->activarModo("lis","estado_lis");
    
    //Agregamos el panel a la ventana
    $comportamientoVentana->agregarPanel($panel);
    
    //Realizamos el display
    $s->display('tablasMaestras/p_estados.tpl');
    ?>
  4. Ahora vamos a diseñar la plantilla (tpl) de la pantalla en el directorio plantillas de la aplicación. En la tpl diseñaremos como quedará al final la pantalla, con una combinación de plugins propios de gvHidra, etiquetas propias de smarty y etiquetas html haremos el diseño. Las plantillas tendrán todas una estructura común a partir de la cual podremos ir particularizando nuestra pantalla.

    En ella creamos los componentes necesarios para nuestra pantalla, ajustando todos los parámetros de cada plugin con los nombres dados en la clase manejadora y las acciones correspondientes del mappings. Lo veremos más detallado en el punto 3 Diseño de pantalla con smarty/plugins.

    Precauciones a tener en cuenta a la hora de diseñar una tpl:

    • Los identificadores de campos pueden estar en cualquier combinación de mayúsculas y minúsculas, aunque no debe haber dos donde sólo cambie esto (es decir, case-insensitive).

    • Hay que evitar poner atributos o tags innecesarios, ya que eso se traduce en una página HTML de mayor peso. Por ejemplo, si una columna es oculta, no tiene sentido indicar su tamaño, obligatoriedad, ...

    • Los carácteres especiales como acentos o eñes conviene ponerlos usando entidades html, para que no hay problemas con la codificación.

    • No conviene usar el carácter '_' en las tpls. Mejor darle otro nombre mediante alias en la select.

    • Cuando en la tpl exista una ficha ({CWFicha}) habrá que distribuir los campos dentro de ella. En principio si son pocos campos con un simple salto de línea (<br />) se podrían dibujar, pero cuando se complica más se creará una tabla con los parámetros que se indican en el ejemplo, y luego distribuir los campos en celdas.

    • Delante del primer campo de la celda no debemos poner ningún espacio en blanco (&nbsp;)

      <tr>
          <td>
               {CWCampoTexto ...
          </td>
      </tr>
      

      Solamente se pondrá en el caso de que se sitúen dos campos en una misma celda; así dejaríamos un espacio entre ellos. Ejemplo:

      <tr>
          <td>
             {CWCampoTexto ...}&nbsp;{CWCampoTexto ...}
          </td>
      </tr>

    Ejemplo completo de una plantilla, hay que destacar que es importante no repetir identificadores de elementos de pantalla (nombres de los campos) dentro de una misma TPL, como vemos en el ejemplo, los conceptos estado y descripción estado corresponden a los campos filCodigoEstado y filDescEstado

    {CWVentana tipoAviso=$smty_tipoAviso codAviso=$smty_codError descBreve=$smty_descBreve textoAviso=$smty_textoAviso onLoad=$smty_jsOnLoad}
     {CWBarra usuario=$smty_usuario codigo=$smty_codigo customTitle=$smty_customTitle}
      {CWMenuLayer name="$smty_nombre" cadenaMenu="$smty_cadenaMenu"}
     {/CWBarra}
    
     {CWMarcoPanel conPestanyas="true"}
    
     <!--*********** MODO fil ******************-->
       {CWPanel id="fil" action="buscar" method="post" estado="$estado_fil" claseManejadora="TinvEstados"}
         {CWBarraSupPanel titulo="Estados de los bienes"}
           {CWBotonTooltip imagen="04" titulo="Limpiar campos" funcion="limpiar" actuaSobre="ficha"}
         {/CWBarraSupPanel}
         {CWContenedor}
           {CWFicha}
             <br />
             {CWCampoTexto nombre="filCodEstado" size="3" editable="true" textoAsociado="Código" dataType=$dataType_TinvEstados.filCodEstado}
             <br /><br />
             {CWCampoTexto nombre="filDescEstado" size="10" editable="true" textoAsociado="Descripción" dataType=$dataType_TinvEstados.filCodigoEstado}
             <br /><br />
           {/CWFicha}
         {/CWContenedor}
         {CWBarraInfPanel}
           {CWBoton imagen="50" texto="Buscar" class="boton" accion="buscar" }
         {/CWBarraInfPanel}
       {/CWPanel}
    
    
     <!-- ****************** MODO lis ***********************-->
       {CWPanel id="lis" tipoComprobacion="envio" action="operarBD" method="post" estado="$estado_lis" claseManejadora="TinvEstados"}
         {CWBarraSupPanel titulo="Estados de los bienes"}
           {CWBotonTooltip imagen="01" titulo="Insertar registros" funcion="insertar" actuaSobre="tabla"}
           {CWBotonTooltip imagen="02" titulo="Modificar registros" funcion="modificar" actuaSobre="tabla"}
           {CWBotonTooltip imagen="03" titulo="Eliminar registros" funcion="eliminar" actuaSobre="tabla"}
         {/CWBarraSupPanel}
         {CWContenedor}
           {CWTabla conCheck="true" conCheckTodos="true" id="Tabla1" datos=$smty_datosTabla}
             {CWFila tipoListado="false"}
               {CWCampoTexto nombre="lisCodEstado" textoAsociado="Cód. Estado." editable="nuevo" size="2" dataType=$dataType_TinvEstados.lisCodEstado}
               {CWCampoTexto nombre="lisDescEstado" textoAsociado="Desc. Estado." editable="true" size="12" dataType=$dataType_TinvEstados.lisDescEstado}
             {/CWFila}
             {CWPaginador enlacesVisibles="3"}
           {/CWTabla}
         {/CWContenedor}
         {CWBarraInfPanel}
           {CWBoton imagen="41" texto="Guardar" class="boton" accion="guardar"}
           {CWBoton imagen="42" texto="Cancelar" class="boton" accion="cancelar" action="cancelarTodo"}
         {/CWBarraInfPanel}
       {/CWPanel}
    
     <!-- ****************** PESTAÑAS ************************-->
       {CWContenedorPestanyas}
         {CWPestanya tipo="fil" estado=$estado_fil}
         {CWPestanya tipo="lis" estado=$estado_lis}
       {/CWContenedorPestanyas}
     {/CWMarcoPanel}
    {/CWVentana}
  5. Registramos el fichero de actions con la nueva clase en el fichero include.php de la aplicación que se encuentra en el directorio include. Podemos hacerlo con un include o usando la carga dinámica de clases explicada en el capítulo 4 punto Carga dinámica de clases.

  6. Finalmente incluimos la ventana correspondiente en include/menuModulos.xml que contiene las entradas de menú.

    <opcion titulo="Estados" descripcion="Mantenimiento de estados de bienes" url="phrame.php?action=TinvEstados__iniciarVentana"/>

Una vez cubiertos estos pasos se puede probar la ventana entrando a la aplicación. Suerte!