23 - Biblioteca Volley - Consulta, modificación y borrado de datos de un servidor (PHP y MySQL)

Problema

Implementar una aplicación que permita consultar, modificar y borrar registros de una tabla de MySQL localizada en Internet y su acceso por un Script en PHP.

Tenemos la tabla en un servidor web llamada:

create table articulos (
    codigo int primary key AUTO_INCREMENT,
    descripcion varchar(50),
    precio float
);

Crearemos el proyecto 'Compose25'

Agregamos las dependencias de la biblioteca Volley:

dependencies {
        ...
        implementation 'com.android.volley:volley:1.2.0'
    }

Nuestra aplicación debe acceder a internet por lo que debemos pedir dicho permiso:

<uses-permission android:name="android.permission.INTERNET"/>

Debemos llamar a las URLs:

   https://scratchya.com.ar/videosandroidjava/volley/consultar.php?codigo=?
   https://scratchya.com.ar/videosandroidjava/volley/modificar.php
   https://scratchya.com.ar/videosandroidjava/volley/borrar.php

En el servidor hay un archivo en PHP llamado 'consultar.php':

<?php
header('Content-Type: application/json');

require("conexion.php");

$conexion = retornarConexion();

$datos = mysqli_query($conexion, "select codigo,descripcion,precio from articulos where codigo=$_GET[codigo]");
$resultado = mysqli_fetch_all($datos, MYSQLI_ASSOC);
echo json_encode($resultado);
?>

En el servidor hay otro archivo PHP llamado 'modificar.php':

<?php
header('Content-Type: application/json');

$datos = json_decode(file_get_contents("php://input"), true);

require("conexion.php");
$conexion = retornarConexion();

mysqli_query($conexion, "update articulos set descripcion='$datos[descripcion]', precio=$datos[precio] where codigo=$datos[codigo]");
$cant = mysqli_affected_rows($conexion);
if ($cant==1)
	echo '{"resultado":"1"}';
else
	echo '{"resultado":"0"}';

En el servidor hay otro archivo PHP llamado 'borrar.php':

<?php header('Content-Type: application/json'); $datos = json_decode(file_get_contents("php://input"), true); require("conexion.php"); $conexion = retornarConexion(); mysqli_query($conexion, "delete from articulos where codigo=$datos[codigo]"); $cant = mysqli_affected_rows($conexion); if ($cant==1) echo '{"resultado":"1"}'; else echo '{"resultado":"0"}';

Y un archivo 'conexion.php'

<?php
function retornarConexion() {
    $server="localhost";
    $usuario="xxxx";
    $clave="xxxx";
    $base="xxxx";
    $con=mysqli_connect($server,$usuario,$clave,$base) or die("problemas") ;
    mysqli_set_charset($con,'utf8'); 
    return $con;
}
?>

La interfaz visual que debemos implementar con Compose debe ser similar a:

biblioteca Volley Jetpack Compose

El código a implementar en Kotlin para obtener dicha funcionalidad es:

package com.tutorialesprogramacionya.compose25


import android.content.Context
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Button
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.input.KeyboardType
import com.android.volley.Request
import org.json.JSONException
import org.json.JSONObject
import com.android.volley.toolbox.JsonArrayRequest
import com.android.volley.toolbox.Volley
import com.android.volley.toolbox.JsonObjectRequest

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ConsultaArticulo()
        }
    }
}

