Mostrando entradas con la etiqueta Bases de datos. Mostrar todas las entradas
Mostrando entradas con la etiqueta Bases de datos. Mostrar todas las entradas

viernes, 21 de agosto de 2009

Diseño de interfaces gráficas en Java, Parte 5: Completando el Modelo


Prepárense para construir el Modelo

De regreso, bien, en este punto ya debe estar corriendo el servidor de base de datos PostgreSQL y la base de datos con su respectiva tabla debe estar lista y ésta última debe contar con algunos registros para la prueba. También los programas en Java de nuestro proyecto deben contar con la referencia al archivo JAR que descargamos e instalamos en el artículo anterior. Ahora solo queda concluir con la construcción del Modelo programando las capas de negocio y de acceso a datos de la aplicación.

Una vez más, retomamos el mismo proyecto que hemos ido construyendo en esta serie de post sobre interfaces gráficas en Java.

Primero, programaremos una clase para crear objetos de datos durante la ejecución del programa, es decir objetos cuyo único propósito sea transportar información a través de las capas de la aplicación. En este caso la clase se llamará InfoUsuario, y la utilizaremos para que sus objetos almacenen los datos extraídos de la tabla usuario de la base de datos:

InfoUsuario.java
/**
* @Desc Clase utilizada como una estructura de datos para alamacenar la información de los registros que
* consultemos de la base de datos
* @author Beto González
*/
public class InfoUsuario {
    public String nombre;
    public String correoElectronico;
    public String domicilio;
    public String telefono;
    public int edad;
}


Después crearemos una clase para manejar el acceso a la base de datos desde nuestra aplicación, incluyendo las consultas a la tabla requerida. El código fuente es el siguiente:

AccesoADatos.java
import java.sql.*;
import java.util.*;
/**
* @Desc Clase utilizada para acceder a la base de datos y realizar consultas sobre sus tablas
* @author Beto González
*/
public class AccesoADatos {
    private String usuario;
    private String contraseña;
    private String url;
    private Connection conexion;
    private Vector<InfoUsuario> resultado; //Vector en el que se almacenarán la lista de registros que resulten en una cosulta, con el <InfoUsuario> se especifica que cada uno de sus elementos será un objeto de la clase InfoUsuario
    private String mensaje;
    AccesoADatos(String url, String usuario, String contraseña)     {
        this.usuario = usuario;
        this.contraseña = contraseña;
        this.url = url;
        this.resultado = new Vector<InfoUsuario>();
        this.mensaje = "";
    }
    /**
     * @Desc
     * @return Verdadero si se realizó correctamente, falso en caso contrario
     */
    public boolean conecta() {
        boolean estado = true;
        Properties props = new Properties();
        props.setProperty("user",usuario);
        props.setProperty("password",contraseña);
        try{
            conexion = DriverManager.getConnection(url, props);
        }catch(SQLException e){
            estado = false;
            mensaje = e.toString();
        }
        return estado;
    }
    /**
     * @Desc Método que ejecuta una consulta SQL sobre la tabla usuario
     * @param condicion Condición para la consulta SQL
     * @return Verdadero si se realizó correctamente, falso en caso contrario
     */
    public boolean consultaUsuario(String condicion) {
        boolean estado = true;
        if(!condicion.isEmpty()) {
            try{
                PreparedStatement st = conexion.prepareStatement("SELECT * FROM usuario WHERE " + condicion);
                ResultSet rs = st.executeQuery();
                InfoUsuario iUsuario = new InfoUsuario(); //Objeto para almacenar la información de cada registro de la tabla
                while (rs.next()) {
                    iUsuario.correoElectronico = rs.getString("correoElectronico");
                    iUsuario.nombre = rs.getString("nombre");
                    iUsuario.domicilio = rs.getString("domicilio");
                    iUsuario.telefono = rs.getString("telefono");
                    iUsuario.edad = rs.getInt("edad");
                    resultado.add(iUsuario);
                }
                rs.close();
                st.close();
            }catch(SQLException e){
                estado = false;
                mensaje = e.toString();
            }
        }
        else{
            estado = false;
            mensaje = "Consulta vacía";
        }
        return estado;
    }
    /**
     * @Desc Método que realiza la búsqueda de un usuario a partir de un correo electrónico
     * @param correo Correo electrónico buscado
     * @return Verdadero si se realizó correctamente, falso en caso contrario
     */
    public boolean buscaCorreo(String correo) {
        boolean estado = consultaUsuario("\"correoElectronico\" = '" + correo + "'");
        return estado;
    }
    /**
     * @Desc Método que realiza la búsqueda de un usuario a partir de un nombre
     * @param correo Correo electrónico buscado
     * @return Verdadero si se realizó correctamente, falso en caso contrario
     */
    public boolean buscaNombre(String nombre) {
        boolean estado = consultaUsuario("nombre LIKE '%" + nombre + "%'");
        return estado;
    }
    /**
     * @Desc Devuelve un elemento específicado que se encuentre dentro del Vector de resultados
     * @param indice Indice del elemento dentro del vector
     * @return
     */
    public InfoUsuario regresaInfoUsuario(int indice) {
        if(resultado.size() > 0)
            return resultado.get(indice);
        else
            return null;
    }
    /**
     * @Desc Devuelve el último mensaje de erro generado por el controlador de la base de datos
     * @return Mensaje devuelto por el controlador de la base de datos
     */
    public String regresaMensaje() {
        return mensaje;
    }
    /**
     * @Desc Devuelve el número de elementos del Vector de resultados
     * @return Número de elementos
     */
    public int regresaNumDeRegistros() {
        return resultado.size();
    }
    /**
     * @Desc Método que cierra la conexión con el sistema manejador de base de datos
     */
    public void cierraConexion() {
        try{
            conexion.close();
        }catch(SQLException e){}
    }
}


Esta clase tiene como propósito abstraer al resto de la aplicación de los detalles del acceso a la base de datos, en este caso a través del SMBD PostgreSQL. Como vemos, se incluyen métodos para abrir y cerrar la conexión con el servidor, un método llamado consultaUsuario(), para ejecutar consultas sobre la tabla usuario a partir de la condición especificada, almacenando la información de los registros resultantes en la propiedad resultado, así como el método regresaInfoUsuario() que precisamente extrae la información de los registros almacenados en esta propiedad. El objeto resultado pertenece a la clase Vector, y funciona como un arreglo en el que se almacenará una lista de objetos de la clase InfoUsuario que ya definimos previamente. De forma que a cada registro resultante le corresponderá un nodo en el vector.

La tercera clase que crearemos se encargará de implementar una pequeña lógica de negocio, se llamará Buscador y en ésta se recibirá la información capturada por el usuario, con la cual se decidirá qué método de la capa de acceso a datos deberá ejecutarse para llevar a cabo la búsqueda de un usuario y retornar su información a la capa de presentación.

