Ir al contenido principal

Modificaciones

Qué es una modificación

Una modificación cambia el valor de algunas columnas de filas que ya existen. No agrega filas ni las elimina: las mismas filas siguen ahí, con algún dato distinto.

Es la operación más frecuente de un sistema real, y bastante más de lo que parece. Un socio se carga una sola vez, pero su teléfono, su correo y su dirección cambian muchas veces a lo largo de los años. Cada uno de esos cambios es una modificación, y en un sistema en uso pasan decenas por día.

Es también la más peligrosa de las cuatro, por una razón que no tiene nada que ver con la dificultad de programarla. Un alta mal hecha deja una fila de más, que se ve en el listado y se borra. Una modificación mal hecha no se ve: cambia un dato en silencio y puede pasar meses hasta que alguien note que el saldo está mal, que un correo quedó con el de otra persona o que se actualizaron los cuarenta socios en lugar de uno. El error de una modificación es invisible por definición, y por eso exige más cuidado que las otras tres.

La sentencia UPDATE

La sentencia que modifica registros existentes es UPDATE, y está desarrollada en la Unidad 3: sus tres partes, el peligro del WHERE olvidado y el método de verificar la condición con un SELECT antes de ejecutar.

UPDATE socios
SET    correo   = '[email protected]',
       telefono = '221-5551234'
WHERE  id = 47;

De todo lo que se vio allá hay una advertencia que en esta unidad pesa más, y conviene repetirla porque cambia de naturaleza.

Por qué se modifica por id y no por nombre

El WHERE tiene que identificar las filas sin ninguna duda, y ese es un requisito más estricto de lo que parece. Comparar por un dato del contenido no lo cumple:

-- Peligroso
UPDATE socios SET telefono = '221-5551234' WHERE nombre = 'Ana Pérez';

Esa condición tiene tres problemas, y los tres se dan en la práctica:

  • Puede haber varios. Dos personas pueden llamarse igual. La sentencia modificaría los dos.
  • Puede haber ninguno, si el nombre está escrito distinto —con o sin acento, con un espacio de más, con el apellido primero—. Y como no hay error, parece que funcionó.
  • El nombre puede cambiar. Es justamente uno de los datos que se modifican. Usarlo como referencia es frágil por definición.

El id no tiene ninguno de esos problemas: es único —lo garantiza PRIMARY KEY—, nunca se repite y no cambia nunca. Existe exactamente para esto. Por eso el id viaja desde el listado hasta el formulario de edición, y es lo que identifica qué fila se está modificando.

El código PHP

La modificación necesita un dato más que el alta: cuál es la fila. Por lo demás, la estructura es la misma —recibir, validar, ejecutar—:

<?php

$bd = new PDO('mysql:host=localhost;dbname=biblioteca;charset=utf8mb4', 'usuario', 'clave');
$bd->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// El id se convierte a número entero. Así no puede traer nada más que un número.
$id     = (int) ($_POST['id'] ?? 0);
$nombre = trim($_POST['nombre'] ?? '');
$correo = trim($_POST['correo'] ?? '');

$errores = [];

if ($id <= 0) {
    $errores[] = 'No se indicó qué socio modificar.';
}
if ($nombre === '') {
    $errores[] = 'El nombre es obligatorio.';
}
if ($correo !== '' && ! filter_var($correo, FILTER_VALIDATE_EMAIL)) {
    $errores[] = 'El correo no tiene un formato válido.';
}

if (! $errores) {

    $sentencia = $bd->prepare(
        'UPDATE socios SET nombre = :nombre, correo = :correo WHERE id = :id'
    );

    $sentencia->execute([
        ':nombre' => $nombre,
        ':correo' => $correo,
        ':id'     => $id,
    ]);

    header('Location: listado.php?modificado=ok');
    exit;
}

Las diferencias con el alta, que son las que hay que entender:

  • La consulta es UPDATE ... SET ... WHERE en lugar de INSERT. Y el WHERE está, siempre.
  • El id se convierte con (int). Es una precaución barata: obliga a que el valor sea un número entero, así que aunque alguien manipule el formulario, por ahí no puede entrar nada más. Cuando un dato tiene que ser un número, convertirlo es la validación más simple y más eficaz que existe.
  • El id también se valida: si llega en cero o negativo, no se ejecuta nada. Un WHERE id = 0 no modificaría ninguna fila, pero es mejor no mandar la consulta que confiar en que no encuentre nada.

El UPDATE también usa marcadores para sus valores. La regla y su explicación están en Inyección SQL y consultas preparadas.

Qué devuelve rowCount() en un UPDATE

Acá hay una sutileza que confunde a casi todos, y que conviene conocer antes de chocarla. En un UPDATE, rowCount() devuelve la cantidad de filas que efectivamente cambiaron, y eso no es lo mismo que la cantidad de filas que coincidieron con el WHERE.

