Introducción: "¿Por qué PDO no entiende mi array?"
\nTodo desarrollador PHP que trabaja con bases de datos, tarde o temprano se enfrenta a la necesidad de ejecutar una consulta con el operador IN(). Parece sencillo: pasar un array de valores y obtener los datos filtrados. Pero aquí está el verdadero truco.
Este es el ejemplo clásico que viene a la mente de un principiante:
\n<?php\n$ids = [1, 2, 3, 5];\n$stmt = $pdo->prepare("SELECT * FROM usuarios WHERE id IN (:ids)");\n$stmt->execute([':ids' => $ids]); // ¡Error!\n?>\nY entonces PDO lanza un error: "Array to string conversion" o simplemente devuelve un resultado vacío. ¿Por qué sucede esto? PDO no sabe "desplegar" arrays en consultas SQL. Espera un valor escalar para cada placeholder nombrado, no un array completo.
\nEn este artículo analizaremos los errores principales que cometen los desarrolladores al intentar vincular un array a IN(), y mostraremos soluciones correctas, seguras y eficientes.
Error #1: Pasar el array directamente a execute()
\nEste es el error más común. El desarrollador piensa que PDO adivinará que debe sustituir cada valor del array en la condición IN(). Pero, como ya vimos, PDO no funciona así.
Cómo se ve en el código
\n<?php\n$ids = [10, 20, 30];\n$stmt = $pdo->prepare("SELECT * FROM productos WHERE id IN (:ids)");\n$stmt->execute([':ids' => $ids]); // Error fatal\n?>\nResultado: Error o advertencia sobre conversión de array a cadena. Incluso si el código "pasa", PDO insertará la palabra "Array" en la consulta, obteniendo WHERE id IN ('Array'), lo que no tiene sentido.
¿Por qué sucede? PDO exige que cada placeholder tenga un solo valor. Un array no es un valor, sino un conjunto de valores. PDO no puede ni debe adivinar lo que quieres hacer.
\n\nError #2: Usar bindValue() con un array
\nA veces los desarrolladores intentan engañar a PDO usando bindValue():
<?php\n$ids = [1, 2, 3];\n$stmt = $pdo->prepare("SELECT * FROM usuarios WHERE id IN (:ids)");\n$stmt->bindValue(':ids', $ids, PDO::PARAM_STR); // ¡Error!\n$stmt->execute();\n?>\nEl problema es el mismo: bindValue() espera un valor escalar. El array se convertirá en la cadena "Array" y la consulta no se ejecutará correctamente. El tercer parámetro (PDO::PARAM_STR) no ayuda: solo indica el tipo de dato, pero no cambia la lógica de manejo del array.
Error #3: Concatenación directa (Inyección SQL)
\nDesesperados, algunos desarrolladores toman el camino más fácil: insertar el array en la consulta mediante implode():
<?php\n$ids = [1, 2, 3];\n$ids_str = implode(',', $ids);\n$stmt = $pdo->query("SELECT * FROM usuarios WHERE id IN ($ids_str)"); // ¡Peligroso!\n?>\n¿Por qué es peligroso? Si los valores del array provienen del usuario (por ejemplo, de parámetros GET o formularios), un atacante podría enviar 1; DROP TABLE usuarios; --. La consulta se convertiría en IN (1; DROP TABLE usuarios; --) y tu base de datos sería destruida. Esto es una inyección SQL clásica.
Incluso si estás seguro de que los datos son seguros, esta práctica es de mal gusto. Algún día olvidarás verificar la fuente de los datos y las consecuencias serán catastróficas.
\n\nSolución correcta: Placeholders dinámicos
\nAhora las buenas noticias. PDO permite crear placeholders de forma dinámica. Debes generar una cadena con la cantidad necesaria de ? o :id1, :id2, ... y pasar el array de valores por separado.
Método 1: Usar placeholders posicionales (?)
\nEste es el método más simple y popular:
\n<?php\n$ids = [1, 2, 3, 5];\n$placeholders = implode(',', array_fill(0, count($ids), '?'));\n$stmt = $pdo->prepare("SELECT * FROM usuarios WHERE id IN ($placeholders)");\n$stmt->execute($ids); // ¡Funciona!\n?>\n