En un formulario podemos tener campos o columnas de varios tipos de componentes, entre otros campos de texto, combos, checkbox, ... En el caso de los campos de texto, podemos definir su tipo y algunas características más, con lo que el framework nos va a facilitar algunas tareas básicas asociadas a estos. Una vez que hemos definido el tipo, tenemos que enlazarlo con el campo en la template con el parámetro dataType.
Ejemplo:
# clase manejadora
$this->addFieldType('filTelefono', new gvHidraString(false, 15));
# template
{CWCampoTexto nombre="filTelefono" size="15" editable="true" textoAsociado="Teléfono"
dataType=$dataType_nombreClaseManejadora.filTelefono}
El framework proporciona diferentes tipos de datos que permiten controlar varios aspectos de interfaz y comportamientos. Concretamente, al indicar el tipo de datos de un campo el framework nos ofrece:
Máscaras de entrada según tipo de datos en el cliente.
Validaciones en el servidor antes de operaciones de modificación de los mismos en la BD (modificación e inserción).
Limitar la longitud máxima del campo en pantalla, comprobaciones a nivel cliente (maxlength) y servidor
Comprobación de obligatoriedad a nivel de cliente y servidor.
Ordenación en tabla por tipo de datos (sino se indica ordena siempre como cadena).
Mostrar en pantalla características especiales según tipo de datos.
Los tipos de datos disponibles en gvHidra son:
gvHidraString
gvHidraDate
gvHidraDateTime
gvHidraInteger
gvHidraFloat
Para hacer uso de los tipos, en la clase manejadora hay que crear una instancia con el tipo que queramos, y luego asignarla al campo con el método addTypeField() de la clase manejadora. El constructor de los tipos de datos tiene por defecto dos parametros
required: indica que el campo es obligatorio.
maxLength: indica la longitud máxima esperada. Las fechas no tienen este parámetro.
Para informar al plugin (CWCampoTexto, CWAreaTexto, CWLista...) el tipo de datos que va a contener, tenemos que utilizar la propiedad dataType. El framework insertará los datos en la variable $dataType_claseManejadora.nombreCampo.
Con esta definición de tipos conseguimos que el framework valide los datos antes de cada operación de modificación de la BD (actualización e inserción). Al detectar algún error, de forma automática, recogerá la información y la mostrará en pantalla informando al usuario.
Para el tratamiento de cadenas tenemos el tipo gvHidraString. Este tipo tiene los siguientes métodos propios:
setRegExp(string): Permite introducir una expresión regular para validar la cadena en el cliente (cuando el campo pierde el foco) como en el servidor.
setPasswordType(bool): Permite crear un campo de tipo password, de forma que oculta el contenido del mismo a los ojos del usuario.
setInputMask(string): Permite introducir una máscara de entrada de datos para la cadena. El formato de máscara será
'#' Indica un carácter numérico.
'x' Indica una letra (mayúscula o minúsculas)
'*' Indica un carácter alfanumérico (cualquier letra o número)
'(', ')', '-', '.', etc: Caracteres delimitadores típicos
Vamos a ver algunos ejemplos para que quede más claro el uso de las máscaras:
Máscara: (##) #######
Cadena: 963333333
Resultado: (96) 3333333
Máscara: CP: #####
Cadena: 46001
Resultado: CP: 46001
Nota: si no se hace uso de los métodos los mismos están deshabilitados.
En el siguiente fragmento de código vemos como podemos asignar el tipo gvHidraString a un campo.
$telefono = new gvHidraString(false, 15); $telefono->setInputMask('(+##)-#########'); $this->addFieldType('filTelefono',$telefono);
Primero se crea un objeto de tipo string llamando a la clase gvHidraString, con el primer parámetro indicamos la obligatoriedad del campo, y con el segundo el tamaño máximo del campo. Ahora ya podemos hacer uso del método setInputMask(), que marcará el campo con esa máscara. Y por último se asigna el tipo al campo en concreto, en nuestro caso es "filTelefono" (alias del campo en la select de búsqueda o edición).
La intención de gvHidra es simplificar lo máximo posible los problemas derivados de los formatos de las fechas, y para ello se ofrece al programador una forma única de manejar las fechas (formato de negocio) y es el framework el que se encarga, en caso necesario, de convertir estas fechas al formato del usuario para interaccionar con éste (formato de usuario), o al formato que entiende nuestro gestor de base de datos para almacenar/recuperar los datos (formato de datos).
Actualmente gvHidra define de manera global el formato con el que va a mostrar las fechas al usuario (método ConfigFramework::getDateMaskUser) y se puede cambiar por el programador.
También se define, por cada tipo de SGBD, cual es el formato que reconocen (método mascaraFechas de IgepDBMS_*) aunque no se recomienda modificarlos.
Por último, también se define el modo de representar las fechas internamente (método ConfigFramework::getDateMaskFW), que es el que nos permitirá operar en PHP con las fechas y que tampoco se recomienda modificarlo.
En la siguiente figura podemos ver un esquema de la situación general:
Cada nodo representa uno de los tipos de formato explicados antes, y las etiquetas junto a ellos representan el método usado para obtener la definición de ese formato. Las transiciones entre nodos representan las conversiones que se realizan en el framework, y las etiquetas junto a ellas representan los métodos que convierten del formato origen al destino de la transición. Además los métodos marcados con * (y con color más claro) indican que el método es interno al framework, y por tanto el programador no necesitará usarlo normalmente. A pesar de ello se han incluido todos ellos en el esquema para dar una visión completa.
Primero, para incluir una fecha en un panel necesitamos:
Definir el campo en la select (búsqueda o edición).
Definir el campo equivalente en la plantilla (tpl).
Asociar un tipo de datos al campo de la plantilla (tpl) a través del constructor de la clase manejadora. Usaremos las clases gvHidraDate o gvHidraDatetime dependiendo de si queremos la hora o no.
// Fecha con calendario y etiqueta para el día de la semana en formato corto // El parámetro indica "obligatoriedad" para cumplimentar el campo $tipoFecha = new gvHidraDate(true); // Existirá un calendario asociado al campo $tipoFecha->setCalendar(true); //Aparecerá la inicial del día de la semana (L, M, X, J, V, S, D) tipoFecha->setDayOfWeek('short'); $this->addFieldType('fcertificacion', $tipoFecha);
Cuando el programador accede a un campo declarado como fecha (en un método "pre" o "post", en una operación o en una acción particular) obtiene un objeto de la clase gvHidraTimestamp (basada en DateTime) (si no tiene valor se recibirá un NULL). No confundir esta clase con las usadas para definir el tipo, ésta es la que se usa para operar con las fechas (con o sin hora).
$fpeticionIni = $objDatos->getValue('fpeticionIni'); if (empty($fpeticionIni)) return; // en $fpeticionIni tenemos una instancia de gvHidraTimestamp // incrementar la fecha en 1 dia $fpeticionIni->addDays(1); // inicializar un campo a la fecha actual: $objDatos->setValue('fcreacion', new gvHidraTimestamp());
La clase gvHidraTimestamp nos ofrece algunos métodos útiles para modificar u operar sobre las fechas:
__construct([$ts='now' [, $tz=null]]): el constructor tiene los mismos parámetros que DateTime; si no le pasamos ninguno se inicializa a la fecha y hora actual.
setTime($hours, $minutes [, $seconds = 0]): para modificar la hora.
setDate($year, $month, $day): para modificar la fecha.
modify($str): método de DateTime que usa la sintaxis de strtotime para modificar la fecha.
addDays(int), subDays(int): añadir y restar dias a una fecha.
addWeeks(int), subWeeks(int): añadir y restar semanas a una fecha.
addMonths(int), subMonths(int), addYears(int), subYears(int): añadir y restar meses/años a una fecha. Estos métodos cambia el funcionamiento por defecto usado en modify: si estamos en dia 31 y le sumamos un mes, si no existe el dia obtenemos el 1 del mes siguiente. Con estos métodos, en la situación anterior obtenemos el último día del mes siguiente.
También hay métodos para obtener la fecha en distintos formatos o hacer comparaciones:
formatUser(): obtiene la fecha en el formato que ve el usuario. En principio no debería usarse por el programador.
formatFW(): obtiene la fecha en el formato que reconoce el framework. En principio no debería usarse por el programador.
formatSOAP(): obtiene la fecha en el formato usado en SOAP. Lo usaremos para generar fechas en servidores de web services.
format($format): método de DateTime que acepta los formatos usados por date(). Este método sólo deberia usarse para obtener elementos de la fecha como el dia o el mes. Para otros formatos más complejos habría que valorar si se crea un nuevo método en la clase.
isLeap(): indica si el año de la fecha es bisiesto.
cmp($f1, $f2): método estático para comparar fechas. Devuelve 1 si la primera en menor, -1 si es mayor y 0 si son iguales. (OBSOLETO, ya que se puede comparar los objetos directamente).
between($f1, $f2): comprueba si la fecha del objeto se encuentra entre el intérvalo [ f1, f2 ].
betweenDays($f1, $days): comprueba si la fecha del objeto se encuentra entre el intérvalo [ f1, (f1 + days) ]. Si el número de dias es negativo se usa el intervalo [(f1 + days), f1 ]
getTimestamp(): obtiene la fecha en timestamp. Este tipo tiene restricciones (en PHP < 5.3 el rango va de 1970 al 2036 aprox.), por lo que se recomienda no usar (casi todas las operaciones con timestamp se pueden hacer con DateTime).
Estas serían las clases usadas para indicar que el tipo de un campo es una fecha, con la diferencia que la segunda admite también la hora. Al definir un campo de este tipo, el framework ya se encarga de hacer las transformaciones necesarias de formatos, asi como de aplicar máscaras y calendarios para la edición.
Estas clases también disponen de los siguientes métodos propios:
setCalendar(bool): indica si se muestra o no el calendario.
setDayOfWeek({none|short|long}): indica si se quiere mostrar el dia de la semana.
setDayOfYear(bool): indica si se quiere mostrar el día del año.
Si no se hace uso de los métodos los valores por defecto son calendario=false, dayOfWeek=none, dayOfYear=false.
Hay que tener precaución con no usar el tipo gvHidraDate si el campo asociado tiene información de hora en la base de datos, ya que el framework produciria una excepción. Este comportamiento se ha definido de esta forma para evitar pérdidas de datos por truncamiento.
Nota: Hay que tener en cuenta que el tipo gvHidraTimestamp (que hereda de DateTime de PHP) es una marca de tiempo, con lo que vamos a trabajar con instantes de tiempo. En el caso de operaciones con fechas, al crear instancias de dicha clase el valor que coge por defecto es "now" (el instante de tiempo de la invocación); por lo que si se está trabajando con fechas esto puede acarrear errores. Para obtener la referencia al día actual se debe invocar al constructor con el parámetro "today". A continuación mostramos unos ejemplos:
// Ejemplo, instante actual 24/07/2009 13:28:30 $f = new gvHidraTimestamp(); print($f->format("d/m/Y H:i:s")); // equivalente a $f->formatUser() //Resultado = 24/07/2009 13:28:30 $f = new gvHidraTimestamp("today"); print($f->format("d/m/Y H:i:s")); //Resultado = 24/07/2009 00:00:00 $f = new gvHidraTimestamp("yesterday"); print($f->format("d/m/Y H:i:s")); //Resultado = 23/07/2009 00:00:00
Obtener información de una fecha:
$fpeticionIni = $objDatos->getValue('fpeticion'); if (is_null($fpeticionIni)) return; $day = $fpeticionIni->format('d'); $month = $fpeticionIni->format('m'); $year = $fpeticionIni->format('Y');
Comparar fecha:
$fpeticionIni = $objDatos->getValue('fpeticion'); if (is_null($fpeticionIni)) return; // es fecha futura? if (new gvHidraTimestamp("today") < $fpeticionIni)
Asignar una fecha cualquiera:
$f = new gvHidraTimestamp(); $f->setDate(1950,12,24); $f->setTime(15,0,0); $objDatos->setValue("fsolucion", $f);
Modificar una fecha recibida:
$fpeticionIni = $objDatos->getValue('fpeticion'); if (is_null($fpeticionIni)) return; $fpeticionIni->subMonths(10); $objDatos->setValue("fpeticion", $fpeticionIni);
Obtener la diferencia en dias entre dos fechas:
$diffDias = round(($fini->getTimestamp() - $ffin->getTimestamp()) / (24*60*60)) ; // a partir de PHP 5.3 $intervalo = $fini->diff($ffin); $diffDias = $intervalo->format('%a');
Usar la fecha en una sentencia sql de la conexión correspondiente:
$fechabd = $this->getConnection()->prepararFecha($fpeticionIni); $sentencia = "update facturas set fecha_entrada = '$fechabd' where usuario = 'xxx'";
Obtener un objeto fecha de la base de datos de una consulta que no hace el framework:
$this->consultar("SELECT fecha,fechahora from tabla" , array( 'DATATYPES'=>array('fecha'=>TIPO_FECHA,'fechahora'=>TIPO_FECHAHORA)));
Cuando trabajamos con números podemos encontramos con los mismos problemas que hemos visto para las fechas, y que son los que vienen derivados de los distintos formatos que pueden usarse para representarlos.
En gvHIDRA se definen los formatos numéricos indicando el caracter usado como separador decimal, y el separador de miles (puede ser cadena vacia). Al igual que en las fechas, tenemos:
formato de interfaz, el usado para interactuar con el usuario, y que viene definido en método ConfigFramework::getNumericSeparatorsUser.
formato de datos, definido para cada tipo de gestor de base de datos, en métodos caracteresNumericos de IgepDBMS_*. Es el formato usado para las operaciones con las bases de datos de ese tipo.
formato del framework (o negocio), es el que el programador usa internamente para las operaciones, y se define en ConfigFramework::getNumericSeparatorsFW. Este formato coincide con la representación de números interna en PHP, es decir, con punto decimal y sin separador de miles. Por tanto el programador puede manejar los números directamente con los operadores y funciones propios de PHP.
El framework se encarga de hacer las conversiones necesarias en cada operación. De los tres formatos definidos, solo el de la interfaz es configurable por el programador.
En la siguiente figura podemos ver un esquema de la situación, siguiendo las mismas reglas explicadas anteriormente para las fechas:
gvHidraInteger.
Usaremos este tipo para datos de tipo entero. No dispone de métodos propios.
gvHidraFloat.
Para datos de tipo numérico con coma flotante. Métodos propios:
setFloatLength(int). Permite fijar la longitud de la parte decimal del número. De este modo el número se define a partir de su longitud total más la de la parte decimal (siguiendo el patrón de SQL). Si el número de decimales es 0, se recomienda usar gvHidraInteger.
Nota 1: si no se hace uso del método setFloatLength el valor por defecto es 2.
Nota 2: Si no coincide el número de decimales en la BD con los que le indicamos al tipo se produce una excepción, por lo que en caso necesario el programador tiene que truncar/redondear los datos según sus necesidades.
En el constructor de la clase manejadora del panel hay que crear el tipo para poder asignárselo al campo que corresponda, así como marcar las propiedades/características.
// numero con 8 digitos en la parte entera y 2 decimales $tipoNumeroDec = new gvHidraFloat(false, 10); // numero con 7 digitos en la parte entera y 3 decimales $tipoNumeroDec->setFloatLength(3); $this->addFieldType('ediCoste', $tipoNumeroDec);
Primero se crea un objeto de tipo float llamando a la clase gvHidraFloat, con el primer parámetro indicamos la obligatoriedad del campo, y con el segundo el tamaño máximo del campo, incluidos los decimales. Ahora ya podemos hacer uso del método setFloatLength(). Y por último se asigna el tipo al campo en concreto, en nuestro caso es "ediCoste" (alias del campo en la select de búsqueda o edición).
De esta forma, gvHidra se encarga de realizar las conversiones necesarias para comunicarse con la BD y el usuario no necesita preocuparse por si debe introducir como separador de decimales la coma o el punto. Los separadores de miles aparecen "solos" y el separador decimal aparece con la coma o el punto del teclado numérico. Cualquier otro caracter (letras, paréntesis, etc...) no pueden introducirse, se ignoran.
Si trabajando en la capa de negocio queremos asignar explícitamente un número para mostrarlo en pantalla, lo podemos hacer directamente con el metodo setValue() del objeto datos usando un numero en PHP. Si lo que queremos es coger un número (getValue del objeto datos), operar con él y asignarlo, también lo podemos hacer directamente. Si tras operar con él queremos hacer algo con la base de datos, usaremos el método prepararNumero de la conexión correspondiente:
$costebd = $this->getConnection()->prepararNumero($coste); $sentencia = "update facturas set importe = $costebd where factura = 10";
Puede ser interesante que, para ciertos casos puntuales, se requiera crear un tipo de datos concreto que nos garantice que los datos son correctos. El framework realizará las validaciones por nosotros y nos garantizará que el dato es válido. Para ello debemos crear una clase dentro de nuestra aplicación o custom que sea accesible. Esta clase debe heredar de uno de los tipos básicos de gvHidra o del tipo básico (gvHidraTypeBase) si sólo se quieren heredar las características básicas. Esta clase debe implementar la interfaz gvHidraType. Esto supone que el programador de la clase deberá implementar el método validate(). A continuación tenemos un ejemplo:
//Type que controla que el campo introducido corresponde con el año actual class anyoActual extends gvHidraTypeBase implements gvHidraType { public function __construct($required=false) { $maxLength = 4; parent::__construct($required,$maxLength); }//Fin de constructor public function validate($value) { if($value!=date('Y')) throw new Exception('No ha introducido el año actual.'); } }