Archive for the ‘xBase’ Category

Introducion al MVC (modelo-vista-controlador)

Sunday, March 21st, 2010

Algunos colegas de profesión me han pedido en varias ocasiones que dedique un artículo al MVC (modelo-vista-controlador) para intentar acercarlos esta Patrón de Diseño.

El MVC es un Patrón de Diseño de software por el que se intenta independizar el código en tres partes:

El Modelo, también llamado Dominio, que será el encargado de comunicarse con los datos.
La Vista, que será la encargada de comunicarse con el usuario.
El Controlador, que será el encargado de comunicar internamente al Modelo y a la Vista.

Tengamos, por ejemplo, la pulsación del botón grabar en una pantalla de edición; el evento se produce en la Vista y deberá ser redirigido al Controlador, donde se efectuarán las comprobaciones y operaciones necesarias, para posteriormente llamar al método del Modelo que se encargue de la grabación de los datos en la Base de datos.

El objetivo de este sistema es independizar las partes de nuestro código para poder reutilizarlo en otras aplicaciones fácilmente o cambiar el aspecto de la Vista.

Una vez se comprende el funcionamiento, su implementación es bastante sencilla aunque laboriosa, ya que requiere la redacción y mantenimiento de tres programas; aunque se gana en ventajas al saber que cada parte por separado funciona correctamente y puede ser reutilizada en otras aplicaciones.

Os dejo este ejemplo, realizado con Xailer Enterprise y MySQL.

Espero vuestros comentarios.

Actualización 09.05.2010: Nueva versión del ejemplo MVC con Xailer. Incluye un campo blob.

Discupad los retrasos

Monday, December 1st, 2008

Hola, en las ultimas semanas estoy basante ocupado con otros temas para poder terminar de pulir los artículos que siguen a los dos anteriores sobre el modelo de datos. Disculpad los retrasos, en cuanto tenga un momento me pongo a ello.

Introducción al modelo de datos (II)

Sunday, October 19th, 2008

Este es el segundo artículo sobre modelo de datos, en este estudiaremos las clases TFactura y TLineasFactura.

Desde la clase TFactura accederemos a objetos de las clases TCliente y TLineasFactura; y de la clase TLineasFactura accederemos a un objeto de la clase TArticulo.

Observando el código de las clases notaremos que el nivel de automatización al que se llega y que nos facilitará mucho el trabajo de codificación de nuestra aplicación.

CLASS TFactura FROM TQuery

DATA cConsulta

DATA oCli

DATA oLin

METHOD Init( cQuery )

METHOD Delete( nId_Articulo )

METHOD Change( oSender )

ACCESS oCliente

ACCESS oLineas

END CLASS

METHOD Init( cQuery ) CLASS TFactura

DEFAULT cQuery TO “SELECT * FROM cabfac”

::New()

::OnChange := { | oSender | Change( oSender ) }

::cConsulta := cQuery

::Query( ::cConsulta )

RETURN Self

METHOD Delete( nId_Factura ) CLASS TFactura

IF !Empty( nId_Factura )

::Command( “DELETE FROM cabfac “ + ;

“WHERE id = “ + Str( nId_Factura ) + “ “ + ;

“LIMIT 1” )

::Change()

END IF

RETURN Nil

METHOD Change( oSender ) CLASS TFactura

::oCli := NIL

::oLin := NIL

RETURN Nil

METHOD oCliente CLASS TFactura

IF Empty( ::oCli )

::oCli := TCliente():Init( “SELECT * FROM cliente “ + ;

“WHERE id = “ + Str( ::Id_Cliente ) )

END IF

RETURN ::oCli

METHOD oLineas CLASS TFactura

IF Empty( ::oLin )

::oLin := TLineasFactura():Init( “SELECT * FROM detfac “ + ;

“WHERE id_factura = “ + Str( ::Id ) + ” ” + ;

“ORDER BY linea” )

END IF

RETURN ::oLin

//——————————————————————-//

CLASS TLineasFactura FROM TQuery

DATA cConsulta

DATA oArt

METHOD Init( cQuery )

METHOD Delete( nId_Articulo )

METHOD Change( oSender )

ACCESS oArticulo

END CLASS

METHOD Init( cQuery ) CLASS TLineasFactura

DEFAULT cQuery TO “SELECT * FROM detfac”

::New()

::OnChange := { | oSender | Change( oSender ) }

::cConsulta := cQuery

::Query( ::cConsulta )

RETURN Self

METHOD Delete( nId_Factura, nLinea ) CLASS TLineasFactura

IF !Empty( nId_Factura )

::Command( “DELETE FROM derfac “ + ;

“WHERE id_factura = “ + Str( nId_Factura ) + “ “ + ;

“AND linea = ” + Str( nLinea ) + ” ” + ;

“LIMIT 1” )

::Change()

END IF

RETURN Nil

METHOD Change( oSender ) CLASS TLineasFactura

::oArt := NIL

RETURN Nil

METHOD oArticulo CLASS TLineasFactura