@Composable
fun ConsultaArticulo() {
    val contexto = LocalContext.current
    Column(
        modifier = Modifier.fillMaxSize()
    ) {
        var codigo by remember { mutableStateOf("") }
        var descripcion by remember { mutableStateOf("") }
        var precio by remember { mutableStateOf("") }
        var mensaje by remember { mutableStateOf("") }
        OutlinedTextField(
            value = codigo,
            onValueChange = { codigo = it },
            label = {
                Text("Código")
            },
            modifier = Modifier
                .fillMaxWidth()
                .padding(10.dp),
            singleLine = true,
            keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
        )
        OutlinedTextField(
            value = descripcion,
            onValueChange = { descripcion = it },
            label = {
                Text("Descripción")
            },
            modifier = Modifier
                .fillMaxWidth()
                .padding(10.dp),
            singleLine = true
        )
        OutlinedTextField(
            value = precio,
            onValueChange = { precio = it },
            label = {
                Text("Precio")
            },
            modifier = Modifier
                .fillMaxWidth()
                .padding(10.dp),
            singleLine = true,
            keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
        )

        Button(
            onClick = {
                ConsultaCodigo(
                    codigo = codigo.toString(),
                    respuesta = {
                        if (it!=null) {
                            descripcion = it.descripcion
                            precio = it.precio.toString()
                            mensaje=""
                        } else {
                            mensaje = "No existe el código de producto ingresado"
                            descripcion=""
                            precio=""
                        }
                    },
                    contexto = contexto
                )
            },
            modifier = Modifier.padding(10.dp)
        ) {
            Text(text = "Consultar por código")
        }
        Button(
            onClick = {
                Modificar(
                    articulo = Articulo(codigo.toInt(),descripcion,precio.toFloat()),
                    respuesta = {
                        if (it)
                            mensaje="Los datos fueron modificados"
                        else
                            mensaje = "No existe el código de producto ingresado"
                    },
                    contexto = contexto
                )
            },
            modifier = Modifier.padding(10.dp)
        ) {
            Text(text = "Modificar")
        }
        Button(
            onClick = {
                Borrar(
                    codigo = codigo,
                    respuesta = {
                        if (it) {
                            mensaje = "Se eliminó el artículo"
                            codigo=""
                            descripcion=""
                            precio=""
                        } else
                            mensaje = "No existe el código de producto ingresado"
                    },
                    contexto = contexto
                )
            },
            modifier = Modifier.padding(10.dp)
        ) {
            Text(text = "Borrar artículo (ingresando código)")
        }
        Text(text = "$mensaje")
    }
}

data class Articulo(val codigo: Int, val descripcion: String, val precio: Float)


fun ConsultaCodigo(codigo: String, respuesta: (Articulo?) -> Unit, contexto: Context) {
    val requestQueue = Volley.newRequestQueue(contexto)
    val url = "https://scratchya.com.ar/videosandroidjava/volley/consultar.php?codigo=$codigo"
    val requerimiento = JsonArrayRequest(
        Request.Method.GET,
        url,
        null,
        { response ->
            if (response.length() == 1) {
                try {
                    val objeto = JSONObject(response[0].toString())
                    val articulo = Articulo(
                        objeto.getString("codigo").toInt(),
                        objeto.getString("descripcion"),
                        objeto.getString("precio").toFloat()
                    )
                    respuesta(articulo)
                } catch (e: JSONException) {
                }
            }
            else
                respuesta(null);
        }
    ) { error ->

    }
    requestQueue.add(requerimiento)
}

fun Modificar(articulo: Articulo, respuesta: (Boolean) -> Unit, contexto: Context) {
    val requestQueue = Volley.newRequestQueue(contexto)
    val url = "https://scratchya.com.ar/videosandroidjava/volley/modificar.php"
    val parametros = JSONObject()
    parametros.put("codigo", articulo.codigo.toString())
    parametros.put("descripcion", articulo.descripcion)
    parametros.put("precio", articulo.precio.toString())
    val requerimiento = JsonObjectRequest(
        Request.Method.POST,
        url,
        parametros,
        { response ->
            try {
                val resu = response["resultado"].toString()
                if (resu == "1")
                    respuesta(true)
                else
                    respuesta(false)
            } catch (e: JSONException) {
                respuesta(false)
            }
        }
    ) { error -> respuesta(false) }
    requestQueue.add(requerimiento)
}