Buscador.java
import java.awt.Component;
import javax.swing.JOptionPane;
/**
* @Desc Clase que implementa las busquedas de información en el sistema a través de un objeto de la clase AccesoADatos
* @author Beto González
*/
public class Buscador {
    /**
     * @Desc Método que busca la información de un usuario a través de un objeto de la clase AccesoADatos
     * @param correoElectronico Correo electrónico del usuario
     * @param nombre Nombre del usuario
     * @return Un objeto de la clase InfoUsuario, que contendrá la información del usuario solicitado
     */
    public InfoUsuario buscaUsuario(String correoElectronico, String nombre) {
        InfoUsuario info = new InfoUsuario();
        AccesoADatos a = new AccesoADatos("jdbc:postgresql://localhost/pruebas","postgres","myhouse6");
        boolean estado = true;
        if(a.conecta()) {
            if(!nombre.isEmpty()) {
                if((estado = a.buscaNombre(nombre)) && a.regresaNumDeRegistros() > 0)
                    info = a.regresaInfoUsuario(0);
            }
            else if(!correoElectronico.isEmpty()) {
                if((estado = a.buscaCorreo(correoElectronico)) && a.regresaNumDeRegistros() > 0)
                    info = a.regresaInfoUsuario(0);
            }
            a.cierraConexion();
        }
        if(!estado) {
            JOptionPane.showMessageDialog((Component)null,"Error de acceso a la base de datos " + a.regresaMensaje(),"Error",JOptionPane.OK_OPTION);
        }
        if(a.regresaNumDeRegistros() == 0) {
            JOptionPane.showMessageDialog((Component)null,"No se encontraron registros con los datos capturados","Alerta",JOptionPane.WARNING_MESSAGE);
        }
        return info;
    }
}


En el método buscaUsuario() vemos que se instancía la clase AccesoADatos y sus métodos son utilizándolos según el contenido de la información recibida como parámetro, así si se recibió un nombre de usuario válido para la búsqueda se utilizará el método buscaNombre(), en caso contrario si el correo electrónico es válido, se empleará el método buscaCorreo(). Ambos métodos devolverán el objeto de datos de la clase InfoUsuario, cuyas propiedades contienen la información del usuario consultado. Finalmente se incluye un pequeño control de los mensajes de error generados, para indicar los errores de acceso a datos y las consultas que no arrojaron resultados.

La información devuelta por el método buscaUsuario() deberá llegar hasta la capa de presentación de la aplicación, por lo que dentro de los programas Controlador.java y PanelDeDatos.java realizaremos los siguientes cambios:

PanelDeDatos.java
import javax.swing.*;
import java.awt.*;
/**
* @Desc Clase utilizada para desplegar ventanas emergentes dentro del sistema que contendrán la información establecida
* en un objeto JPanel pasado por el constructor
* @author Beto González
*
*/
public class PanelDeDatos extends JPanel {
    private JLabel lblNombre;
    private JLabel lblCorreoElectronico;
    private JLabel lblDomicilio;
    private JLabel lblEdad;
    private JLabel lblTelefono;
    //Constructor de la clase
    PanelDeDatos () {
        this.lblNombre = new JLabel("Nombre del usuario: ");
        this.lblCorreoElectronico = new JLabel("Correo electronico: ");
        this.lblDomicilio = new JLabel("Domicilio: ");
        this.lblEdad = new JLabel("Edad: ");
        this.lblTelefono = new JLabel("Telefono: ");
        this.setLayout(new GridBagLayout());
    }
    /**
     * @Desc Método que agrega al panel principal cada una de las etiquetas declaradas
     */
    public void estableceContenido(InfoUsuario datos) {
        lblNombre.setText(lblNombre.getText() + datos.nombre);
        lblCorreoElectronico.setText(lblCorreoElectronico.getText() + datos.correoElectronico);
        lblDomicilio.setText(lblDomicilio.getText() + datos.domicilio);
        lblEdad.setText(lblEdad.getText() + Integer.toString(datos.edad));
        lblTelefono.setText(lblTelefono.getText() + datos.telefono);

        GridBagConstraints delimitador = new GridBagConstraints(); //Creamos un delimitador que usaremos para especificar la ubicación de los componentes dentro del panel
        delimitador.gridx = 0;
        delimitador.gridy = 0;
        delimitador.anchor = GridBagConstraints.EAST;
        this.add(lblNombre, delimitador);
        delimitador.gridy = 1;
        this.add(lblCorreoElectronico, delimitador);
        delimitador.gridy = 2;
        this.add(lblDomicilio, delimitador);
        delimitador.gridy = 3;
        this.add(lblEdad, delimitador);
        delimitador.gridy = 4;
        this.add(lblTelefono, delimitador);
    }
}


En la clase PanelDeDatos adecuamos el método estableceContenido() para que reciba un objeto de la clase InfoUsuario y actualice el contenido de sus etiquetas con los datos de éste.

Controlador.java
import javax.swing.JFrame;
import java.awt.event.*;
/**
* @Desc Clase que maneja los eventos realizados en la Vista implementando una serie de interfaces nativas del lenguaje
* @author Beto González
*
*/
public class Controlador implements ActionListener, MouseMotionListener {
    public Ventana ventana;
    Controlador(Ventana ventana) {
        this.ventana = ventana;
    }
    /**
     * @Desc método perteneciente a la interface ActionListener que es implementado para controlar las acciones sobre los botones
     * ya que se ejecutará si se hace clic sobre el botón que este atendiendo
     */
    public void actionPerformed(ActionEvent ie) {
        if(ie.getActionCommand() == "Buscar usuario") { //Validación para asegurarse que la acción fue realizada sobre el botón Buscar
            ventana.panelPrincipal.lblMensaje.setText("Has hecho clic en el bóton de búsqueda");
            //Desplegamos una ventana emergente que contendrá la información de un panel de la clase PanelDeDatos
            PanelDeDatos panel = new PanelDeDatos();
            Buscador buscador = new Buscador();
            InfoUsuario datos = buscador.buscaUsuario(ventana.panelPrincipal.txtCampo2.getText().trim(), ventana.panelPrincipal.txtCampo1.getText().trim()); //Buscamos la información del usuario según los datos capturados
            panel.estableceContenido(datos);
//Pasamos la información consultada al panel de datos
            VentanaEmergente emergente = new VentanaEmergente("Información del usuario", panel);
            emergente.setBounds(180, 180, 300, 250);
            emergente.setVisible(true);
            emergente.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            emergente.addWindowListener(new WindowAdapter()
            {
                public void WindowCloser(WindowEvent e)                 {
                    System.exit(0);
                }
            });
        }
    }
     /**
     * @Desc Método ejecutado cuando el usuario arrastra el ratón presionando el botón de acción
     */
    public void mouseDragged(MouseEvent e) {
        ventana.panelPrincipal.lblMensaje.setText("Ratón arrastrado");
    }
    /**
     * @Desc Método ejecutado cuando el usuario solo arrastra el ratón
     */
    public void mouseMoved(MouseEvent e) {
        ventana.panelPrincipal.lblMensaje.setText("Posición del puntero X:"
         + Integer.toString(e.getX()) + " Y:"
         + Integer.toString(e.getY()));
    }
}


En la clase Controlador modificamos el método actionPerformed() para que se incluya un objeto de la clase Buscador que proporcione la información del usuario al objeto panel.

Ahora solo queda probar todos estos cambios, el resultado al ejecutar la aplicación deberá verse al capturar un nombre un un correo en el formulario y presionar el botón "Buscar", tal y como se muestra en la siguiente imagen:


En este caso, buscamos un usuario con nombre María. Debido a que en la base de datos existe una registro con nombre 'María Rodríguez' la aplicación nos arrojó un resultado satisfactorio, gracias a que en el nivel de acceso a datos la búsqueda se realiza utilizando un LIKE '%%', aunque claro que el usuario será ajeno a todos esos detalles.

En caso de buscar algún nombre inexistente, el sistema nos debe desplegar un mensaje como el siguiente:


También pueden probar la búsqueda por correo electrónico, en donde la coincidencia con la información almacenada sí deberá ser exacta.

