PHP UD3 OP MODIFICAR
Sumario
Introducción
- Requerimentos previos:
- Ter creada a base de datos indicada nunha sección anterior
- Ter gardados os parámetros de conexión nun arquivo separado como foi indicado nunha sección anterior
- Estes exercicios están baseados nunha versión de PHP 8.1 ou superior onde as ordes PHP para manexar a base de datos lanzan expcecións. Se tedes unha vesión anterior, teredes que facer o indicado na sección de consultas e na sección de conexión.
- NOTA IMPORTANTE: Vimos en seccións anteriores que cando empregmos datos dun formulario, existen unhas técnicas para evitar posibles ataques. Todos os exercicios que veñen a continuación empregan ditas técnicas. Existe un tipo de ataque que non se ten en conta ao non ter chegado á sección de emprego de variables de sesións. En dita sección se explica o que temos que facer. É importante que vos acordedes de aplicar a técnica descrita cando vexades os contidos.
- A modificación se produce cando executamos a orde SQL UPDATE.
- Ao igual que no caso INSERT, se modificamos as claves primarias, farase o action recollido na creación das táboas.
- Normalmente non se debe modificar a clave primaria das táboas e o que modificaremos será algún dos atributos da táboa.
- A orde SQL UPDATE levará como parámetros os novos valores a modificar e enviarase a clave primaria para que poida buscar a fila concreta que vai modificarse.
- Ao igual que no caso da orde INSERT, podemos saber o número de rexistros afectados chamando á función mysqi_affected_rows.
- Neste exercicio dita función debería devolver sempre 1, pero podería aplicarse a outros exemplo no que queremos informar ao usuario de cantos rexistros foron afectados pola opción.
- Nota: Lembrar que nesta solución estamos a utilizar a chamada a multi_query pero se podería utilizar a chamada a query no caso de que o procedemento non devolva resultados (teña un select que queiramos recoller en PHP).
Caso Práctico: Modificación
Orientado a procedementos
- Imos implementar esta opción na páxina consultar_estados.php continuando có visto na sección de ALTA.
- Se estamos a utilizar procedementos almacenados, lembrar que en caso de erro '2014 - Commands out of sync; you can't run this command now', debemos engadir a seguinte liña despois de chamar a un procedemento almacenado:
if ($conex->multi_query($proc)){ $result = $conex->store_result(); if ($result && ($result->num_rows>0)){ while($fila=$result->fetch_assoc()){ // Procesamos cada fila } $result->free(); } while($conex->next_result() && $conex->store_result()); }
- O que temos que facer é:
- Definir no arquivo bd.php unha constante coa orde SQL para modificar o nome dun estado a partires dun id dado (clave primaria)
- Modificar a páxina consultar_estados.php e engadir a opción 'Editar' a cada un dos estados. Debemos ir a unha páxina editar_estado.php.
- O poderemos facer:
- Cun enlace href, enviando como parámetro o id do estado a modificar. Eu vou escoller esta opción, pero sodes libres de practicar e intentalo coa outra opción.
- Cun formulario e un campo oculto có id do estado a modificar.
- A páxina editar_estado.php terá un formulario cun action a 'procesar_editar_estado.php'. Dentro do formulario fará un include do arquivo campos_estado.php que xa foi feito na páxina de alta de estados.
- Tamén obterá os datos do estado a partires do id enviado polo enlace href ou polo formulario da páxina consultar_estados.php. Ditos datos serán gardados en variables e o nome será empregado pola páxina campos_estado.php.
- Modificaremos a páxina campos_estado.php para que teña en conta os datos obtidos da consulta á base de datos.
$e->getCode():* Crear a páxina procesar_borrar_estado.php que dará de baixa o estado e se se produce algún erro deberá de informar á páxina de consultar_estados.php de cal foi o erro. Ao igual que na alta, poderemos enviar dita información nun parámetro na URL de redirección ou veremos nas seguintes seccións como empregar variables de sesión.
- A xestión do erro en caso de querer borrar un rexistro relacionado non a imos facer.
- Lembrar que dito control poderíamos facelo no propio procedemento almacenado/páxina php, capturando a excepción ou comprobando cun select previo se existe o rexistro relacionado.
Posible solución Caso Práctico: Modificación
Arquivo: bd.php
define('CONSULTAR_ESTADO_POR_ID',"SELECT id,descripcion FROM estados WHERE id=%d"); define('ACTUALIZAR_ESTADO',"UPDATE estados set descripcion='%s' WHERE id=%d");
Arquivo: consultar_estados.php
<?php /* Lembrar quitar isto ao probar o scripts sen erros */ ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); require ('/var/www/confBD/bd.php'); try { $conex = new mysqli(SERVIDOR, USUARIO, PASSWORD,BD); $conex->set_charset('utf8mb4'); ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Xestión de estados</title> </head> <body> <form action="alta_estado.php" method="post"> <button type="submit">Novo Estado</button> </form> <table border="1" style="margin-top:20px"> <tr> <th>Estado</th> <th>Accións</th> </tr> <?php $resultado = $conex->query(CONSULTAR_ESTADOS); while($fila = $resultado->fetch_assoc()) { $nombre = htmlentities($fila['descripcion']); echo "<tr>"; echo "<td>$nombre</td>"; echo "<td>"; printf("<td><a href='editar_estado.php?id=%d'>Editar</a>",urlencode($fila['id'])); printf("<form action='procesar_borrar_estado.php' method='post'>"); printf("<input type='hidden' name='id' value='%s' />",htmlentities($fila['id'])); echo '<input onclick="return confirm(\'Estás seguro de que queres borrar este estado?\');" type="submit" value="Borrar" name="btnBorrarEstado" style="background:none;border:none;padding:0;maring:0;color: blue; text-decoration:underline; cursor:pointer; font-size:inherit" />'; echo '</form>'; echo "</td>"; echo "</tr>"; } $resultado->free(); ?> </table> <div> <?php if(isset($_GET['id'])) printf("<h3>Estado con id %s dado de alta correctamente.</h3>",htmlentities($_GET['id'])); if(isset($_GET['msg'])) printf("<h4>%s</h3>",htmlentities($_GET['msg'])); if(isset($_GET['error'])) printf("<h2>Houbo un erro: %s</h2>",htmlentities($_GET['error'])); ?> </div> </body> </html> <?php } // Pecha o try da parte superior da páxina catch (mysqli_sql_exception $e) { die("Erro mysql: " . $e->getMessage()); } catch (Expcetion $e) { die("Erro xeral: " . $e->getMessage()); } finally{ if(!empty($conex)){ $conex->close(); } } ?>
Arquivo: campos_estado.php
<label for="nombre">Descricción:</label> <input type="text" maxlength="100" id="descripcion" name="descripcion" required value="<?php echo isset($descripcion) ? htmlentities($descripcion) : ''; ?>"><br>
Arquivo: editar_estado.php
<?php /* Lembrar quitar isto ao probar o scripts sen erros */ ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); require ('/var/www/confBD/bd.php'); $conex = new mysqli(SERVIDOR, USUARIO, PASSWORD, BD); $conex->set_charset('utf8mb4'); $erros = ''; ?> <?php $consulta = sprintf(CONSULTAR_ESTADO_POR_ID,$conex->real_escape_string($_GET['id'])); $result=$conex->query($consulta); // Ven na URL da paxina listar_pistas.php if ($result && $result->num_rows>0){ $row = mysqli_fetch_assoc($result); // Os datos serán empregados polos inputs do formulario $descripcion = htmlentities($row['descripcion']); $id = htmlentities($row['id']); //O id non se amosa ao usuario $result->free(); } else { $erros = "Non se atopan os datos da pista!!!!"; } ?> <!DOCTYPE html> <html> <head> <title>Modificar Estado</title> </head> <body> <h3>EDITAR ESTADO</h3> <form method="POST" action="procesar_editar_estado.php"> <?php include 'campos_estado.php'; ?> <div> <input type="hidden" name="id_orixinal" value="<?php echo $id; ?>"> <input type="submit" value="Modificar" name="btnModificarEstado"> </div> </form> <?php if(!empty($erros)){ printf("<h3>$erros</h3>"); } ?> </body> </html>
Arquivo: procesar_editar_estado.php
<?php /* Lembrar quitar isto ao probar o scripts sen erros */ ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); require ('/var/www/confBD/bd.php'); $conex = new mysqli(SERVIDOR, USUARIO, PASSWORD, BD); $conex->set_charset('utf8mb4'); $error = ''; $msg = ''; if (isset($_POST['btnModificarEstado'])) { try { // Lóxica para dar de alta unha nova pista $desc_novo = $conex->real_escape_string(strtoupper($_POST['descripcion'])); if(strlen($desc_novo)<4 || strlen($desc_novo)>100){ $error = sprintf("error=%s", urlencode("O nome do estado non pode ter menos de 4 ou 100 caracteres")); } else{ $id = $conex->real_escape_string($_POST['id_orixinal']); // Ven nun campo oculto do form $consulta = sprintf(ACTUALIZAR_ESTADO,$desc_novo, $id); $conex->query($consulta); $msg = sprintf("msg=%s", urlencode("Estado actualizado correctamente.")); } } catch (mysqli_sql_exception $e) { $error=sprintf("error='Erro mysql: %s'",urlencode($e->getMessage())); } catch (Expcetion $e) { $error=sprintf("error='Erro xeral: %s'",urlencode($e->getMessage())); } finally{ if(!empty($conex)){ $conex->close(); } } } $url = sprintf("consultar_estados.php?%s&%s",$msg,$error); header("Location:" . $url); ?>
Empregando PDO
- Imos implementar esta opción na páxina consultar_estados.php continuando có visto na sección de ALTA.
- O que temos que facer é:
- Definir no arquivo bd.php unha constante coa orde SQL para modificar o nome dun estado a partires dun id dado (clave primaria)
- Modificar a páxina consultar_estados.php e engadir a opción 'Editar' a cada un dos estados. Debemos ir a unha páxina editar_estado.php.
- O poderemos facer:
- Cun enlace href, enviando como parámetro o id do estado a modificar. Eu vou escoller esta opción, pero sodes libres de practicar e intentalo coa outra opción.
- Cun formulario e un campo oculto có id do estado a modificar.
- A páxina editar_estado.php terá un formulario cun action a 'procesar_editar_estado.php'. Dentro do formulario fará un include do arquivo campos_estado.php que xa foi feito na páxina de alta de estados.
- Tamén obterá os datos do estado a partires do id enviado polo enlace href ou polo formulario da páxina consultar_estados.php. Ditos datos serán gardados en variables e o nome será empregado pola páxina campos_estado.php.
- Modificaremos a páxina campos_estado.php para que teña en conta os datos obtidos da consulta á base de datos.
$e->getCode():* Crear a páxina procesar_borrar_estado.php que dará de baixa o estado e se se produce algún erro deberá de informar á páxina de consultar_estados.php de cal foi o erro. Ao igual que na alta, poderemos enviar dita información nun parámetro na URL de redirección ou veremos nas seguintes seccións como empregar variables de sesión.
- A xestión do erro en caso de querer borrar un rexistro relacionado non a imos facer.
- Lembrar que dito control poderíamos facelo no propio procedemento almacenado/páxina php, capturando a excepción ou comprobando cun select previo se existe o rexistro relacionado.
Posible solución Caso Práctico: Modificación
Arquivo: bd.php
define('CONSULTAR_ESTADO_POR_ID',"SELECT id,descripcion FROM estados WHERE id=:id_estado"); define('ACTUALIZAR_ESTADO',"UPDATE estados set descripcion=:descripcion WHERE id=:id_estado");
Arquivo: consultar_estados.php
<?php /* Lembrar quitar isto ao probar o scripts sen erros */ ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); require('/var/www/confBD/bd.php'); try { $dsn = sprintf("mysql:host=%s;dbname=%s;charset=utf8mb4", SERVIDOR, BD); $conex = new PDO($dsn, USUARIO, PASSWORD); $conex->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $erro = ""; // Variable que a usaremos para amosar os posibles erros controlados por nos que poidan darse. ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="es" xml:lang="es"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Xestión de estados</title> </head> <body> <form action="alta_estado.php" method="post"> <button type="submit">Novo Estado</button> </form> <table border="1" style="margin-top:20px"> <tr> <th>Estado</th> <th>Accións</th> </tr> <?php $result=$conex->query(CONSULTAR_ESTADOS); // Temos que poñer prepare if ($result && $result->rowCount() > 0) { while ($row = $result->fetch(PDO::FETCH_ASSOC)) { $nombre = htmlentities($row['descripcion']); echo "<tr>"; echo "<td>$nombre</td>"; echo "<td>"; printf("<a href='editar_estado.php?id=%s'>Editar</a>",urlencode($row['id'])); echo("<form action='procesar_borrar_estado.php' method='post'>"); printf("<input type='hidden' name='id' value='%s' />",htmlentities($row['id'])); echo '<input onclick="return confirm(\'Estás seguro de que queres borrar este estado?\');" type="submit" value="Borrar" name="btnBorrarEstado" style="background:none;border:none;padding:0;margin:0;color: blue; text-decoration:underline; cursor:pointer; font-size:inherit" />'; echo '</form>'; echo "</td>"; echo "</tr>"; } } $result = null; ?> </table> <div> <?php if(isset($_GET['id'])) printf("<h3>Estado con id %s dado de alta correctamente.</h3>",htmlentities($_GET['id'])); if(isset($_GET['msg'])) printf("<h4>%s</h3>",htmlentities($_GET['msg'])); if(isset($_GET['error'])) printf("<h2>Houbo un erro: %s</h2>",htmlentities($_GET['error'])); ?> </div> </body> </html> <?php } // Pecha o try do principio da páxina catch (PDOException $e) { die("Erro PDO: " . $e->getMessage()); } catch (Exception $e) { die("Erro xeral: " . $e->getMessage()); } finally { $conex = null; // Pechar a conexión } ?>
Arquivo: campos_estado.php
<label for="nombre">Descricción:</label> <input type="text" maxlength="100" id="descripcion" name="descripcion" required value="<?php echo isset($descripcion) ? htmlentities($descripcion) : ''; ?>">
Arquivo: editar_estado.php
<?php /* Lembrar quitar isto ao probar o scripts sen erros */ ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); require('/var/www/confBD/bd.php'); try { $dsn = sprintf("mysql:host=%s;dbname=%s;charset=utf8mb4", SERVIDOR, BD); $conex = new PDO($dsn, USUARIO, PASSWORD); $conex->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $erro = ""; // Variable que a usaremos para amosar os posibles erros controlados por nos que poidan darse. $stmt = $conex->prepare(CONSULTAR_ESTADO_POR_ID); $stmt->bindValue(":id_estado", $_GET['id'], PDO::PARAM_INT); $result = $stmt->execute(); if ($result) { $row = $stmt->fetch(PDO::FETCH_ASSOC); $id = $row['id']; $descripcion = htmlentities($row['descripcion']); $stmt = null; } else { $erros = "Non se atopan os datos da pista!!!!"; } ?> <!DOCTYPE html> <html> <head> <title>Modificar Estado</title> </head> <body> <h3>EDITAR ESTADO</h3> <form method="POST" action="procesar_editar_estado.php"> <?php include 'campos_estado.php'; ?> <div> <input type="hidden" name="id_orixinal" value="<?php echo $id; ?>"> <input type="submit" value="Modificar" name="btnModificarEstado"> </div> </form> <?php if(!empty($erros)){ printf("<h3>$erros</h3>"); } ?> </body> </html> <?php } // Pecha o try do principio da páxina catch (PDOException $e) { die("Erro PDO: " . $e->getMessage()); } catch (Exception $e) { die("Erro xeral: " . $e->getMessage()); } finally { $conex = null; // Pechar a conexión } ?>
Arquivo: procesar_editar_estado.php
<?php /* Lembrar quitar isto ao probar o scripts sen erros */ ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); require('/var/www/confBD/bd.php'); try { $dsn = sprintf("mysql:host=%s;dbname=%s;charset=utf8mb4", SERVIDOR, BD); $conex = new PDO($dsn, USUARIO, PASSWORD); $conex->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $error = ''; $msg = ''; if (isset($_POST['btnModificarEstado'])) { $desc_novo = strtoupper($_POST['descripcion']); if (strlen($desc_novo) < 4 || strlen($desc_novo) > 100) { $error = sprintf("error=%s", urlencode("O nome do estado non pode ter menos de 4 ou 100 caracteres")); } else { $stmt = $conex->prepare(ACTUALIZAR_ESTADO); // Temos que poñer prepare $stmt->bindValue(':id_estado', $_POST['id_orixinal'], PDO::PARAM_INT); $stmt->bindValue(':descripcion', $desc_novo, PDO::PARAM_STR); $result = $stmt->execute(); if ($result) { $msg = sprintf("msg=%s", urlencode("Estado actualizado correctamente.")); } else { $error = sprintf("error=$s", urlencode("Houbo un problema na actualización.")); } } } } catch (PDOException $e) { $error = sprintf("error=PDO:%s", urlencode($e->getMessage())); } catch (Exception $e) { $error = sprintf("error=ERRO XERAL:%s", urlencode($e->getMessage())); } finally { $conex = null; // Pechar a conexión } $url = sprintf("consultar_estados.php?%s&%s", $msg, $error); header("Location:" . $url);
-- Ángel D. Fernández González -- (2025).