PHP UD3 OP BAIXA

De MediaWiki
Ir a la navegación Ir a la búsqueda

Introducción

  • Requerimentos previos:


  • 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.




  • Podemos eliminar unha fila ou un conxunto de filas en base a uns criterios (parte WHERE da cláusula DELETE)
  • Ao igual que fixemos anteriormente, podemos borrar directamente en PHP facendo uso da orde DELETE ou chamar a un procedemento almacenado, enviando os datos necesarios e executando a sentenza.


  • IMPORTANTE: Temos que ter en conta que nas bases de datos relacionais, dar de baixa un rexistro pode implicar borrar rexistros relacionados.
Cando creamos as táboas indicamos as relacións entre elas por medio de claves foráneas, nas que indicamos como queremos que se comporten cando se produce un borrado ou unha modificación. Máis información neste enlace.


Por exemplo, na táboa LIBROS_AUTORES:
1 CREATE TABLE `LIBROS_AUTORES` (
2   `libro_id` int(11) NOT NULL,
3   `autor_id` int(11) NOT NULL,
4   PRIMARY KEY (`libro_id`,`autor_id`),
5   KEY `fk_LIBROS_AUTORES_AUTORES_idx` (`autor_id`),
6   CONSTRAINT `fk_LIBROS_AUTORES_AUTORES` FOREIGN KEY (`autor_id`) REFERENCES `AUTORES` (`id_autor`) ON DELETE NO ACTION ON UPDATE CASCADE,
7   CONSTRAINT `fk_LIBROS_AUTORES_LIBROS` FOREIGN KEY (`libro_id`) REFERENCES `LIBROS` (`id_libro`) ON DELETE NO ACTION ON UPDATE CASCADE
8 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_spanish2_ci;
No exemplo estamos a indicar que en caso de borrar unha fila de táboa AUTORES, non se vai permitir se existen filas na táboa LIBROS_AUTORES.
Se actualizamos a clave primaria da táboa AUTORES, actualizará a clave na táboa AUTORES_LIBROS (parte ON UPDATE CASCADE).


  • Polo tanto, será tarefa do programador comprobar se os datos están sendo utilizados noutras táboas e avisar en caso de que o usuario intente borralos ou ben permitir borrados en cascada ou ben capturar o erro que dará o mysql en caso de intentar borrar datos relacionados e informar ao usuario do problema.


Caso Práctico: Baixa

  • 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:
 1                     if ($conex->multi_query($proc)){
 2                         $result = $conex->store_result();
 3                         if ($result && ($result->num_rows>0)){
 4                             while($fila=$result->fetch_assoc()){
 5                                 // Procesamos cada fila
 6                             }
 7                             $result->free();
 8                         }
 9                         while($conex->next_result() && $conex->store_result());
10                     }


  • O que temos que facer é:
  • Definir no arquivo bd.php unha constante coa orde SQL para dar de baixa a un estado.
  • Modificar a páxina consultar_estados.php e engadir a opción 'Eliminar' a cada un dos estados. Debemos ir a unha páxina procesar_borrar_estado.php.
O poderemos facer:
  • Cun enlace href, enviando como parámetro o id do estado a borrar.
  • Cun formulario e un campo oculto có id do estado a borrar. Eu vou escoller esta opción, pero sodes libres de practicar e intentalo coa primimeira.
  • 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 a imos facer na páxina PHP comprobando que o valor numérico do erro sexa o 1451, que se corresponde co erro 'Cannot delete or update a parent row: a foreign key constraint fails'.
Lembrar que dito control poderíamos facelo no propio procedemento almacenado capturando a excepción ou comprobando cun select previo se existe o rexistro relacionado.


  • Nota:
  • Lembrar que nesta solución estamos a utilizar a chamada a multi_query para obter o número de rexistros borrados, pero se podería utilizar a chamada a query no caso de que o procedemento non devolva resultados ou non nos interese obter o número de rexistros borrados.
    Nesta solución non comprobamos se xa existen datos relacionados con ese estado antes de borralos.
  • Usaremos como base o código feito na solución consultar_estados.php feito anteriormente na sección de alta.





Posible solución Caso Práctico: Baixa

Arquivo bd.php. Engadimos o seguinte ao que xa tiñamos.

define('BORRAR_ESTADO','DELETE FROM estados 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("<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 %d 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 procesar_borrar_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');

$msg='';
$error = '';
if (isset($_POST['btnBorrarEstado'])) {
  try {
    $conex = new mysqli(SERVIDOR, USUARIO, PASSWORD, BD);
    $conex->set_charset('utf8mb4');

    // Lóxica para dar de alta unha nova pista
    $id = $conex->real_escape_string($_POST['id']);
    $consulta = sprintf(BORRAR_ESTADO,$id);
    if ($conex->multi_query($consulta)){
      $msg = sprintf("msg=Num. rexistros borrados:%d", $conex->affected_rows);
      while($conex->next_result() && $conex->store_result());
    }

  }   // Pecha o try da parte superior da páxina. Lembrar que aos usuarios non se lle deben amosar os erros. Isto está posto a nivel informativo para os programadores
  catch (mysqli_sql_exception $e) {
      $error=sprintf("error='Erro mysql: %s'",htmlentities($e->getMessage()));
  }
  catch (Expcetion $e) {
      $error=sprintf("error='Erro xeral: %s'",htmlentities($e->getMessage()));
  }
  finally{
      if(!empty($conex)){
          $conex->close();
      }
  }
  
    
}
$url = sprintf("consultar_estados.php?%s&%s",$msg,$error);
header("Location:" . $url);






-- Ángel D. Fernández González -- (2023).