fun Borrar(codigo: String, respuesta: (Boolean) -> Unit, contexto: Context) {
    val requestQueue = Volley.newRequestQueue(contexto)
    val url = "https://scratchya.com.ar/videosandroidjava/volley/borrar.php"
    val parametros = JSONObject()
    parametros.put("codigo", codigo)
    val requerimiento = JsonObjectRequest(
        Request.Method.POST,
        url,
        parametros,
        { response ->
            try {
                val resu = response["resultado"].toString()
                if (resu == "1")
                    respuesta(true)
                else
                    respuesta(false)
            } catch (e: JSONException) {
                respuesta(false)
            }
        }
    ) { error -> respuesta(false) }
    requestQueue.add(requerimiento)
}

El algoritmo de la consulta ya lo vimos en un concepto anterior, veamos que sucede con el algoritmo de la modificación.

En la función Button configuramos la función lambda para cuando se presiona el botón "modificar", creamos un objeto del data class Articulo con los datos ingresados en el formulario y llamamos a la función 'Modificar', dicha función mediante otra función lambda nos retorna un true o false si la modificación se efectuó en forma correcta:

        Button( 
            onClick = {
                Modificar(
                    articulo = Articulo(codigo.toInt(),descripcion,precio.toFloat()),
                    respuesta = {
                        if (it)
                            mensaje="Los datos fueron modificados"
                        else
                            mensaje = "No existe el código de producto ingresado"
                    },
                    contexto = contexto
                )
            },
            modifier = Modifier.padding(10.dp)
        ) {
            Text(text = "Modificar")
        }

La función Modificar crea un objeto de la clase JSONObject y configura las datos a enviar al servidor. Seguidamente creamos un objeto de la clase JsonObjectRequest, pasando por el método POST los parámetros y mediante una función lambda esperamos la respuesta del servidor. Según dicha respuesta llamamos a la función lambda de la otra función con un true o false:

fun Modificar(articulo: Articulo, respuesta: (Boolean) -> Unit, contexto: Context) {
    val requestQueue = Volley.newRequestQueue(contexto)
    val url = "https://scratchya.com.ar/videosandroidjava/volley/modificar.php"
    val parametros = JSONObject()
    parametros.put("codigo", articulo.codigo.toString())
    parametros.put("descripcion", articulo.descripcion)
    parametros.put("precio", articulo.precio.toString())
    val requerimiento = JsonObjectRequest(
        Request.Method.POST,
        url,
        parametros,
        { response ->
            try {
                val resu = response["resultado"].toString()
                if (resu == "1")
                    respuesta(true)
                else
                    respuesta(false)
            } catch (e: JSONException) {
                respuesta(false)
            }
        }
    ) { error -> respuesta(false) }
    requestQueue.add(requerimiento)
}

Finalmente para el borrado pasamos a la función Borrar el código de artículo:

        Button(
            onClick = {
                Borrar(
                    codigo = codigo,
                    respuesta = {
                        if (it) {
                            mensaje = "Se eliminó el artículo"
                            codigo=""
                            descripcion=""
                            precio=""
                        } else
                            mensaje = "No existe el código de producto ingresado"
                    },
                    contexto = contexto
                )
            },
            modifier = Modifier.padding(10.dp)
        ) {
            Text(text = "Borrar artículo (ingresando código)")
        }

En la función borrar pasamos por POST el código de artículo y esperamos la respuesta del servidor:

fun Borrar(codigo: String, respuesta: (Boolean) -> Unit, contexto: Context) {
    val requestQueue = Volley.newRequestQueue(contexto)
    val url = "https://scratchya.com.ar/videosandroidjava/volley/borrar.php"
    val parametros = JSONObject()
    parametros.put("codigo", codigo)
    val requerimiento = JsonObjectRequest(
        Request.Method.POST,
        url,
        parametros,
        { response ->
            try {
                val resu = response["resultado"].toString()
                if (resu == "1")
                    respuesta(true)
                else
                    respuesta(false)
            } catch (e: JSONException) {
                respuesta(false)
            }
        }
    ) { error -> respuesta(false) }
    requestQueue.add(requerimiento)
}

Este proyecto lo puede descargar en un zip desde este enlace: Compose25.zip