Tutorial 21: Crear Datos con PDO Parte 2
¡Hola de nuevo! En el tutorial pasado, nuestros datos viajaron desde el formulario hasta el modelo. Hoy vamos a terminar el viaje: vamos a escribir la consulta SQL de manera segura usando PDO, y lograremos que los datos se guarden en la base de datos.
Paso 1: La Sentencia SQL (La Receta)
Lo primero que necesitamos es la "receta" para guardar datos en nuestra tabla de MySQL. Vamos a phpMyAdmin, seleccionamos nuestra base de datos, y luego la tabla usuarios. Ahí hay una pestaña que dice SQL. Al hacer clic, podemos escribir comandos SQL directamente.
El comando para insertar datos es INSERT. Una consulta típica se ve así:
INSERT INTO usuarios (id, usuario, password, email) VALUES (NULL, 'juan', '1234', 'juan@correo.com')
Pero espera, ¡no vamos a copiar esto exactamente! Vamos a adaptarla para nuestro código.
La tabla: Nosotros ya recibimos el nombre de la tabla como parámetro (
$tabla), que en este caso es "usuarios". Así que en lugar de escribir "usuarios" fijo, usaremos la variable$tabla.Los campos: No necesitamos incluir el campo
idporque en nuestra base de datos esAUTO_INCREMENT. Esto significa que MySQL le asignará un número único automáticamente. Solo vamos a insertarusuario,passwordyemail.Los valores: Aquí viene lo más importante y seguro. NUNCA vamos a pegar directamente las variables del usuario (
$_POST["usuario"]) dentro de la consulta SQL. Eso es como invitar a un hacker a entrar a nuestra base de datos. En lugar de eso, usaremos marcadores de posición.
Nuestra consulta final preparada se verá así:
INSERT INTO $tabla(usuario, password, email) VALUES (:usuario, :password, :email)
Los :usuario, :password y :email son los marcadores de parámetros con nombre. Son como "huecos" que llenaremos más tarde, de forma segura.
Paso 2: Preparar la Consulta con PDO (prepare)
Ahora vamos a nuestro modelo (crud.php). Dentro de la función registroUsuarioModel, vamos a usar el método prepare() de PDO.
¿Qué hace prepare()? Le envía la consulta SQL a la base de datos y le dice: "Oye, aquí tengo una consulta que quiero ejecutar, pero aún no tiene los valores. Por favor, prepárate para recibirla después con los datos". Esto es clave para la seguridad.
public function registroUsuarioModel($datosModel, $tabla){ // Usamos Conexion::conectar() para conectarnos a la BD, // y luego llamamos a prepare() con nuestra consulta SQL. $stmt = Conexion::conectar()->prepare("INSERT INTO $tabla(usuario, password, email) VALUES (:usuario, :password, :email)"); // ... (siguiente paso) }
Explicación:
Conexion::conectar(): Nos da la conexión activa a la base de datos.->prepare(...): Preparamos la consulta SQL con los marcadores:usuario, etc.Guardamos todo en una variable llamada
$stmt(que viene de "statement", o declaración/preparación).
Paso 3: Vincular los Parámetros (bindParam)
Ya tenemos la consulta preparada con sus "huecos" (:usuario, :password, :email). Ahora tenemos que llenar esos huecos con los valores reales que el usuario escribió. Para eso usamos bindParam().
bindParam() vincula (o asocia) una variable PHP con un marcador de posición. Así, cuando la consulta se ejecute, PDO tomará el valor de esa variable y lo colocará en el hueco correspondiente, de manera segura.
public function registroUsuarioModel($datosModel, $tabla){ $stmt = Conexion::conectar()->prepare("INSERT INTO $tabla(usuario, password, email) VALUES (:usuario, :password, :email)"); // Vinculamos los marcadores con los valores reales que vienen en $datosModel $stmt->bindParam(":usuario", $datosModel["usuario"], PDO::PARAM_STR); $stmt->bindParam(":password", $datosModel["password"], PDO::PARAM_STR); $stmt->bindParam(":email", $datosModel["email"], PDO::PARAM_STR); // ... (siguiente paso) }
Explicación:
$stmt->bindParam(...): Sobre nuestra consulta preparada ($stmt), usamosbindParam.":usuario": El nombre del marcador que queremos llenar (debe coincidir exactamente con el de la consulta).$datosModel["usuario"]: La variable PHP que contiene el dato real (por ejemplo, "juan").PDO::PARAM_STR: Le decimos a PDO que el dato que vamos a insertar es de tipo texto (STRING). Esto es una capa extra de seguridad.
Paso 4: Ejecutar la Consulta (execute)
Todo está listo. La consulta está preparada y los parámetros están vinculados. El último paso es ejecutarla con el método execute().
El método execute() ejecuta la consulta y devuelve true si todo salió bien, o false si hubo algún error.
public function registroUsuarioModel($datosModel, $tabla){ $stmt = Conexion::conectar()->prepare("INSERT INTO $tabla(usuario, password, email) VALUES (:usuario, :password, :email)"); $stmt->bindParam(":usuario", $datosModel["usuario"], PDO::PARAM_STR); $stmt->bindParam(":password", $datosModel["password"], PDO::PARAM_STR); $stmt->bindParam(":email", $datosModel["email"], PDO::PARAM_STR); // Ejecutamos y verificamos el resultado if($stmt->execute()){ return "success"; } else { return "error"; } }
Explicación:
if($stmt->execute()): Preguntamos: "¿La ejecución fue exitosa?".Si es
true, la funciónregistroUsuarioModeldevuelve la palabra "success".Si es
false, devuelve "error".
Este resultado ("success" o "error") viaja de regreso al controlador y se guarda en la variable $respuesta.
Paso 5: Validar que el Formulario se Envió (Corrigiendo un Error)
Ahora, si probamos nuestro formulario tal como está, nos encontraremos con un error: "Undefined index: usuario...". Esto pasa porque cuando entramos a la página por primera vez, el formulario no se ha enviado, ¡pero nuestro código dentro del controlador aún intenta leer $_POST["usuario"]!
Para solucionarlo, debemos preguntar si el formulario ya fue enviado. Esto lo hacemos con isset(), que verifica si una variable existe y no es nula.
En nuestro controlador (controller.php), envolvemos toda la lógica dentro de un if:
public function registroUsuarioController(){ // Preguntamos: ¿Ya se envió el formulario? (¿Existe $_POST["usuario"]?) if(isset($_POST["usuario"])){ $datosController = array( "usuario"=>$_POST["usuario"], "password"=>$_POST["password"], "email"=>$_POST["email"] ); $respuesta = Datos::registroUsuarioModel($datosController, "usuarios"); echo $respuesta; // Por ahora, solo muestra "success" o "error" } }
Ahora, el código dentro del if solo se ejecutará cuando alguien haya hecho clic en "Enviar". El primer error ha desaparecido.
Paso 6: ¡Probamos! (Y Corregimos un Error más)
Vamos a probar. Llenamos el formulario con:
Usuario: juan
Contraseña: 1234
Email: juan@hotmail.com
Hacemos clic en "Enviar" y... ¡aparece "success"! Ahora, si revisamos nuestra tabla usuarios en phpMyAdmin, veremos algo como esto:
| id | usuario | password | |
|---|---|---|---|
| 1 | juan | 1234 | juan@hotmail.com |
¡Funcionó! Los datos están en la base de datos.
Paso 7: Mejorando la Experiencia del Usuario (Redireccionar y Mostrar Mensajes)
Tenemos un pequeño problema. Si después de ver "success", actualizamos la página, el formulario se reenviará y se creará otro usuario duplicado (esto se llama "reenvío de formulario"). Para evitarlo, usaremos una técnica llamada PRG (Post-Redirect-Get).
La idea es: después de procesar el formulario (POST), en lugar de mostrar un mensaje directamente, redirigimos al usuario a otra página (GET) usando header(). Luego, en esa página, mostramos un mensaje.
Paso 7.1: Redirigir desde el Controlador
Cambiamos el controlador. Si el registro fue exitoso, redirigimos a index.php?action=ok. Si no, redirigimos a index.php?action=error.
public function registroUsuarioController(){ if(isset($_POST["usuario"])){ $datosController = array( "usuario"=>$_POST["usuario"], "password"=>$_POST["password"], "email"=>$_POST["email"] ); $respuesta = Datos::registroUsuarioModel($datosController, "usuarios"); if($respuesta == "success"){ header("location:index.php?action=ok"); } else { header("location:index.php?action=error"); } } }
Paso 7.2: Mostrar el Mensaje en la Vista
En nuestro archivo de vista (registro.php), debemos agregar la lógica para mostrar el mensaje según lo que venga en la URL (action).
<h1>REGISTRO DE USUARIO</h1> <?php // Verificamos si existe la variable 'action' en la URL if(isset($_GET["action"])){ if($_GET["action"] == "ok"){ echo "<p style='color:green;'>¡Registro Exitoso!</p>"; } if($_GET["action"] == "error"){ echo "<p style='color:red;'>Hubo un error en el registro.</p>"; } } ?> <form method="post"> <input type="text" placeholder="Usuario" name="usuario" required> <input type="password" placeholder="Contraseña" name="password" required> <input type="email" placeholder="Email" name="email" required> <input type="submit" value="Enviar"> </form> <?php $registro = new MvcController(); $registro -> registroUsuarioController(); ?>
Paso 7.3: Actualizar la Lista Blanca de Enlaces
Recuerda que tenemos un archivo de enlaces.php (o similar) que controla a qué páginas podemos navegar. Debemos agregar "ok" y "error" a la lista de páginas permitidas para que la redirección funcione correctamente.
Resumen de la Parte 2
¡Felicidades! Hemos completado el ciclo completo de registro.
Preparamos la consulta SQL con
prepare(), usando marcadores:nombrepara los datos del usuario.Vinculamos los parámetros con
bindParam(), asociando cada marcador con el valor real que viene del controlador.Ejecutamos la consulta con
execute()y verificamos si fue exitosa.Protegimos nuestra aplicación comprobando que el formulario fue enviado con
isset().Mejoramos la experiencia del usuario usando el patrón PRG (Post/Redirect/Get) para evitar el reenvío del formulario al actualizar la página.
Ahora tienes un sistema de registro funcional y, lo más importante, ¡seguro contra ataques de inyección SQL!
Comentarios
Publicar un comentario