UPDATE en PHP
Actualizar registros existentes en MySQL desde PHP es una operación que ejecutarás frecuentemente en cualquier aplicación web, desde actualizar perfiles de usuario hasta modificar inventarios y precios. PDO facilita esta tarea con sentencias preparadas y control de transacciones. En este artículo aprenderás a ejecutar actualizaciones parametrizadas, verificar si se modificaron datos y construir consultas dinámicas de forma segura.
Requisitos previos
Necesitas una conexión PDO configurada y la tabla productos con datos de prueba. Consulta los artículos anteriores si necesitas configurar el entorno.
Código completo
Este ejemplo actualiza el precio y stock de un producto:
<?php
$pdo = new PDO(
'mysql:host=localhost;dbname=tienda;charset=utf8mb4',
'root', 'tu_contraseña',
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]
);
$stmt = $pdo->prepare('UPDATE productos SET precio = ?, stock = ? WHERE id = ?');
$stmt->execute([13499.99, 30, 1]);
echo "Filas afectadas: " . $stmt->rowCount() . "\n";Salida esperada:
Filas afectadas: 1
Explicación paso a paso
El método rowCount() en un UPDATE devuelve el número de filas que realmente cambiaron. Si actualizas un campo con el mismo valor que ya tenía, rowCount() devolverá 0 porque ninguna fila cambió realmente. Esto es diferente de la cantidad de filas que coinciden con el WHERE.
Si necesitas saber cuántas filas coincidieron con la condición (independientemente de si cambiaron), puedes configurar la opción PDO::MYSQL_ATTR_FOUND_ROWS:
<?php
$pdo = new PDO(
'mysql:host=localhost;dbname=tienda;charset=utf8mb4',
'root', 'tu_contraseña',
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::MYSQL_ATTR_FOUND_ROWS => true // rowCount devuelve filas encontradas
]
);Verificar si la actualización tuvo efecto
<?php
function actualizarPrecio(PDO $pdo, int $productoId, float $nuevoPrecio): bool {
$stmt = $pdo->prepare('UPDATE productos SET precio = ? WHERE id = ?');
$stmt->execute([$nuevoPrecio, $productoId]);
if ($stmt->rowCount() === 0) {
echo "No se encontró el producto o el precio no cambió\n";
return false;
}
echo "Precio del producto {$productoId} actualizado a \${$nuevoPrecio}\n";
return true;
}
actualizarPrecio($pdo, 1, 12499.99);
actualizarPrecio($pdo, 999, 100.00);Salida esperada:
Precio del producto 1 actualizado a $12499.99
No se encontró el producto o el precio no cambió
Actualización con parámetros nombrados
Los parámetros nombrados hacen el código más legible cuando hay muchos campos:
<?php
$stmt = $pdo->prepare(
'UPDATE productos SET nombre = :nombre, precio = :precio, stock = :stock WHERE id = :id'
);
$stmt->execute([
':nombre' => 'Laptop HP Pavilion 15 (2024)',
':precio' => 14999.99,
':stock' => 20,
':id' => 1
]);
echo "Producto actualizado. Filas afectadas: " . $stmt->rowCount() . "\n";Actualización dinámica
Cuando el usuario puede modificar solo algunos campos, necesitas construir la consulta dinámicamente:
<?php
function actualizarProducto(PDO $pdo, int $id, array $campos): array {
$permitidos = ['nombre', 'categoria', 'precio', 'stock', 'activo'];
$sets = [];
$params = [];
foreach ($campos as $campo => $valor) {
if (in_array($campo, $permitidos, true)) {
$sets[] = "{$campo} = :{$campo}";
$params[":{$campo}"] = $valor;
}
}
if (empty($sets)) {
return ['exito' => false, 'mensaje' => 'No se proporcionaron campos válidos'];
}
$params[':id'] = $id;
$sql = 'UPDATE productos SET ' . implode(', ', $sets) . ' WHERE id = :id';
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
return [
'exito' => true,
'modificado' => $stmt->rowCount() > 0
];
}
// Uso: actualizar solo precio y stock
$resultado = actualizarProducto($pdo, 1, ['precio' => 11999.99, 'stock' => 50]);
var_export($resultado);Salida esperada:
array ( 'exito' => true, 'modificado' => true, )
Actualización con transacciones
Para operaciones que involucran múltiples actualizaciones relacionadas:
<?php
function transferirStock(PDO $pdo, int $origenId, int $destinoId, int $cantidad): bool {
$pdo->beginTransaction();
try {
// Verificar stock disponible
$stmt = $pdo->prepare('SELECT stock FROM productos WHERE id = ? FOR UPDATE');
$stmt->execute([$origenId]);
$origen = $stmt->fetch();
if (!$origen || $origen['stock'] < $cantidad) {
$pdo->rollBack();
echo "Stock insuficiente\n";
return false;
}
// Descontar del origen
$stmt = $pdo->prepare('UPDATE productos SET stock = stock - ? WHERE id = ?');
$stmt->execute([$cantidad, $origenId]);
// Agregar al destino
$stmt = $pdo->prepare('UPDATE productos SET stock = stock + ? WHERE id = ?');
$stmt->execute([$cantidad, $destinoId]);
$pdo->commit();
echo "{$cantidad} unidades transferidas del producto {$origenId} al {$destinoId}\n";
return true;
} catch (PDOException $e) {
$pdo->rollBack();
echo "Error en la transferencia: {$e->getMessage()}\n";
return false;
}
}Caso práctico
Veamos una función completa de actualización de perfil con auditoría:
<?php
function actualizarPerfil(PDO $pdo, int $userId, array $cambios): array {
$pdo->beginTransaction();
try {
// Obtener valores actuales
$stmt = $pdo->prepare('SELECT nombre, email, telefono FROM usuarios WHERE id = ?');
$stmt->execute([$userId]);
$actual = $stmt->fetch();
if (!$actual) {
$pdo->rollBack();
return ['exito' => false, 'mensaje' => 'Usuario no encontrado'];
}
// Filtrar solo campos que cambiaron
$permitidos = ['nombre', 'email', 'telefono', 'direccion'];
$updates = [];
$params = [];
foreach ($cambios as $campo => $valor) {
if (in_array($campo, $permitidos, true) && ($actual[$campo] ?? null) !== $valor) {
$updates[] = "{$campo} = :{$campo}";
$params[":{$campo}"] = $valor;
}
}
if (empty($updates)) {
$pdo->rollBack();
return ['exito' => true, 'mensaje' => 'No hay cambios que aplicar'];
}
$params[':id'] = $userId;
$sql = 'UPDATE usuarios SET ' . implode(', ', $updates) . ', fecha_actualizacion = NOW() WHERE id = :id';
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
// Registrar auditoría
$stmt = $pdo->prepare(
'INSERT INTO audit_log (tabla, registro_id, accion, detalles, fecha) VALUES (?, ?, ?, ?, NOW())'
);
$stmt->execute(['usuarios', $userId, 'UPDATE', json_encode(['antes' => $actual, 'despues' => $cambios])]);
$pdo->commit();
return ['exito' => true, 'campos_modificados' => count($updates)];
} catch (PDOException $e) {
$pdo->rollBack();
return ['exito' => false, 'mensaje' => 'Error al actualizar: ' . $e->getMessage()];
}
}Manejo de errores
Los errores comunes al actualizar registros en PHP:
<?php
function actualizarSeguro(PDO $pdo, int $id, array $datos): array {
try {
$stmt = $pdo->prepare('UPDATE productos SET nombre = ?, precio = ? WHERE id = ?');
$stmt->execute([$datos['nombre'], $datos['precio'], $id]);
if ($stmt->rowCount() === 0) {
return ['error' => 'NOT_FOUND', 'mensaje' => 'Producto no encontrado o sin cambios'];
}
return ['exito' => true];
} catch (PDOException $e) {
$codigo = $e->errorInfo[1];
switch ($codigo) {
case 1062:
return ['error' => 'DUPLICADO', 'mensaje' => 'Ya existe un producto con ese nombre'];
case 1406:
return ['error' => 'LONGITUD', 'mensaje' => 'El nombre excede el límite de caracteres'];
case 1205:
return ['error' => 'TIMEOUT', 'mensaje' => 'El registro está bloqueado por otro proceso'];
case 1213:
return ['error' => 'DEADLOCK', 'mensaje' => 'Interbloqueo detectado. Intenta de nuevo'];
default:
return ['error' => 'INTERNO', 'mensaje' => "Error [{$codigo}]: {$e->getMessage()}"];
}
}
}Ahora que conoces las técnicas para actualizar registros en PHP, en el siguiente artículo aprenderás a eliminar datos de MySQL.
Escrito por Eduardo Lázaro
