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.