La consecuencia: si el usuario abre el formulario de edición, no cambia nada y aprieta «Guardar», los valores que se mandan son iguales a los que ya estaban. La base de datos actualiza la fila con los mismos datos, ningún valor cambia, y rowCount() devuelve 0.

rowCount() Qué quiere decir
1Se modificó una fila. Todo salió como se esperaba.
0O bien los valores eran los mismos y no hubo nada que cambiar, o bien no existe ninguna fila con ese id.
Más de 1Alerta. La condición del WHERE no identificaba una sola fila. Hay que revisarla antes de seguir.

El caso de más de 1 es el que hay que vigilar: significa que la modificación tocó filas que no debía, y que el WHERE está mal armado. Y el 0 es el que engaña: no se puede usar rowCount() para decidir si el socio existe, porque devuelve lo mismo en los dos casos. Si hace falta distinguirlos, la forma correcta es consultar primero con un SELECT, o no depender de ese número para el mensaje que se le muestra al usuario. Lo importante es tener claro que 0 no es un error: es «no hubo cambios», que muchas veces es exactamente lo que correspondía.

El formulario que ya viene lleno

Una modificación no empieza en el formulario de edición: empieza en el listado, con el botón «Editar» en la fila del dato. Ese botón tiene que llevar el id, y con ese id el programa hace dos pasos distintos:

  1. Mostrar el formulario con los datos actuales. Para eso hay que leerlos de la base con un SELECT, y ponerlos como valor inicial de cada campo. Si el formulario llegara vacío, el usuario no vería qué está cambiando.
  2. Procesar el envío. Ahí sí se ejecuta el UPDATE con los valores nuevos.

El id tiene que viajar entre los dos pasos, y lo hace en un campo oculto:

<form action="modificar.php" method="post">
    <input type="hidden" name="id" value="<?= $socio['id'] ?>">

    <label for="nombre">Nombre y apellido</label>
    <input type="text" id="nombre" name="nombre" value="<?= htmlspecialchars($socio['nombre']) ?>">

    <button type="submit">Guardar cambios</button>
</form>

El campo oculto no se ve, pero el usuario puede modificarlo: no hay que confiar en él más que en cualquier otro dato que llega. Un campo oculto no es una medida de seguridad, solo una comodidad para no mostrar el número. Y el htmlspecialchars() que envuelve al nombre tiene su propia razón, que se ve en detalle en el listado: si el dato guardado contiene comillas o símbolos de menor y mayor, escribirlo tal cual dentro del atributo value rompería el HTML de la página.

El formulario de arriba es el de los socios. Así se ve, en cambio, el del CRUD de la tienda, con los datos del producto ya traídos de la base y el botón que dispara la actualización:

El formulario Modificar Producto del CRUD: el menú arriba, y los campos Artículo con Aceite, Descripción con Producto de girasol y Unidades Disponibles con 20, ya llenos, más el botón Actualizar
El formulario de modificación del CRUD de la tienda, con los datos leídos de la base. El mismo archivo hace las dos cosas: si la página se pide, muestra el formulario con esos datos; si se envía, actualiza la fila y vuelve al listado.

Los errores típicos de la modificación

Qué falta Qué pasa
El WHERE Se modifican todas las filas de la tabla. Sin error y sin aviso.
Un WHERE por un dato del contenido Puede tocar varias filas o ninguna, según cómo esté escrito el dato.
La validación en el servidor Se guarda lo que llegue: un correo inventado, un nombre vacío.
La conversión del id a número El id llega como texto y puede traer cualquier cosa desde el formulario.
El formulario con los valores actuales El usuario edita a ciegas y borra sin querer lo que ya estaba bien.
La redirección después de guardar Recargar vuelve a enviar la modificación. Suele ser inofensivo, pero no siempre.

El penúltimo es el que menos se tiene en cuenta y el que más afecta al usuario. Un formulario de edición que llega vacío obliga a reescribir todo lo que ya estaba bien, y en ese reescribir se pierden datos que nadie iba a cambiar. La regla es sencilla: el formulario de edición tiene que mostrar lo que hay, y el usuario cambia solo lo que necesita.

Resumen

  • Una modificación cambia valores de filas que ya existen. No agrega ni quita filas.
  • Es la operación más frecuente y la más peligrosa, porque su error no se ve.
  • UPDATE tabla SET columna = valor WHERE condición: las tres partes son obligatorias.
  • Sin WHERE se modifican todas las filas, sin error y sin vuelta atrás.
  • El hábito que lo evita: probar la condición con un SELECT antes.
  • Se modifica por id, nunca por un dato del contenido: el id es único y no cambia.
  • El id se convierte con (int) y se valida antes de usarlo.
  • rowCount() devuelve las filas que cambiaron, no las que coincidieron: 0 puede significar «no hubo cambios» y no es un error.
  • Más de 1 fila afectada es una alerta: el WHERE está mal.
  • El formulario de edición llega con los datos actuales, y el id viaja en un campo oculto.
  • Un campo oculto se puede modificar: no es una medida de seguridad.

Ver el listado