Introducción
A partir de Visual FoxPro 9.0 tenemos una nueva opción para publicar los datos de nuestras tablas en la Web. Esta opción la logramos sin adquirir ni descargar ninguna herramienta de terceras partes, ni desarrollar objetos COM con Visual FoxPro.
En este artículo veremos con un ejemplo como desarrollar esta opción, con el uso de procedimientos almacenados en nuestra base de datos, el uso de una nueva función incluida en Visual FoxPro 9.0 y el nuevo Proveedor OLE DB de Visual FoxPro 9.0.
¿Qué necesitamos?
Suponemos en este artículo que uno tiene conocimientos sobre Microsoft Internet Information Server (IIS) y como crear aplicaciones con Active Server Pages (ASP).
Lo que necesitamos para desarrollar este breve y funcional ejemplo es:
1. Una PC servidora con Internet Information Server (IIS) instalado en donde montaremos nuestro sitio Web. Si tienen poca experiencia en esto, pueden leer el siguiente artículo publicado en PortalFox:
Configurando IIS
Por Antonio Muñoz de Burgos y Caravaca
http://www.portalfox.com/article.php?sid=802
2. Visual FoxPro 9.0 instalado, ó solamente el Proveedor OLE DB de Visual FoxPro 9.0 que lo pueden descargar de:
Microsoft OLE DB Provider for Visual FoxPro 9.0
http://www.microsoft.com/downloads/details.aspx?FamilyID=e1a87d8f-2d58-491f-a0fa-95a3289c5fd4&DisplayLang=en
Como parte de lo nuevo que trae el Proveedor OLE DB de Microsoft para Visual FoxPro 9.0, y que utilizaremos en este ejemplo es:
- Los procedimientos almacenados pueden ahora retornar RecordSets creados a partir de cursores de VFP usando las nuevas funciones de la versión 9: SETRESULTSET(), GETRESULTSET() y CLEARRESULTSET()
- Se actualizó la base de datos Northwind con 5 (cinco) procedimientos almacenados que incluyen la función SETRESULTSET() de modo que éstos retornan RecorSets cuando se ejecutan.
3. Microsoft Data Access Components (MDAC) versión 2.6 o superior. La última versión de MDAC la pueden descargar de:
MDAC 2.8 (Español)
http://www.microsoft.com/downloads/details.aspx?displaylang=es&FamilyID=6c050fe3-c795-4b7d-b037-185d0506396c
4. Disponer de un editor de páginas ASP como Visual InterDev, FrontPage o simplemente el conocido Block de Notas.
Algunos conceptos previos
¿Que es el Proveedor OLE DB de Visual FoxPro?
OLE DB (Object Linking and Embedding for DataBases) para Visual FoxPro es un componente COM que se utiliza para tener acceso a las bases de datos y tablas de VFP desde otros lenguajes de programación u otras aplicaciones.
OLE DB no se utiliza directamente, sino por medio de ADO (ActiveX Data Object). ADO permite conectarse a orígenes de datos compatibles con OLE DB u ODBC (Open DataBase Connectivity). ADO esta formado por varios objetos organizados de forma jerárquica y cada uno de estos objetos poseen sus métodos y propiedades.
Veamos a los 2 objetos de ADO que veremos en el desarrollo del ejemplo:
Objeto Connection: Nos proporciona una conexión a una base de datos. Esta conexión nos permitirá ejecutar los distintos comandos sobre la base de datos. Connection es el objeto primario de ADO, ninguno de los otros objetos puede existir si este no es declarado. La conexión terminará cuando ejecutamos el método Close o cuando termine la ejecución de la página ASP.
Objeto Recordset: Representa el resultado de una consulta ejecutada contra la base de datos. Este objeto es la interface natural contra la base de datos.
Nuestra primer página
Una vez instalado y corriendo el servicio de IIS, e instalado el Proveedor OLE DB de VFP en el servidor creamos una carpeta ("\vfp") bajo el sitio Web predeterminado (por defecto "C:\Inetpub\wwwroot\") y esta será nuestra carpeta o sitio de trabajo.
En esta nueva carpeta creamos nuestra primera página ASP con el nombre "vfpoledb.asp" con el siguiente contenido:
<%@ Language=VBScript%>
<%
Dim Ruta, oConn, oRs
'-- Si solo tenemos instalado el proveedor OLE DB de VFP 9.0
' Ruta = "C:\Archivos de programa\" & _
' "Microsoft Visual FoxPro OLE DB Provider\Samples\Northwind\Northwind.DBC"
'-- Si tenemos instalado Visual FoxPro 9.0
Ruta = "C:\Archivos de programa\" & _
"Microsoft Visual FoxPro 9\Samples\Northwind\Northwind.DBC"
Set oConn = CREATEOBJECT("ADODB.Connection")
Set oRs = CREATEOBJECT("ADODB.RecordSet")
oConn.Open("Provider=VFPOLEDB.1;Data Source=" & Ruta)
oRs.Open "SELECT CustomerId, CompanyName, Address, City, Country FROM Customers", oConn
%>
<html>
<body>
<h3>Listado de Clientes al: <% =Now() %></h3>
<table border=1 width=100% cellspacing="1" cellpadding="2">
<tr>
<% For i = 0 to oRS.Fields.Count - 1 %>
<th><% = UCASE(oRS(i).Name) %></th>
<% Next %>
</tr>
<% Do While Not oRS.EOF %>
<tr bgcolor="#FFFFFF">
<% For i = 0 to oRS.Fields.Count - 1 %>
<td><% = oRS(i) %></td>
<% Next %>
</tr>
<%
oRs.MoveNext
Loop
oRs.Close
oConn.Close
Set oRs = Nothing
Set oConn = Nothing
%>
</table>
</body>
</html>
Guardamos los cambios y desde el Explorador de Internet ejecutamos:
http://localhost/vfp/vfpoledb.asp
Si todo ha ido bien, debemos visualizar la siguiente página:
¿Fácil? Ahora ya sabemos como podemos hacer nuestras páginas consultando dinámicamente los datos de nuestras tablas de VFP y publicarlas en la Web.
Para no repetir el código de creación y apertura de nuestro objeto Connection en todas nuestras páginas, vamos a utilizar los Include que nos permiten las páginas ASP y en caso de cambiar la ruta o el nombre de nuestra base de datos solo deberemos cambiar en un solo lugar. Esto también nos dará mas seguridad a nuestro código.
Creamos una nueva página que llamaremos "conn.asp" y copiamos el siguiente código.
<%
Dim cRuta, oConn
'-- Si tenemos instalado Visual FoxPro 9.0
cRuta = "C:\Archivos de programa\" & _
"Microsoft Visual FoxPro 9\Samples\Northwind\Northwind.DBC"
Set oConn = CREATEOBJECT("ADODB.Connection")
oConn.Open("Provider=VFPOLEDB.1;Data Source=" & cRuta & ";Mode=ReadWrite")
%>
Cambiamos el código de la página "vfpoledb.asp" y guardamos como "vfpoledb2.asp".
<%@ Language=VBScript%>
<!-- #include file="conn.asp" -->
<%
Dim oRs
Set oRs = CREATEOBJECT("ADODB.RecordSet")
oRs.Open "SELECT CustomerId, CompanyName, Address, City, Country FROM Customers", oConn
%>
<html>
<body bgcolor="#E0EAFC">
<h3>Listado de Clientes al: <% =Now() %></h3>
<table border=1 width=100% cellspacing="1" cellpadding="2">
<tr>
<% For i = 0 to oRS.Fields.Count - 1 %>
<th><% = UCASE(oRS(i).Name) %></th>
<% Next %>
</tr>
<% Do While Not oRS.EOF %>
<tr bgcolor="#FFFFFF">
<% For i = 0 to oRS.Fields.Count - 1 %>
<td><% = oRS(i) %></td>
<% Next %>
</tr>
<%
oRs.MoveNext
Loop
oRs.Close
oConn.Close
Set oRs = Nothing
Set oConn = Nothing
%>
</table>
</body>
</html>
Ejecutamos desde el Explorador de Internet:
http://localhost/vfp/vfpoledb2.asp
Y veremos los mismos resultados que el ejemplo anterior.
Utilizando procedimientos almacenados
Para nuestro ejemplo utilizaremos los procedimientos almacenados actualizados que trae Visual FoxPro 9.0 en la base de datos "Northwind" y crearemos algunos nuevos procedimientos almacenados.
Para crear nuestro procedimiento almacenado, abrimos la base de datos y editamos los procedimientos almacenados
OPEN DATABASE (HOME(2) + "Northwind\Northwind")
MODIFY PROCEDURE
Al final del archivo, escribimos nuestro procedimiento almacenado llamado "CustomersAll" que nos retornará todos los registros de la tabla "Customers"
PROCEDURE CustomersAll
SELECT CustomerId, CompanyName, Address, City, Country ;
FROM Customers ;
INTO CURSOR CustomersAll
RETURN SETRESULTSET("CustomersAll")
ENDPROC
El código para llamar un procedimiento almacenado de nuestra base de datos desde la página ASP es el siguiente:
<%
Dim cRuta, oConn
Ruta = "C:\Archivos de programa\" & _
"Microsoft Visual FoxPro 9\Samples\Northwind\Northwind.DBC"
Set oConn = CREATEOBJECT("ADODB.Connection")
oConn.Open("Provider=VFPOLEDB.1;Data Source=" & cRuta)
Dim oRS, nReg
Set oRs = oConn.Execute("CustomersAll",nReg,4)
%>
Creamos la nueva página "customers.asp" para obtener todos los registros de la tabla "Customers", esta vez llamando al procedimiento almacenado creado anteriormente. En esta página agregamos código HTML para enlazar con las siguientes páginas que realizaremos para completar este ejemplo. El código de esta nueva página es:
<%@ Language=VBScript%>
<!-- #include file="conn.asp" -->
<%
Dim oRs, nReg
Set oRs = oConn.Execute("CustomersAll",nReg,4)
%>
<html>
<body bgcolor="#E0EAFC">
<h3>Listado de Clientes al: <% =Now() %></h3>
<table border=1 width=100% cellspacing="1" cellpadding="2">
<tr>
<th>Id</th>
<th>Compañia</th>
<th>Dirección</th>
<th>Ciudad</th>
<th>País</th>
</tr>
<% Do While Not oRS.EOF %>
<tr bgcolor="#FFFFFF">
<td><a href="/vfp/orders.asp?cId=<%=oRs(0)%>"><%=oRs(0)%></a></td>
<td><%=oRs(1)%></td>
<td><%=oRs(2)%></td>
<td><%=oRs(3)%></td>
<td><%=oRs(4)%></td>
</tr>
<%
oRs.MoveNext
Loop
oRs.Close
oConn.Close
Set oRs = Nothing
Set oConn = Nothing
%>
</table>
<p>Registros: <%=nReg%></p>
</body>
</html>
Al ejecutar la página "customers.asp" nos muestra la siguiente tabla:
Como se aprecia en la figura, hemos creado un enlace a la página "orders.asp" pasando como parámetro el Id del cliente. El código de esta página es el siguiente, el cual llama al procedimiento almacenado "CustOrdersOrders", que ya está incluido en la base de datos "Northwind", con el parámetro del Id del cliente:
<%@ Language=VBScript%>
<!-- #include file="conn.asp" -->
<%
Dim cId
cId = Request("cId")
Dim oRs, nReg
Set oRs = oConn.Execute("CustOrdersOrders('" + cId + "')",nReg,4)
%>
<html>
<body bgcolor="#E0EAFC">
<h3>Ordenes del cliente <%=cId%></h3>
<table border=1 width=100% cellspacing="1" cellpadding="2">
<tr>
<th>Orden Id</th>
<th>Fecha Orden</th>
<th>Fecha Pedido</th>
<th>Fecha Envio</th>
</tr>
<% Do While Not oRS.EOF %>
<tr bgcolor="#FFFFFF">
<td><a href="/vfp/details.asp?oId=<%=oRs(0)%>"><%=oRs(0)%></a></td>
<td><%=oRs(1)%></td>
<td><%=oRs(2)%></td>
<td><%=oRs(3)%></td>
</tr>
<%
oRs.MoveNext
Loop
oRs.Close
oConn.Close
Set oRs = Nothing
Set oConn = Nothing
%>
</table>
<p>Registros: <%=nReg%></p>
<p><a href="javascript:history.back()">Página anterior</a></p>
</body>
</html>
Los resultados de la ejecución de la página "orders.asp" se muestran en la siguiente figura:
Al igual que en la página anterior, incluimos un enlace a la página "details.asp" que llama al procedimiento almacenado "CustOrdersDetails" con el parámetro correspondiente. El código de la página es el siguiente:
<%@ Language=VBScript%>
<!-- #include file="conn.asp" -->
<%
Dim oId
oId = Request("oId")
Dim oRs, nReg
Set oRs = oConn.Execute("CustOrdersDetail(" + oId + ")",nReg,4)
%>
<html>
<body bgcolor="#E0EAFC">
<h3>Detalle de la orden <%=oId%></h3>
<table border=1 width=100% cellspacing="1" cellpadding="2">
<tr>
<th>Producto</th>
<th>Precio unitario</th>
<th>Cantidad</th>
<th>Descuento</th>
<th>Precio Total</th>
</tr>
<% Do While Not oRS.EOF %>
<tr bgcolor="#FFFFFF">
<td><%=oRs(0)%></td>
<td align="right"><%=oRs(1)%></td>
<td align="right"><%=oRs(2)%></td>
<td align="right"><%=oRs(3)%>%</td>
<td align="right"><%=oRs(4)%></td>
</tr>
<%
oRs.MoveNext
Loop
oRs.Close
oConn.Close
Set oRs = Nothing
Set oConn = Nothing
%>
</table>
<p>Registros: <%=nReg%></p>
<p><a href="javascript:history.back()">Página anterior</a></p>
</body>
</html>
Y el resultado de esta página lo vemos en la siguiente figura:
Hasta aquí se observa que con un poco de código ASP y código en nuestra base de datos, podemos realizar cualquier tipo de consultas y publicarlas en la Web fácilmente. Si la sentencias SELECTs escritas en nuestros procedimientos almacenados son optimizables, los tiempos de respuestas son excelentes.
Modificando los datos de las tablas
Aparte de consultar los datos de nuestras tablas, también podemos modificar e insertar datos en tablas de VFP. Sobre este punto se debe tener mucha atención al realizar nuestras páginas ASP y nuestros procedimientos almacenados. Las páginas ASP deben ser seguras y debemos validar todos los datos ingresados, no solo por errores del usuario, sino también por datos ingresados con fines maliciosos. En los procedimientos almacenados también debemos validar los parámetros recibidos.
En este artículo solo veremos el caso de modificar los datos de los clientes, sin poner mucha atención en el punto anteriormente citado.
Para realizar la página de edición de datos, primeramente agregaremos una columna en la página "customers.asp" para enlazar la página "editcustomer.asp". Solo debemos agregar el siguiente código a continuación de la última columna de la tabla.
<td><a href="/vfp/editcustomer.asp?cId=<%=oRs(0)%>">Editar</a></td>
Antes de crear las páginas para la edición y actualización de los datos, agregamos dos nuevos procedimientos almacenados a la base de datos "Northwind".
PROCEDURE GetCustomer(tcCID)
SELECT CustomerId, CompanyName, ContactName, ContactTitle, ;
Address, City, REGION, PostalCode, Country, Phone, Fax ;
FROM Customers ;
WHERE CustomerId = tcCid ;
INTO CURSOR GetCustomer
RETURN SETRESULTSET("GetCustomer")
ENDPROC
PROCEDURE UpdateCustomer
LPARAMETERS tcCustomerid, tcCompanyName, tcContactName, ;
tcContactTitle, tcAddress, tcCity, tcRegion, ;
tcPostalCode, tcCountry, tcPhone, tcFax
LOCAL lnErr
UPDATE Customers SET ;
Companyname = tcCompanyname, ;
Contactname = tcContactname, ;
Contacttitle = tcContacttitle, ;
Address = tcAddress, ;
City = tcCity, ;
Region = tcRegion, ;
Postalcode = tcPostalcode, ;
Country = tcCountry, ;
Phone = tcPhone, ;
Fax = tcFax ;
WHERE Customerid = tcCustomerid
lnErr = _TALLY
RETURN lnErr
ENDPROC
La página "editcustomer.asp" llama al procedimiento almacenado "GetCustomer" y contiene un formulario con todos los campos del registro y un botón "Grabar" que invoca a la página "updatecustomer.asp". El código de la página es el siguiente:
<%@ Language=VBScript %>
<!-- #include file="conn.asp" -->
<html>
<%
Dim oRs, nReg, cId
cId = Request("cId")
Set oRs = oConn.Execute("GetCustomer('" & cId & "')", nReg, 4)
%>
<body bgcolor="#E0EAFC">
<h3>Editar cliente <%=cId%> </h3>
<hr>
<form id="frmCust" name="frmCust" action="updatecustomer.asp" method="post">
<table width="100%" border="0" cellpadding="5" cellspacing="0">
<tr>
<td>Id Cliente</td>
<td><input name="CustomerId" size="10" value="<%=Trim(oRs(0))%>"
readonly style="color: #FF0000"></td>
</tr>
<tr>
<td>Nombre Compañia</td>
<td><input name="CompanyName" size="40" value="<%=Trim(oRs(1))%>"></td>
</tr>
<tr>
<td>Nombre Contacto</td>
<td><input name="ContactName" size="40" value="<%=Trim(oRs(2))%>"></td>
</tr>
<tr>
<td>Título Contacto</td>
<td><input name="ContactTitle" size="40" value="<%=Trim(oRs(3))%>"></td>
</tr>
<tr>
<td>Domicilio</td>
<td><input name="Address" size="40" value="<%=Trim(oRs(4))%>"></td>
</tr>
<tr>
<td>Ciudad</td>
<td><input name="City" value=" <%=Trim(oRs(5))%>"></td>
</tr>
<tr>
<td>Región</td>
<td><input name="Region" value=" <%=Trim(oRs(6))%>"></td>
</tr>
<tr>
<td>Código postal</td>
<td><input name="PostalCode" size="10" value="<%=Trim(oRs(7))%>"></td>
</tr>
<tr>
<td>País</td>
<td><input name="Country" value="<%=Trim(oRs(8))%>"></td>
</tr>
<tr>
<td>Teléfono</td>
<td><input name="Phone" value="<%=Trim(oRs(9))%>"></td>
</tr>
<tr>
<td>Fax</td>
<td><input name="Fax" value="<%=Trim(oRs(10))%>"></td>
</tr>
<tr>
<td></td>
<td>
<input type="submit" value="Grabar" name="cmdG">
<input type="button" value="Cancelar" name="cmdC" OnClick="history.back()"/>
</td>
</tr>
</table>
</form>
<script Language="VBScript">frmCust.CompanyName.Focus</script>
</body>
</html>
Al ejecutar la página nos retorna el registro a modificar como lo muestra la figura siguiente:
Una vez editados los datos, el botón "Grabar" llama a la página "updatecustomer.asp" que invoca al procedimiento almacenado "UpdateCustomer" con todos los campos pasados como parámetros. El código es el siguiente:
<%@ Language=VBScript %>
<!-- #include file="conn.asp" -->
<html>
<%
Dim cParams
cParams = "'" & Request("CustomerId") & "','" & Request("CompanyName") & "','" & _
Request("ContactName") & "','" & Request("ContactTitle") & "','" & _
Request("Address") & "','" & Request("City") & "','" & _
Request("Region") & "','" & Request("PostalCode") & "','" & _
Request("Country") & "','" & Request("Phone") & "','" & Request("Fax") & "'"
Dim nReg, oRs
nReg = 0
oRs = oConn.Execute("UpdateCustomer(" & cParams & ")", nReg, 4)
%>
<body bgcolor="#E0EAFC">
<h3>Grabar cliente <%=Request("CustomerId")%></h3>
<hr>
<% If nReg > 0 Then %>
<h1>Los datos se grabaron correctamente</h1>
<% Else %>
<h1>Error al grabar los datos</h1>
<% End If %>
<input type="button" value="Continuar" name="cmdC" OnClick="location.href='customers.asp'"/>
</body>
</html>
Si los datos se grabaron correctamente en la tabla, la página se visualizará como lo muestra la siguiente figura:
Con este último ejemplo observamos que la modificación de los datos, no es una tarea complicada de realizar. Solo se deberán crear las páginas ASP y los procedimientos almacenados para cada necesidad y tomar las precauciones descriptas anteriormente.
Para tener en cuenta
Cuando se instala Internet Information Server (IIS) en un servidor, éste crea el usuario "IUSR_<Nombre_Servidor>" para el acceso anónimo al sitio publicado. Para que todo funcione correctamente, éste usuario deberá tener los privilegios necesarios para consultar y modificar la carpeta donde se encuentra nuestra base de datos.
Por defecto IIS controla la contraseña de éste usuario, esto nos puede traer problemas, si la base de datos se encuentra en otro servidor distinto al servidor donde esta ubicado el sitio web. Para evitar esto debemos desmarcar la casilla de verificación "Permitir que IIS controle la contraseña"
Para llegar a la ventana de la figura anterior, en las "Propiedades" del sitio Web, seleccionamos la solapa "Seguridad de directorio" y oprimimos el botón "Modificar'" en "Control de autentificación y acceso anónimo".
Este caso está documentado en la Base de Conocimientos de Microsoft:
Accessing FoxPro Table in ASP Returns Error 80040e14
http://support.microsoft.com/kb/175801
Descargas
Todas las páginas ASP desarrolladas en este ejemplo las podemos descargar y copiar en el sitio Web del servidor en la carpeta "\vfp".
Enlace a los ejemplos: Accediendo a datos de VFP 9.0 desde la Web.zip (6 KB)
Conclusiones
Como comentamos al inicio de este artículo, esta es una opción válida y muy fácil de implementar para publicar nuestros datos de Visual FoxPro en la Web. La complejidad y el alcance de la aplicación a desarrollar solo depende de nosotros y de nuestras necesidades.
Para finalizar deseo agradecer el apoyo y las ideas recibidas de Antonio Muñoz de Burgos (eMans) que mucho me ayudaron para elaborar el código de los ejemplos de este artículo.
Hasta la próxima.
Luis María