Claro que a este programa se le pueden hacer múltiples mejoras, como por ejemplo en la búsqueda por nombre desplegar una lista con la información de todos los usuarios, en lugar del primer usuario consultado, sin embargo el ejemplo cumple con el propósito de mostrar una forma para separar nuestras aplicaciones en una Vista, un Controlador y un Modelo, así como trabajar con capas verticalmente, yendo desde la base de datos hasta la presentación. Un aspecto que me agrada mucho de trabajar con objetos y que aquí se ve reflejado, es el encapsulamiento que proporcionan, es decir que los detalles de las tareas realizadas por una capa de más bajo nivel, quedan fuera del conocimiento de las capas superiores, a estas solo les interesarán los datos o mensajes que las capas de abajo arrojen al realizar sus tareas.

Con este último post es que finalizamos con el pequeño proyecto que fuimos construyendo a partir de la parte 2 de la programación de interfaces gráficas en Java. Ya vimos algunos detalles interesantes sobre la presentación de los componentes y el manejo de eventos, complementándolos con el acceso a una base de datos. En los siguientes post veremos algunos otros componentes de la interfaz gráfica, y cómo utilizarlos para crear una nueva aplicación para la manipulación de imágenes, que será el proyecto con el que concluiremos con esta serie de artículos sobre Java.

Hasta la próxima.

lunes, 17 de agosto de 2009

Diseño de interfaces gráficas en Java, Parte 4: Creación del Modelo de la aplicación


Primero la capa de datos

¡Saludos nuevamente!

En esta ocasión dejaremos un poco la parte de la presentación y nos enfocaremos en la creación de una pequeña lógica de negocio junto con el acceso a un sistema manejador de base de datos. Aunque podría parecer que me salgo del tema original relacionado con interfaces gráficas, considero que es importante tocar el punto en el que uniríamos el Modelo de la aplicación con la Vista y el Controlador, ya que es el punto al que finalmente se tiene que llegar en todo desarrollo de aplicaciones.

Para lograr este objetivo primero tenemos que considerar la comunicación horizontal de las capas de la aplicación, es decir la forma en que la información llegará desde la capa de acceso a datos hasta la capa de presentación. En el nivel más bajo tendremos un sistema manejador de bases de datos en que estará almacenada nuestra información, en este caso opte por el PostgreSQL, ya que es un gestor en el que apenas me estoy adentrando y nos servirá a todos los novatos para comenzar a dominar un nuevo administrador de base de datos.

Además es un SMBD open source rubusto y por si fuera poco, su mascota es un elefante, y todos sabemos que los elefantes son geniales.

Pueden descargar este sistema desde la sección downloads de la página oficial de PostgreSQL, eligiendo el sistema operativo de su preferencia.

Si descargan la versión para Windows notarán que además hay una aplicación visual de escritorio para administrar las base de datos en PostgreSQL, llamado pgAdmin. Esta aplicación viene incluida dentro del instalador de postgreSQL para este sistema operativo.

Dentro de las características de postgreSQL encontramos que:
- Es un manejador de base de datos dual al ser relacional y orientado a objetos, esto último es una características muy interesante ya que permite manejar conceptos como la herencia entre las tablas.
- Es un sistema multiusuario y multiplataforma.
- Soporta replicación.
- Nos permite utilizar un conjunto de datos complejos, como vectores, xml, figuras geométricas o arreglos, entre otros.
- Permite utilizar varios conceptos avanzados como llaves foráneas, constraints, vistas, transacciones y la propia herencia.
- Implementa un gran conjunto de instrucciones del lenguaje SQL.
- También permite implementar triggers en diferentes lenguajes de programación ajenos al lenguaje nativo procedimental.

La documentación completa de este sistema la encontraremos en la sección Documentation de la página oficial de la plataforma.

Para este ejemplo utilizaremos las funcionalidades estándar, como son crear una base de datos, crear una tabla, añadir registros y consultar.

Iniciando el servidor

Primero asegúrense de que el servidor de PostgreSQL está corriendo, en el caso de un escritorio de Windows pueden echarlo a andar desde Inicio > Todos los programas > PostgreSQL 8.4 > Start Server. Para un S.O. GNU/Linux en la consola de comandos pueden utilizar la instrucción:

#su -
Ingresa tu contraseña de root
#service postgresql initdb
Iniciamos por primera vez el cluster de postgreSQL
#service postgresql start
Iniciamos el servicio de postgreSQL


Si hemos instalado el pgAmin en nuestro escritorio podemos realizar cada uno de los siguientes pasos a través de su interfaz gráfica. En caso contrario también podemos utilizar el Shell de PostgreSQL con los comando. Dentro de Windows pueden encontrar este Shell en Inicio > Todos los programas > PostgreSQL 8.4 > Shell, para Linux basta que desde la consola de comando tenemos que estar logeados como root (tal y como quedamos en el paso anterior) e ingresar los siguientes comandos:

#su - postgres
Nos cambiamos a la cuenta del administrador de postgreSQL
#psql
Entramos al Shell de postgreSQL


Creación de la base de datos

Crearemos una nueva base de datos llamada pruebas. Desde el Shell sería:

postgres=#CREATE DATABASE pruebas WITH OWNER = postgres;


Nota: El siguiente paso es solo necesario para los que esten utilizando el Shell:

Salimos del Shell

postgres=#\q


y volvemos a ingresar, con los pasos que vimos antes, solo que ahora debemos seleccionar la base de datos pruebas que recién creamos. Desde el Shell de Windows bastará con en los parámetros que nos pide capturar en un inicio:

Server [localhost]:
Database [postgres]: pruebas
Port [5432]:
Username[postgres]:


Dejando el resto de los campos sin cambio para que conserven los valores por defecto que se indican. Mientras que en Linux bastará con el comando :

#psql -d pruebas


desde la consola de comandos. Recuerden que tenemos que estar logeados como el usuario postgres.

Creación de la tabla

Ahora, creamos una nueva tabla para esta base de datos llamada usuario, con las características que se muestran en el script:

CREATE TABLE usuario
(
"correoElectronico" character varying(50) NOT NULL,
nombre character varying(60),
domicilio character varying(60),
edad integer,
telefono character varying(20),
CONSTRAINT correo PRIMARY KEY ("correoElectronico")
)
WITH (
OIDS=FALSE
);
ALTER TABLE usuario OWNER TO postgres;


Desde el pgAdmin la creación de tablas se hace accediendo a la base de datos y dentro de Schemas > public seleccionamos la opción New Object > New Table...



Inserción de los registros

Ahora, solo queda insertar algunos registros en la tabla para hacer las pruebas desde nuestra aplicación en Java:

INSERT INTO usuario(
"correoElectronico", nombre, domicilio, edad, telefono)
VALUES
('maria@gmail.com', 'María Rodríguez', 'Av. Libertadores #5464', 19, '(611)6766676'),
('antonio@gmail.com', 'Antonio Márquez', 'Misiones #786', 22, '(611)9898981'),
('estela@gmail.com', 'Estela Saldivar', 'Av. Ruta de la Seda #9004', 25, '1184564');


Controlador de PostgreSQL para Java

De esta forma la base de datos esta preparada para ser utilizada. Ahora solo tenemos que incluir en nuestro proyecto de Java el controlador para conectarse con bases de datos de PostgreSQL. Este lo pueden descargar desde la página oficial del proyecto PostgreSQL > JDBC Driver, el cual consiste en un archivo .jar que contiene una biblioteca de clases para implementar el acceso a las bases de datos de este servidor. Una vez descargado el archivo, para incluirlas en nuestro proyecto de Eclipse, bastará con que configuremos el Build Path del JRE System Library (biblioteca del sistema de JRE) del proyecto, haciendo clic derecho sobre el JRE System Library y seleccionando:



