Llamar a procedimientos almacenados en PHP

Una entrada que llevaba mucho tiempo en el TODO list, sobre trabajar con procedimientos almacenados con PHP.

Para empezar vamos a plantear un escenario en el que tenemos tres procedimientos almacenados en MySQL (dos selects y un insert) contra una tabla llamada productos, que tiene tres campos (id como clave primaria, descripcion con una descripción del producto, precio con un valor numérico decimal).

El SP de la primera select, que nos devuelve la descripción y el precio del producto según su id, sería este:

CREATE PROCEDURE getProducto(IN id_val INT) 
  BEGIN 
    SELECT descripcion, precio from productos WHERE id = id_val; 
  END;

El SP de la segunda select, que nos devuelve la descripción y el precio todos los productos, por lo que no recibe parámetros:

CREATE PROCEDURE getAllProductos() 
  BEGIN 
    SELECT descripcion, precio from productos ; 
  END;

Y el sp del insert sería este, al que le pasamos los tres valores a meter:

CREATE PROCEDURE addProducto(IN id_val INT,descripcion_in varchar(50), precio_in float) 
  BEGIN 
    INSERT INTO productos VALUES (id_val, descripcion_in, precio_in);
  END;

Bueno, ya tenemos nuestro escenario, entonces ¿cómo hacemos para llamar a estos procedimientos desde nuestro código php? Pues simplemente lanzamos una consulta en la que llamamos a la instrucción CALL de MySQL para que ejecute el procedimiento almacenado que le digamos con los parámetros que tocan.

/*Empezamos con el procedimiento de inserción*/
/*Lo primero es crear un objeto mysqli*/
$mysqli = new mysqli("servidor", "usuario", "pass", "db");

/*Y llamamos al procedimiento para hacer la inserción*/
/*Si falla imprimimos el error*/
if (!$mysqli->query("CALL addProducto(1001, 'Nueces de Macadamia', 12.23)")) {
    echo "Falló la llamada: (" . $mysqli->errno . ") " . $mysqli->error;
}

La propia función query del objeto mysqli nos devolverá TRUE si no viene con errores y FALSE si pasa algo inesperado.

¿Y qué pasa con la recuperación de datos? Ahí nos vamos a un terreno más pantanoso. Para una consulta con un solo resultado no es problema:

/*Empezamos con el procedimiento de recuperación de una fila*/
/*Lo primero es crear un objeto mysqli*/
$mysqli = new mysqli("servidor", "usuario", "pass", "db");

/*Y llamamos al procedimiento para recoger los datos*/
/*Si falla imprimimos el error*/
if (!($res = $mysqli->query("CALL getProducto(1001)"))) {
    echo "Falló la llamada: (" . $mysqli->errno . ") " . $mysqli->error;
}

/*E imprimimos el resultado para ver que el ejemplo ha funcionado*/
var_dump($res->fetch_assoc());

Pero claro, en este caso podemos hacerlo así porque esperamos un solo resultado, pero si esperamos varias filas la cosa es distinta ya que mysqli->query() devuelve sólo el primer conjunto de resultados. Tenemos dos alternativas para esta situación: mysqli_real_query() o mysqli_multi_query(). En el ejemplo usaremos la segunda, y veremos también la forma de recorrer este resultado:

/*Empezamos con el procedimiento de recuperación de una fila*/
/*Lo primero es crear un objeto mysqli*/
$mysqli = new mysqli("servidor", "usuario", "pass", "db");

/*Y llamamos al procedimiento para recoger los datos*/
/*Si falla imprimimos el error*/
if (!$mysqli->multi_query("CALL getAllProductos()")) {
    echo "Falló la llamada: (" . $mysqli->errno . ") " . $mysqli->error;
}

/*Ahora con este bucle recogemos los resultados y los recorremos*/
do {
    /*En el if recogemos una fila de la tabla*/
    if ($res = $mysqli->store_result()) { 
        /*Imprimimos el resultado de la fila y debajo un salto de línea*/
        var_dump($res->fetch_all());
        printf("\n");
        /*La llamada a free() no es obligatoria, pero si recomendable para aligerar memoria y para evitar problemas si después hacemos una llamada a otro procedimiento*/
        $res->free();
    } else {
        if ($mysqli->errno) {
            echo "Store failed: (" . $mysqli->errno . ") " . $mysqli->error;
        }
    }
} while ($mysqli->more_results() && $mysqli->next_result());
/*El bucle se ejecuta mientras haya más resultados y se pueda saltar al siguiente*/

Añadiré a esto que también es posible utilizar las llamadas a procedimientos almacenados usando sentencias preparadas, procediendo del modo habitual a la hora de pasar los parámetros.

Anuncio publicitario

3 comentarios en “Llamar a procedimientos almacenados en PHP

  1. Miguel Angel Dawe

    Hola interesante tu articulo, tomando como base tu ejemplo, estoy intentando implementar en una aplicación de login de usuarios por medio de P.A. en Mysql y PHP pero me bota el siguiente error:
    Fatal error: Call to a member function fetch_assoc() on a non-object in C:\wamp\www\app\includes\login1.php on line 27
    EL P.A. esta de la siguiente forma:
    DROP PROCEDURE IF EXISTS validarUsuario;
    CREATE PROCEDURE validarUsuario(IN p_email VARCHAR (200), IN p_password VARCHAR (200))
    BEGIN
    SELECT * FROM usuarios WHERE userEmail=p_email AND userClave=p_password;
    END
    La conexión y llamada al PA desde PHP la hago como tu la publicaste:
    //Recibo los parametros por POST
    $usuario= htmlspecialchars($_POST[‘usuario’]);
    $p_email = htmlspecialchars($_POST[’email’]);
    $p_password = htmlspecialchars($_POST[‘clave’]);
    /*Empezamos con el procedimiento de recuperación de una fila*/
    /*Lo primero es crear un objeto mysqli*/
    $mysqli = new mysqli(“servidor”, “usuario”, “pass”, “db”‘);
    /*Y llamamos al procedimiento para recoger los datos*/
    /*Si falla imprimimos el error*/
    if (!($res = $mysqli->query(“CALL validarUsuario($p_email,$p_password)”))) {
    echo ‘Falló la llamada: (‘ . $mysqli->errno . ‘) ‘ . $mysqli->error;
    }
    /*E imprimimos el resultado para ver que el ejemplo ha funcionado*/
    var_dump($res->fetch_assoc());
    ¿Seria posible que me ayudaras a resolver y entender un poco más como usar los P.A.?
    De antemano agradezco tu apoyo.

  2. Israel Rodriguez

    muy útil amigo, gracias, solo que al final para recorrer, ese código me imprimía toda la matriz de la consulta, pero con un foreach lo pude solucionar

Deja una respuesta

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.