IF Empty( ::oArt )

::oArt := TArticulo():Init( “SELECT * FROM articulo “ + ;

“WHERE id = “ + Str( ::Id_Articulo ) )

END IF

RETURN ::oArt

//——————————————————————-//

Para acceder a una factura, símplemente tendremos que escribir el siguiente código:

oFactura := TFactura():Init( “SELECT * FROM cabfac WHERE id = ” + Str( nNumFac )

para acceder a los datos del cliente de una factura:

oCliente := oFactura:oCliente

MsgInfo( oCliente:Nombre )

para acceder a las lineas de la factura:

nTotal := 0

oFactura:oLineas:GoTop()

WHILE !oFactura:oLineas:Eof()

nTotal += oFactura:oLineas:Cantidad * oFactura:oLineas:Precio

oFactura:oLineas:Skip()

END DO

MsgInfo( nTotal )

para acceder al nombre de un artículo:

oFactura:oLineas:oArticulo:Nombre

Con esto terminamos el segundo artículo dedicado a la introducción al modelo de datos. En el próximo estudiaremos como ir mejorando poco a poco las estructuras de datos para, por ejemplo, poder refrescar las consultas, aplicar el uso de vistas, crear métodos que proporcionen totales, etc. Veremos como el modelo básico que hemos definido en estos dos artículos puede completarse hasta conseguir una funcionalidad tan amplia que aumentará el ahorro y claridad posterior de nuestro código.

Introducción al Modelo de Datos (I)

Friday, October 17th, 2008

Llevo tiempo pensando en lo útil que resultaría contar con un modelo de datos cómodo que permita facilitar la labor de programación y posterior mantenimiento de nuestras aplicaciones. Coincido con mucha gente que la construcción de un modelo de datos es una tarea pesada que requiere mucha abstracción mental y escribir mucho código, pero también he de admitir que un modelo de datos bien construido ahorra muchas horas de trabajo.

He dividido el artículo en dos para hacerlo menos pesado, aunque al terminar este creo que todos los lectores sabrán por donde van los tiros y que va a pasar en la segunda parte.

Todo el código que pongo aquí está escrito con el único objetivo de ejemplificar un modelo de datos y, desde luego, puede ser mejorado; he suprimido a propósito controles de errores y otras cosas a fin de que se vea claramente como funcionan las clases que lo componen.

El ejemplo que vamos a usar para construir nuestro modelo de datos es una sencilla facturación:

  • Tabla de familias de artículos (id, nombre)
  • Tabla de artículos ( Id, Id_familia, nombre, pvp )
  • Tabla de clientes( Id, nombre, domicilio, telefono )
  • Tabla de cabecera de facturas ( Id, fecha, Id_cliente, iva )
  • Tabla de lineas de facturas ( Id_factura, linea, Id_articulo, cantidad, precio )

Comencemos por identificas las clases que van a constituir nuestro modelo de datos:

  • Clase TFamilia
  • Clase TArticulo
  • Clase TCliente
  • Clase TFactura
  • Clase TLineasFactura

Para el código de los ejemplos usaré xHarbour y Xailer. Todas las clases heredarán de una clase principal y abstracta llamanda TQuery, que es una clase que simula un datasource. La clase TQuery está programada por mí basándome el los wrappers que construyó Manu Expósito en Eagle 1 para acceder de forma nativa a MySQL. Creo que no existen problemas para poder heredar del sistema de datasource de cualquier otro producto. Quien lo desee puede contactarme para que le envíe el código fuente de TQuery.

Las primeras clases que vamos a definir son las relacionadas con las tablas maestras, empezando por las que no contienen referencias a claves foráneas de otras tablas, en nuestro caso, las clases TFamilia y TCliente.

CLASS TFamilia FROM Tquery

DATA cConsulta

METHOD Init( cQuery )

METHOD Delete( nId_Familia )

END CLASS

METHOD Init( cQuery ) CLASS TFamilia

DEFAULT cQuery TO “SELECT * FROM famila”

::New()

::cConsulta := cQuery

::Query( ::cConsulta )

RETURN Self

METHOD Delete( nId_Familia ) CLASS TFamilia

IF !Empty( nId_Familia )

::Command( “DELETE FROM familia “ + ;

“WHERE id = “ + Str( nId_Familia ) + “ “ + ;

“LIMIT 1” )

END IF

RETURN Nil

//——————————————————————//

CLASS TCliente FROM Tquery

DATA cConsulta

METHOD Init( cQuery )

METHOD Delete( nId_Cliente )

END CLASS

METHOD Init( cQuery ) CLASS TCliente

DEFAULT cQuery TO “SELECT * FROM cliente”

::New()

::cConsulta := cQuery

::Query( ::cConsulta )

RETURN Self

METHOD Delete( nId_Cliente ) CLASS TCliente

IF !Empty( nId_Cliente)

::Command( “DELETE FROM cliente “ + ;

“WHERE id = “ + Str( nId_Cliente ) + “ “ + ;

“LIMIT 1” )

END IF

RETURN Nil

//——————————————————————//

Constituiremos ahora la clase para controlar la tabla de artículos, que como podemos observar, contiene una clave foránea a la tabla de familias (Id_familia); por lo tanto, desde nuestro modelo de datos, un objeto oArtículo de la clase TArtículo tendrá que poder acceder a los datos de la familia de cada artículo, facilitando posteriormente las labores de programación de nuestra aplicación.

CLASS TArticulo FROM TQuery

DATA cConsulta

DATA oFam

METHOD Init( cQuery )

METHOD Delete( nId_Articulo )

METHOD Change( oSender )

ACCESS oFamilia

END CLASS

METHOD Init( cQuery ) CLASS TArticulo

DEFAULT cQuery TO “SELECT * FROM articulo”

::New()

::OnChange := { | oSender | Change( oSender ) }

::cConsulta := cQuery

::Query( ::cConsulta )

RETURN Self

METHOD Delete( nId_Articulo ) CLASS TArticulo

IF !Empty( nId_Articulo)

::Command( “DELETE FROM articulo “ + ;

“WHERE id = “ + Str( nId_Articulo ) + “ “ + ;

“LIMIT 1” )

::Change()

END IF

RETURN Nil

METHOD Change( oSender ) CLASS TArticulo

::oFam := NIL

RETURN Nil

METHOD oFamilia CLASS TArticulo

IF Empty( ::oFam )

::oFam := Tfamilia():Init( “SELECT * FROM familia “ + ;

“WHERE id = “ + Str( ::Id_Familia ) )

END IF

RETURN ::oFam

Vamos a detenernos un instante a estudiar que tienen estas clases dentro. De forma general, todas las clases llevan una DATA que contiene la consulta efectuada (cConsulta) y los MÉTODOS Init() y Delete().

El método Init() es el encargado de construir y retornar el objeto correspondiente. En su interior nos encontramos instrucciones que, a parte de establecer un valor por defecto para la consulta, también realizan:

  1. Llamar al método New() de la superclase para la construcción del objeto.

  2. Llamar al método Query() de la superclase para ejecutar la consulta y cargar los datos correspondientes.

El método Delete() es el encargado de eliminar una fila de la tabla correspondiente. En caso de usar bases de datos SQL recomiendo tener bien definida la integridad referencial. En otro tipo de almacenes de datos, en este método habrá que incluir el código necesario para eliminar información de las tablas que contengan información de la fila que estamos eliminando.

Estudiando la clase TArticulo podemos observar algunas diferencias muy significativas: aparece una DATA aFam, el método Change() y el método especial ACCESS oFamilia.

Mediante la palabra reservasa ACCESS de xHarbour se define un método especial que se comportará como una DATA y que además ejecuta su código cada vez que se instancia.

La DATA oFam contendrá un objeto de la clase TFamilia descrita anteriormente.

El método Change() se ejecutará cada vez que dispare el evento OnChange() de la clase TQuery. Este evento responde cada vez que se produce un cambio al añadir, actualizar, borrar, navegar o recargar datos de la consulta. Este método pone a NIL la data oFam para controlar un tipo de recursividad que veremos más adelante.

El método ACCESS oFamilia se ejecutará cada vez que accedamos a él, ejecutando la consulta que carga los datos de la familia relacionada con el artículo actual. Para evitar la recursividad y que se ejecuten consultas constantes (perdida de velocidad :-(), miramos si está vacío.

Con lo visto hasta este momento creo que ya habréis podido adivinar que el modelo de datos perimite acceder de forma automática a los datos de tablas relacionadas sin necesidad de escribir código adicional. Comparemos:

  1. Forma tradicional sin modelo de datos:

oArticulo := TQuery():New( “SELECT * FROM articulo” )

WHILE !oArticulo:Eof()

oFamilia := Tquery():New( “SELECT * FROM familia “ + ;

“WHERE id = “ + Str( oArticulo:Id_Familia ) )

MsgInfo( oFamilia:Nombre )

oArticulo:Skip()

END DO

  1. Usando el modelo de datos:

oArticulo := TArticulo():New( “SELECT * FROM articulo” )

WHILE !oArticulo:Eof()

MsgInfo( oArticulo:oFamilia:Nombre )

oArticulo:Skip()

END DO

Es obvio que usando un modelo de datos conseguimos claridad y automatización en la mayoría de los procesos que tenemos que realizar dentro de nuestros programas.

En la segunda parte de este artículo veremos como se automatiza la clase Tfactura.

9ª Congreso anual de programadores xBase. Inscripción abierta.

Sunday, October 28th, 2007

Los próximos días 10 y 11 de noviembre de 2007, como viene siendo tradicional, nos reuniremos en el salón de actos de la ampresa ATISA para celebrar el 9º congreso de programadores en dialectos xBase.

No quiero adelantar nada, pero la agenda del evento se presenta muy interesante. Os animo a todos a asistir haciendo la inscripción.