Y dentro de la ventana que aparece seleccionamos el botón "Add External JARs..." con lo cual podremos agregar el archivo .jar descargado a la lista de referencia del proyecto:



Si están utilizando el entorno de desarrollo diferente de Eclipse entonces pueden usar la herramienta que posea para agregar archivos JAR externos.

Si por el contrario, están compilando y probando todos los programas desde la consola de comandos, una solución será modificar el CLASSPATH del programa para incluir la referencia al archivo .jar. En la documentación de la biblioteca que encuentran en la misma página de PostgreSQL > JDBC Driver se especifican los pasos, pero para ahorrar un poco el trabajo sería más o menos así:

Como ejemplo, si tenemos un archivo fuente llamado myapp.jar que contiene la definición de la clase MyApp, este archivo se encuentra en /usr/local/lib/myapp.jar y la biblioteca .jar está en /usr/local/pgsql/share/java/postgresql.jar, la ejecución de la aplicación sería:

#export CLASSPATH=/usr/local/lib/myapp.jar:/usr/local/pgsql/share/java/postgresql.jar:.
#java MyApp


Todo desde la consola de comandos. Otra solución, -que me pareció más sencilla- la encontré en este blog, y consisten en copiar el archivo en el directorio de extensiones de JRE, es decir JRE\lib\ext. Por ejemplo, la ruta absoluta en mi S.O. Windows es C:\Archivos de programa\Java\jre6\lib\ext, y variará según el sistema operativo en el que estén trabajando.

Y con esta referencia ya estaremos listos para pasar a la programación de la aplicación en Java, pero debido a lo mucho que se extendió este post, esa parte quedará pendiente para el siguiente artículo.

Recuerden que para cualquier duda u observación pueden dejar un comentario en este y todos los otros artículos.

Hasta la próxima.

jueves, 4 de junio de 2009

Notas para iniciarse en PHP, Parte 8 (Final): La funcionalidad


Manos a la obra


Y si ya dominamos todas las bases entonces ¿qué podemos hacer con PHP?

Cómo mencionamos antes, PHP nos ofrece un gran grupo de funciones internas para realizar diferentes trabajos, así como algunas clases complementarias que se han venido incorporando. Entre las funcionalidades cubiertas encontramos el manejo de archivos de texto, manipulación de bases de datos, envío de correos electrónicos, funciones matemáticas, operaciones con cadenas de texto, uso de los puertos físicos del equipo, manejo de archivos XML, compresión de archivos, creación de imágenes, y eso no es todo, al revisar el manual encontramos muchas opciones más, ya que las funcionalidades que no se encuentran dentro de PHP se pueden incorporar por medio de extensiones, lo cual se ha venido haciendo con varias de las funciones antes mencionadas, como es el caso de el manejo de las bases de datos, gracias a lo que ahora PHP cuenta con una colección de APIs que le permiten acceder a las gran mayoría de los sistemas de bases de datos existente, incluyendo los más importantes de la actualidad como son:
Junto con un grupo de funciones para una capa de abstracción de datos, que nos permiten acceder a diferentes fuentes de datos desde una misma interface:
  • DBA — Database (dbm-style) Abstraction Layer
  • dbx
  • ODBC — ODBC (Unified)
  • PDO — PHP Data Objects
Aquellas extensiones que no estén incorporadas dentro de las opciones del PHP con el que estemos trabajando, pueden incluir fácilmente de diferentes maneras. En Windows basta con copiar los archivos binarios (DLL) en el directorio de extensiones de PHP, normalmente ubicado en el directorio /ext dentro del directorio principal de PHP, y modificar el archivo de configuración php.ini agregando en la sección de extensiones dinámicas la línea extension=nombre_de_la_extension.dll, por ejemplo, para agregar la extensión de acceso a bases de datos de MS SQLServer, bastaría con agregar extension=msql.dll, entonces solo tendríamos que reiniciar el servidor Web para que el cambio surtiera efecto.

En GNU/Linux en cambio, en lugar de archivos DLL se utilizan los archivos Shared Object o de objetos compartidos, que funcionan de manera similar a los DLL ya que nos permiten agregar funcionalidades adicionales a nuestras aplicaciones desde un componente independiente. Para el ejemplo anterior, primero tendríamos que haber incorporado a PHP el archivo binario .so de la extensión, y después incluir en el archivo de configuración la línea extension=msql.so, el primer paso se puede hacer de diferentes formas, como con los asistentes de actualización de software que nos brinda la distribución del sistema operativo que estemos utilizando, algunos de los cuales nos liberan del segundo paso de modificar el archivo php.ini, otra opción más es utilizar el módulo php-dev, que desde la consola de comandos nos permite compilar e instalar extensiones dinámicas para PHP sin la necesidad de tocar el archivo de configuración, gracias a que incorpora a nuestro sistema la instrucción phpize.


Algunas funciones interesantes

Ahora mostraré algunos ejemplos de las diferentes funciones trabajando, como una muestra de los que se puede hacer con PHP, veamos:

Manejo de archivos de texto

Entre las funciones para manipulación de archivos de texto, encontramos las funciones fread() y fwrite(), que nos permiten leer y escribir en archivos respectivamente. En el siguiente ejemplo se muestra una de los usos que podemos darle a estas funciones implementado un contador de vistas:

contador.php
<?php
$total = 0;
$archivo = "contador.txt"; //Nombre del archivo de texto
if(!file_exists($archivo)) { //Comprobamos la existencia del archivo contador.txt
$abre = fopen($archivo, "w"); //De no existir el archivo
$grabar = fwrite($abre, "0"); //lo creamos guardando el valor de 0 dentro
fclose($abre);
}
else {
$grabar = true;
}
if($grabar) {
// Abrimos el archivo para solamente leerlo ("r" para "read")
$abre = fopen($archivo, "r");
// Leemos todo el contenido del archivo
$total = fread($abre, filesize($archivo));
// Cerramos el archivo
fclose($abre);

// Abrimos nuevamente el archivo pero ahora para escribir
$abre = fopen($archivo, "w");
// Sumamos 1 nueva visita
$total = $total + 1;
// Y reemplazamos el contenido por la nueva cantidad de visitas
$grabar = fwrite($abre, $total);
// Cerramos la conexión al archivo
fclose($abre);
}
?>
<html>
<head><title>Contador de visitas</title></head>
<body>
<div align="center">
Eres el visitante número <?php echo $total?> de esta página.
</div>
</body>
</html>


El programa PHP implementa nuestro contador, el cual va llevando dentro de un archivo de texto la cuenta de las ocasiones en que la página es cargada por los usuarios, para posteriormente desplegar la información dentro de la misma.

Si lo que queremos en cambio es acceder únicamente al texto de un archivo para usarlo como una cadena, es más eficiente utilizar la función file_get_contents(), que recibe el nombre o la URL de un archivo y devuelve su contendido como una cadena de texto para que la podamos manipular.

Funciones para cadenas de texto

Entre la gran cantidad de funciones existentes para el manejo de cadenas de texto, encontramos un interesante conjunto contenido en por las funciones POSIX Regex y PCRE, ya que en ambos grupos encontramos que podemos utilizar expresiones regulares para el trabajo de cadenas, así como para la transformación de arreglos a cadenas y viceversa.

Una muestra interesante de esto es la función split que encontramos entre las funciones POSIX:
<?php
$cadena = "En ese camino lago\nQué un día me vio caminar\nNació esta ciega herida";
$arreglo = split("\n", $cadena); //Separamos la cadena por los saltos de línea que contenga
print_r($arreglo);
?>


La cual permite crear un arreglo a partir de una cadena de texto, dividiendo en partes la cadena según el texto que proporcionamos como segundo parámetro a la función. El ejemplo anterior arrojaría un resultado como el siguiente en el navegador:
Array ( [0] => En ese camino lago [1] => Qué un día me vio caminar [2] => Nació esta ciega herida )


Las funciones PCRE son extensiones tipo PERL, que utilizan en todo momento expresiones regulares para trabajar, las cuales resulta muy útiles al momento de validar cadenas de texto, por ejemplo si queremos saber si una cadena cumple con un determinado formato podemos evaluarla con la función preg_match:

validarCadena.php
<?php
$er_nombre = "/^([a-zA-Z]|[ÁÉÍÓÚáéíóúÑñÜü]|[\s,\.'\-])+$/"; //Expresión regular que será utilizada
if(isset($_GET['txtNombre'])) {
$nombre = stripslashes($_GET['txtNombre']); //Eliminamos el caracter de diagonal invertida que PHP agrega automaticamente para los caracteres de comilla simple (')
if(preg_match($er_nombre,$nombre)) //Evaluamos el contenido del texto recibido
$respuesta = "Nombre correcto :)";
else
$respuesta = "Nombre no válido :(";
}
?>
<html>
<head><title>Validador</title></head>
<body>
<form method="get">
Escribe un nombre <input type="text" name="txtNombre" /> <input type="submit" name="enviar" value="Validar" />
</form>
<p><?php echo $respuesta?></p>
</body>
</html>


En el ejemplo anterior se creó una expresión contenida en $er_nombre que sirvió para validar el texto capturado en el formulario de la página, al ser utilizada por la función preg_match(), la cual devuelve un valor booleano dependiendo de si el texto cumple o no con la expresión regular. Puede verse entonces que estas expresiones son herramientas muy útiles al momento de validar los datos capturados por los usuarios en nuestras páginas.

Sin embargo es recomendable utilizar las expresiones regulares solo cuando la situación lo amerita, ya que el tiempo de procesamiento que consumen es superior al de otras instrucciones de procesamiento de texto, que cuales utilizan cadenas simples como parámetros para manipular cadenas.

Manipulación de bases de datos

Como un ejemplo de la funcionalidad ofrecida, vamos probar la extensión mysql par el manejo de bases de datos MySQL, con la cual ya trabajamos en algunos de los artículos antiguos, convendría que revisaran el tema de Introducción al uso de MySQL que tratamos en este blog hace un tiempo para poder seguir este ejemplo, ya que aquí trabajaremos con la misma base de datos. En el siguiente código podemos ver el programa PHP en acción:

consultaMySQL.php
<?php
//Parámetros para la conexión a la base de datos
$servidor = "localhost";
$usuario = "root";
$contrasenia = "[TuConstraseña]";
$baseDeDatos = "obras_literarias";
$conexion = @mysql_connect($servidor,$usuario,$contrasenia); //Abrimos la conexión con MySQL
if(mysql_select_db($baseDeDatos,$conexion)) { //Accedemos a la base de datos
//Establecemos que la información que se intermabiará estará codificada en utf-8
mysql_query("SET NAMES 'utf8'",$conexion);
//Comprobamos que se recibieron correctamente
if(isset($_GET['txtNombre']) && ($nombre = stripslashes(trim($_GET['txtNombre'])))!="") {
$consulta = "SELECT * FROM autores WHERE Nombre LIKE '%".addslashes($nombre)."%'";
$resultado = mysql_query($consulta,$conexion); //Realizamos la consulta
if($resultado) { //Si no hubo error
if(mysql_num_rows($resultado) > 0){//Comprobamos si se encontraron registros
//Almacenamos en una matriz de arreglos asociativos los registros que resultaron
$matriz = array();
while($renglon = mysql_fetch_assoc($resultado)) {//Extrae un renglon del resultado como un arreglo asosciativo
$matriz[] = $renglon;
}
}
else{
$mensajeDeError = "Autor no encontrado";
}
}
else {
$mensajeDeError = mysql_error();
}
}
else {
$mensajeDeError = "Datos imcompletos";
}
}
else{
$mensajeDeError = mysql_error();
}
?>
<head>
<title>Consulta a MySQL</title>
</head>
<body>
<form method="get">
Escribe un nombre <input type="text" name="txtNombre" /> <input type="submit" name="enviar" value="Validar" />
</form>
<? if($mensajeDeError != "") :?>
<p><?php echo $mensajeDeError?></p>
<? else :?>
<table>
<tr><td>Id</td><td>Nombre</td><td>Nacionalidad</td><td>Profesion</td></tr>
<? foreach($matriz as $autor):?>
<tr><td><?=$autor['Id']?></td><td><?=$autor['Nombre']?></td><td><?=$autor['Nacionalidad']?></td><td><?=$autor['Profesion']?></td></tr>
<? endforeach;?>
</table>
<? endif;?>
</body>
</html>

*Nota: Si desean probar el programa recuerden reemplazar el valor de la contraseña en el texto señalado por [tuContraseña].

En este ejemplo, vemos la forma en la que podemos acceder a una base de datos de MySQL y realizar una consulta sobre una tabla, para posteriormente desplegar la información obtenida dentro de una página HTML, lo cual puede parecer algo muy sencillo pero nos permite darnos una idea del potencial de PHP para trabajar son sistemas manejadores de bases de datos. Para ampliar un poco más el ejemplo anteriorla es también pueden revisar el post de este blog titulado Otra forma de acceder a la base de datos con PHP, donde se describe la creación de una clase para abstracción del acceso a datos, es decir que creamos una clase que encapsulará todas las funcionalidades de la extensión mysql dentro de sus métodos, para facilitar el trabajo con la base de datos y el mantenimiento de nuestros programas.

Espero que con este breve artículo hayan podido hacerse una idea general de la funcionalidad de PHP para la creación de proyectos profesionales, como siempre hay que recordar que estos documentos son un compendio de los contenidos de PHP pero no una guía definitiva, por lo que de estarse iniciando en el aprendizaje del lenguaje conviene que revisen los enlaces que fuí colocando como referencias en cada tema. Ahora dependerá de cada desarrollador decidir si PHP es la herramienta que necesitan para resolver sus problemas y de ser así, qué funciones del lenguajes les serán útiles. Y con esta conclusión es que finaliza la serie de post sobre el aprendizaje de PHP que he venido realizando, espero que más de un programador encuentre útil alguna de mis notas y de ser posible me envíen algún comentario.

Hasta la próxima.

jueves, 16 de abril de 2009

Replicación unidireccional en servidores MySQL

¡Saludos nuevamente blogueros!

Después de unas largas vacaciones he decidido regresar con un post sobre bases de datos, nuevamente con MySQL, respondiendo a una duda que siempre me persiguió mientras cursaba la carrera ¿cómo realizar una replicación de una base de datos entre servidores MySQL?

Al investigar me encontré con que existen dos maneras de implementar la replicación entre servidores: una es a través de la replicación asíncrona unidireccional y la otra utilizando MySQL Cluster.

En la primera se tiene un servidor de base de datos maestro, en el cual ocurren todas las actualizaciones a la base de datos, desde el cual los datos son replicados a los servidores esclavos, de forma que se tiene un flujo de información solo de los maestros hacia los esclavos, de ahí la razón de llamarlo unidireccional, además se considera asíncrono porque el servidor maestro y los esclavos no necesitan estar en comunicación todo el tiempo, sino que los esclavos pueden permanecer fuera de línea y los cambios que ocurran en el maestro se replicarán en cuanto se restaure la conexión entre ambos.


Esquema de replicación unidireccional de MySQL


El segundo método MySQL Cluster consiste en la implementación de un cluster de servidores, todos los servidores que formen parte de este sistema de replicación se comportan como si se tratara de un solo servidor, por lo que cualquier cambio en un miembro del sistema se replica en en el resto inmediatamente, a través de los mecanismos de sincronización que esta tecnología posee, de forma similar a lo que ocurre con los cluster de computadoras.


Esquema de MySQL Cluster

En este artículo me enfocaré a la replicación unidireccional, que es un mecanismo muy efectivo para mantener copias de seguridad de nuestras bases de datos, permitiendo además distribuir la carga de trabajo con las bases de datos entre varios servidores.

Como ya se mencionó, dentro de este esquema trabajaremos con los conceptos de servidor maestro y servidor esclavo, a grandes rasgos lo que ocurre es que el servidor maestro guarda un historial de las actualizaciones realizadas en la base de datos replicada dentro de un log binario, después cada servidor esclavo recibe las actualizaciones que el maestro ha guardado en este registro, de forma que el esclavo puede ejecutar las mismas actualizaciones en su copia de los datos.

El punto de partida importante aquí es que en ambos servidores debe existir la misma base de datos, con un nombre idéntico y los mismos datos contenidos, antes de arrancar con el proceso de replicación, ya que, como el sitio oficial de MySQL nos indica, de no ser así al momento que los esclavos intenten ejecutar las instrucciones del log binario del maestro puede ocurrir algún conflicto.

Configuración del maestro

El primer paso en la replicación consiste en configurar uno de los servidores como maestro para la replicación, para ello el primer paso consiste activar el log binario en el servidor maestro, para que éste pueda registrar todos los cambios que ocurran en la información de la base de datos. Para conseguir este objetivo tenemos que modificar el archivo de configuración de MySQL, llamado my.cnf en GNU/Linux y my.ini en Windows:

Dentro del archivo agregaremos las siguientes líneas:
log-bin=mysql-bin
server-id = 1


En donde log-bin indica el nombre del archivo de log binario y server-id define al identificador del servidor, lo más común es que al maestro lo identifiquemos como el número 1 y a los esclavos con los números sucesivos.

Hecho esto, al reiniciar el servidor MySQL se creará automáticamente el archivo del log binario, el esto lo podemos hacer desde el sistema operativo con algún asiste o desde la consola de comandos, en GNU/Linux se puede utilizar:
> service mysqld restart

La siguiente tarea consiste en crear una cuenta de usuario para replicación, que le permita al servidor esclavo autentificarse con el maestro. Dentro de la consola de comandos de MySQL escribimos el siguiente comando:

mysql> GRANT REPLICATION SLAVE ON [base de datos].[tabla] TO '[usuario de replicación]'@'[ip del esclavo]' IDENTIFIED BY '[contraseña del usuario de replicación]';

El comando GRANT indica que se creará una nueva cuenta, mientras que la instrucción REPLICATION SLAVE indica que servirá para la replicación en un esclavo. Dentro de los parámetros de la instrucción encontramos que:
  • [base de datos] este indica el nombre de la base de datos que deseamos replicar, en caso de que queramos que se repliquen todas las bases de datos del servidor entonces escribimos el caracter asterisco * en lugar de un nombre;
  • [tabla] si deseamos realizar únicamente la replicación de una tabla, entonces este parámetro deberá indicar el nombre de la tabla que deseamos replicar, si queremos replicar todas las tablas de una base de datos entonces dejamos el caracter asterisco * en lugar del nombre de la tabla, por ejemplo, si queremos replicar todas las tablas de la base de datos obras_literarias el comando deberá llevar indicar:
mysql> GRANT REPLICATION SLAVE ON obras_literarias.* TO '[usuario de replicación]'@'[ip del esclavo]' IDENTIFIED BY '[contraseña del usuario de replicación]';
  • [usuario de replicación] será el nombre de usuario que utilizaremos en el esclavo para iniciar la replicación;
  • [ip del esclavo] representa la dirección ip del servidor esclavo, para que de esta forma el servidor MySQL maestro valide que solo se realicen accesos de replicación desde éste servidor, si se desea que se pueda acceder al maestro desde cualquier servidor para realizar replicaciones entonces en lugar de una ip se debe colocar el caracter de porcentaje %;
  • [contraseña del usuario de replicación] como su nombre lo indica, es la contraseña que utilizaremos junto con el nombre de usuario en el esclavo para iniciar la replicación.

Ahora, hecho esto, solo tenemos que asegurarnos que la base de datos del maestro exista en los servidores esclavos en los que se va a replicar, para esto existen varias formas, una de ellas consiste en realizar un dump o respaldo de toda la base de datos en un archivo y utilizarlo para crear la base de datos en el esclavo, para realizarlo de este modo primero tenemos que bloquear las tablas de la base de datos haciendo lo siguiente:
mysql> FLUSH PRIVILEGES;
mysql> LOCK ALL TABLES READ ONLY;
mysql> exit;


De vuelta a la consola de comandos del sistema operativo (MS-DOS en Windows) realizamos el respaldo de la base de datos por medio del comando mysqldump, por ejemplo si deseamos respaldar la base de datos obras_literarias:
> mysqldump obras_literarias > obras_20090417.sql

Una vez terminado el proceso, regresamos a la consola de comando de MySQL y obtenemos algunos datos del servidor maestro que necesitaremos después, usando el comando SHOW MASTER STATUS:
mysql> SHOW MASTER STATUS;


Esto desplegará una tabla como la siguiente:

+------------------+-------------+-----------------+------------------+
| File | Position | Binlog_Do_DB | Bilog_Ignore_DB |
+------------------+-------------+-----------------+------------------+
| mysql-bin.000001 | 389| | |
+------------------+-------------+-----------------+------------------+


De esta tabla debemos tomar nota de los campos File y Position, ya que los utilizaremos al configurar el servidor esclavo, el campo File indica el nombre archivo log binario para la replicación mientras que la posición indica el desplazamiento dentro del archivo de la última operación.

Finalmente realizado el dump y después de localizar estos campos, podemos desbloquear nuestras tablas, por medio del comando:
mysql> UNLOCK ALL TABLES;


Ahora que el servidor ya cuenta con una configuración de maestro podemos reiniciarlo desde la consola de MySQL con el comando:
mysql> RESET MASTER;


Configuración del esclavo


En el servidor esclavo, agregaremos la línea a su archivo de configuración de MySQL:
server-id=2


De forma que este esclavo tendrá como identificador el número 2. Reiniciamos el servidor MySQL para que este cambio surta efecto, en GNU/Linux se puede utilizar el comando service:
> service mysqld restart


Ahora tenemos que crear la base de datos y las tablas que respaldamos en el servidor maestro, para ello, en el esclavo ahora ejecutamos lo siguiente:
> mysql obras_literarias < obras_20090417.sql


Y una vez que el esclavo tenga generada la base de datos, lo configuramos para que esté posicionado con respecto al maestro:

mysql> CHANGE MASTER TO MASTER_HOST = '[ip del maestro]';
mysql> CHANGE MASTER TO MASTER_USER = '[usuario de replicacion]';
mysql> CHANGE MASTER TO MASTER_PASSWORD = '[contraseña de replicacion]';
mysql> CHANGE MASTER TO MASTER_LOG_FILE = '[archivo de log binario]';
mysql> CHANGE MASTER TO MASTER_LOG_POS = [posición en el archivo de log binario];
mysql> START SLAVE;


En la implementación real, se tendrían que los valores de [archivo de log binario] y [posición en el archivo de log binario] equivalen a los valores de los campos File y Position de que tomamos nota respectivamente. En el ejemplo que se mostró correspondían a mysql-bin.000001 y 389.

Con el comando START SLAVE se inicia la replicación de las actualizaciones que suceden en el maestro dentro de la base de datos del esclavo, para comprobarlo se puede utilizar el comando SHOW SLAVE STATUS:
mysql> SHOW SLAVE STATUS\G


Este fue una pequeña guía que espero que le sirva a todos aquellos que están iniciando en el tema de la replicación de bases de datos con MySQL, si desean más información sobre estos temas, la pueden encontrar en la página oficial de MySQL que trata sobre los pasos para montar una replicación.

martes, 10 de marzo de 2009

Consumiendo un Servicio Web en PHP desde AJAX, Parte 1: Crear el servicio


Otra solución para múltiples plataformas


Otra forma de que tenemos para acceder a la funcionalidad de nuestro servidor Web es a través de los Servicios Web, cuyo funcionamiento es similar al modo de trabajo que hemos utilizado hasta ahora en el que se solicita una página Web por medio de una petición HTTP, mandándole una serie de parámetros y recibiendo e interpretando la respuesta del servidor. Ahora bien, como la información nos indica los Servicios Web consisten una serie de estándares y protocolos para llevar a cabo el intercambio de datos entre nuestros clientes y el servidor Web, y es gracias a ésto que existe una diferencia entre ambos modos de trabajo, ya que gracias a estos protocolos trabajaremos de manera más transparente al realizar la comunicación entre nuestras aplicaciones, al dejar varios de los detalles de esta labor a la implementación del servicio Web que estemos utilizando. El gran potencial de los servicios Web es que permiten la comunicación de aplicaciones situadas en diferentes plataformas de manera estándar, por lo que podríamos tener un servicio Web hecho en .Net y consumirlo desde un cliente Java, o un servicio Web en PHP y consumirlo desde un cliente .Net, con total independencia del sistema operativo o el manejador de base de datos que estemos utilizando.

En este artículo, me propongo mostrar el ejemplo del uso de un Servicio Web programado en PHP desde nuestro cliente AJAX, realizando las operaciones de consulta, inserción y actualización de los registros de una tabla en nuestra base de datos obras_literarias de MySQL, las cuales serán solicitadas desde nuestro cliente AJAX.

Antes de comenzar

Para desarrollar nuestro servicio Web desde PHP utilizaremos las implementaciones de los protocolos XML-RPC/SOAP que nos ofrece la clase soap_server, la cual esta contenido en la biblioteca del archivo nusoap.php que podemos descargar de manera totalmente gratuita desde el enlace que acabamos de indicar -la documentación completa la encuentran aquí-, en mi caso la última versión disponible que encontré es la 0.7.3. Una vez a nuestra disposición incluiremos el archivo nusoap.php en el directorio de nuestro servidor Web para hacerle referencia en el programa PHP que realizaremos. Además nos apoyaremos en la clase AccesoADatos que definimos en un post anterior.

Nomenclatura utilizada:

-El protocolo de comunicación que se utilizará esSOAP, que es el que define el mecanismo de comunicación entre un cliente (en esta caso en AJAX) y un servicio Web (en este caso en PHP).
-En la definición de este servicio Web también aparecerá el término WSDL siglas de Web Services Description Language, que es un formato XML que se utiliza de manera estándar para describir las características de los servicios Web. Dentro del código definiremos algunas de las características del WSDL del servicio Web que implementaremos.

El trabajo con la base de datos

El sistema manejador de base de datos seguirá siendo MySQL, dentro del cual tendremos la base de datos obras_literarias, en la cual encontramos la tabla autores, que ya habíamos utilizado en los posts anteriores y se puede crear a través del siguiente script:


CREATE TABLE `autores` (
`Id` mediumint(9) NOT NULL,
`Nombre` varchar(60) NOT NULL,
`Nacionalidad` varchar(50) NOT NULL,
`Profesion` varchar(60) NOT NULL,
PRIMARY KEY (`Id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;


Si lo deseamos podemos insertar algunos registros, aunque no es un requisito ya que el sistema que haremos incluirá la inserción de registros:
INSERT INTO `autores` ( `Id` , `Nombre` , `Nacionalidad` , `Profesion` )
VALUES (
'1', 'Juan Rulfo', 'Mexicano', 'Novelista'
),(
'2', 'Pablo Neruda', 'Chileno', 'Poeta'
),(
'3', 'Miguel de Cervantes Saavedra', 'Español', 'Novelista, poeta y dramaturgo'
),(
'4', 'Jorge Luis Borges', 'Argentino', 'Ensayista, escritor y poeta'
);


Trabajo del lado del servidor

Para el servidor crearemos el programa PHP servicio.php, dentro del cual declararemos el servicio Web SOAP que que contendrá los métodos que realizarán las labores de consulta y actualización de la tabla autores.

El código para el trabajo es el siguiente (en seguida explicaré cada uno de los pasos que se fueron realizando):

servicio.php

<?php
require("nusoap.php"); //Referencia al archivo que contiene la clase soap_server
require("AccesoADatos.php");//Referencia al archivo que contiene la clase AccesoADatos
//Implementación del servicio Web
$servicio = new soap_server; //Instancia de la clase para la creación del servicio Web
$servicio->soap_defencoding = "UTF-8"; //Señalamos que el juego de caracteres que utilizaremos es UTF-8
/*
* Definimos un espacio de nombres (namespace) para el servicio Web, funciona como un identificador del servicio Web
* Web -en este caso usamos una URL-
*/
$ns = "http://localhost";
$servicio->configureWSDL('ServicioDeAutores',$ns); //Configuramos el WSDL del servicio Web (Param 1: Nombre del servicio Web, Param 2: Namespace del servicio Web)

/*
*Definimos cuales serán las funciones que poseerá nuestro servicio Web con el metodo register, como primer
* parámetro recibe el nombre de la función, en el segundo y tercer parámetro se incluyen dos arreglos asociativos que especifican los parámetros
* que reciben y los valores devueltos por estas funciones respectivamente
*/
$servicio->register('buscaAutor',
                    array('id'=>'xsd:string'),
                    array('nombre'=>'xsd:string','nacionalidad'=>'xsd:string','profesion'=>'xsd:string','error'=>'xsd:string'),
                    $ns); //Recibirá el id del autor que es de tipo string y devolverá un arreglo con los datos del registro
$servicio->register('actualizaAutor',
                    array('id'=>'xsd:string','nombre'=>'xsd:string','nacionalidad'=>'xsd:string','profesion'=>'xsd:string'),
                    array('mensaje'=>'xsd:string'),
                    $ns); //Recibirá los datos del autor y devolverá un mensaje tipo string
//Obtenemos los parámetros del encabezado HTTP
if(isset($HTTP_RAW_POST_DATA))
{
    $input = $HTTP_RAW_POST_DATA;
}
else
{
    $input = implode("\r\n",file("php://input")); //Secuencia de entrada php://input
}
$servicio->service($input); //Con este método que recibe el contenido del encabezado HTTP se procesa la petición para el servicio Web y se genera la respuesta
exit;

//Funciones para el servicio web
function buscaAutor($id) { //Función para la busqueda de autores
    $acceso = new AccesoADatos('localhost','root','[TuContraseña]','obras_literarias');
    $autor = array('nombre'=>"",'nacionalidad'=>"",'profesion'=>"");
    if($acceso->abreConexion()) { //Accedemos a la base de datos
        $consulta = "SELECT * FROM autores WHERE Id = $id LIMIT 1";
        if($acceso->consulta($consulta)) {//Realizamos la consulta y comprobamos el resultado
            if($acceso->devuelveRegsLeidos() > 0){//Comprobamos si se encontró el registro                                
                $renglon = $acceso->devuelveArreglo();//Extrae el renglon del resultado como un arreglo asosciativo
                $autor['nombre'] = $renglon['Nombre'];
                $autor['nacionalidad'] = $renglon['Nacionalidad'];
                $autor['profesion'] = $renglon['Profesion'];
            }
            else
                $autor['error'] = "Error: No se localizó un autor con el identificador específicado";
        }
        else
            $autor['error'] = "Error de acceso a datos";
    }
    else
        $autor['error'] = "Error de acceso a datos";
    return $autor;
}

function actualizaAutor($id, $nombre, $nacionalidad, $profesion) { //Función para la actualización de la tabla autores
    $acceso = new AccesoADatos('localhost','root','[TuContraseña]','obras_literarias');
    if($acceso->abreConexion()) { //Accedemos a la base de datos     
        $datos = array('Nombre'=>$nombre,'Nacionalidad'=>$nacionalidad,'Profesion'=>$profesion);
        $consulta = "SELECT * FROM autores WHERE Id = $id LIMIT 1";
        if($acceso->consulta($consulta)) { //Realizamos la consulta y comprobamos el resultado
            if($acceso->devuelveRegsLeidos() > 0) { //Comprobamos si se encotró el registro
                $campoLlave = array('Id'=>$id); //Si se encontró, significa que se está actualizando un registro existente                              
                if($acceso->actualizaRegistro("autores",$datos,$campoLlave) > 0)//Realizamos la actualización del registro                            
                    $mensaje = "El registro fué actualizado exitosamente";            
                else
                    $mensaje = "Error: No se registraron cambios en la información del autor";                                        
            }
            else { //En caso contrario se está realizando una insersión
                $datos['Id'] = $id;
                if($acceso->insertaRegistro("autores",$datos) > 0)//Realizamos la inserción del registro
                    $mensaje = "El registro fué insertado exitosamente";            
                else
                    $mensaje = "Error: No se completo la inserción del registro";
            }
        }
        else
            $mensaje = "Error de acceso a datos";            
    }
    else
        $mensaje = "Error de acceso a datos";     
    return $mensaje;
}
?>


Como vemos, al inicio se incluye la referencia a los archivos de las clases que utilizaremos:

require("nusoap.php");
require("AccesoADatos.php");

para implementar el servicio Web y para acceder a la base de datos respectivamente.

En seguida se crea la instancia de la clase soap_server con la cual implementaremos el servicio Web:

$servicio = new soap_server;


Específicamos que el juego de caracteres utilizado es UTF-8:

$servicio->soap_defencoding = "UTF-8";


Por medio de la variable $ns definimos el namespace del servicio Web, que representa el identificador único de nuestro servicio, este caso representado por medio de la URL de nuestro servidor Web local, que indica el directorio en el que podemos localizar el archivo del servicio Web por medio de HTTP:

$ns = "http://localhost";


También podríamos utilizar una URL con dirección IP, o una dirección con un nombre dominio propio servidor si disponemos de una.

En seguida definimos el WSDL del servicio Web, estableciendo el nombre del servicio Web como "ServicioDeAutores" y su namespace como "http://locahost/PruebasPHP":

$servicio->configureWSDL('ServicioDeAutores',$ns);


Una vez hecho esto a través del método registrer() especificamos cuales serán las funciones que ofrecerá ServicioDeAutores:

$servicio->register('buscaAutor',
                    array('id'=>'xsd:string'),
                    array('nombre'=>'xsd:string','nacionalidad'=>'xsd:string','profesion'=>'xsd:string','error'=>'xsd:string'),
                    $ns);
$servicio->register('actualizaAutor',
                    array('id'=>'xsd:string','nombre'=>'xsd:string','nacionalidad'=>'xsd:string','profesion'=>'xsd:string'),
                    array('mensaje'=>'xsd:string'),
                    $ns);

En el primer parámetro de este método definimos el nombre de la función mencionada, en el segundo se definen los parámetros de esta función, el tercer parámetro define los valores que serán devueltos por la función, pudiendo tratarse de un único valor o de un arreglo asociativo, dependiendo del número de valores que se definan. Cómo último parámetro el método register() recibe el namespace del servicio Web. La declaración de estas funciones aparecerá en el bloque final del código del archivo.

En seguida se obtiene el contenido completo del encabezado HTTP que recibe el programa PHP:

if(isset($HTTP_RAW_POST_DATA))
{
    $input = $HTTP_RAW_POST_DATA;
}
else
{
    $input = implode("\r\n",file("php://input"));
}

Este encabezado se puede ser accedido por medio de la variable predifinida $HTTP_RAW_POST_DATA o utilizando la secuencia de entrada php://input.

A continuación y para finalizar con la implementación del servicio Web utilizamos el método service(), el recibe el contenido del encabezado HTTP y procesa la petición para el servicio Web y se genera la respuesta XML:

$servicio->service($input);
exit;


Seguido de la instrucción exit para finalizar el programa PHP.

En el siguiente bloque de código aparece la declaración de las funciones buscaAutor y actualizaAutor, en la primera se lleva a cabo la búsqueda de una autor en la base de datos por medio del $id recibido. La segunda realiza la actualización o inserción de un registro, dependiendo de si el valor campo "Id" proporcionado existe o no en la base de datos.

Nota: Antes de probar el servicio Web recuerden modificar el código anterior agregando la contraseña de acceso a su SMBD MySQL en la parte indicada por [TuContraseña].

Y ahora, gracias a este este código podremos implementar nuestro servicio Web que será consumido por el cliente AJAX que desarrollaremos, con la ventaja de que este mismo servicio puede ser utilizado por cualquier otra aplicación que soporte la utilización de servicios Web.

Para probar que todo anda bien con el ServicioDeAutores podemos acceder a él por medio de nuestro navegador, utilizando la ruta del servidor Web en la que está almacenado, para este ejemplo ésta sería: http://localhost/servicio.php, obteniendo un resultado como el que sigue:



Debido a que nuestro navegador es capas de interpretar el contenido XML de la respuesta del servicio Web, nos ofrece la posibilidad de visualizar su WSDL, así como las características de la funciones que éste posee. El WSDL arrojado en este ejemplo es el siguiente:

img


Mientras que al seleccionar alguna de las funciones nos ofrece una descripción como la que sigue:



Si todo salió bien nuestro servicio Web ya funciona, ahora todo lo que queda es probarlo desde un cliente, cuya programación veremos en el siguiente artículo.

Hasta la próxima.