¿Cómo puedo vincular una matriz de cadenas con una declaración preparada mysqli?

12 minutos de lectura

Avatar de usuario de Mark
Marca

Necesito vincular una matriz de valores a WHERE IN(?) cláusula. ¿Cómo puedo hacer eso?

Esto funciona:

$mysqli = new mysqli("localhost", "root", "root", "db");
if(!$mysqli || $mysqli->connect_errno)
{
    return;
}
$query_str = "SELECT name FROM table WHERE city IN ('Nashville','Knoxville')";
$query_prepared = $mysqli->stmt_init();
if($query_prepared && $query_prepared->prepare($query_str))
{
    $query_prepared->execute();

Pero esto no puedo ponerme a trabajar con un enlazar_param como esto:

$query_str = "SELECT name FROM table WHERE city IN (?)";
$query_prepared = $mysqli->stmt_init();
if($query_prepared && $query_prepared->prepare($query_str))
{
    $cities = explode(",", $_GET['cities']);
    $str_get_cities = "'" . implode("', '", $get_cities) . "'"; // This equals 'Nashville','Knoxville'

    $query_prepared->bind_param("s", $cities);
    $query_prepared->execute();

¿Qué estoy haciendo mal?

yo también he intentado call_user_func_arraypero parece que no puedo obtener la sintaxis correcta.

  • $str_get_cities= "'".implode("','", $get_cities)."'"; . ¡¡No utilices comillas!! esto esta hecho adios bind_param con la opción “s”!

    – moskito-x

    10 de julio de 2013 a las 14:14

  • Como otros recomiendan, use call_user_func_array función para vincular los parámetros requeridos a su consulta parametrizada. Solo para enfatizar que acepta parámetros pasados ​​​​por referencia. Solo pude encontrar fragmentos de código sobre cómo ejecutar consultas parametrizadas con una cantidad dinámica de parámetros para vincular, así que terminé haciendo mi propia función (ver esta publicación). Acepta cualquier consulta SELECCIONAR, ACTUALIZAR, INSERTAR y ELIMINAR parametrizada y me ayuda mucho a hacer dinámicamente cualquier interacción de base de datos MySQL en mi código PHP.

    – emboscador

    23 de enero de 2017 a las 15:16

  • Pregunta anterior, clara y completa que pregunta exactamente lo mismo: use una matriz en una declaración preparada de mysqli: WHERE .. IN(..) consulta

    – mickmackusa

    3 de abril de 2022 a las 4:59


Tu avatar de usuario de Common Sense
Tu sentido común

Desde PHP 8.1, puede pasar una matriz directamente para ejecutar:

$sql = "INSERT INTO users (email, password) VALUES (?,?)"; // sql
$stmt = $mysqli->prepare($sql); // prepare
$stmt->execute([$email, $password]); // execute with data! 

Para las versiones anteriores, la tarea es un poco complicada pero factible. Para un caso simple cuando ya tiene una consulta con marcadores de posición, el código sería

$sql = "INSERT INTO users (email, password) VALUES (?,?)"; // sql
$data = [$email, $password]; // put your data into array
$stmt = $mysqli->prepare($sql); // prepare
$stmt->bind_param(str_repeat('s', count($data)), ...$data); // bind array at once
$stmt->execute();

Si bien, como en su caso, tenemos un número arbitrario de marcadores de posición, tendremos que agregar un poco más de código. Tomaré la explicación de mi artículo. Declaración preparada de Mysqli con múltiples valores para la cláusula IN:

  • En primer lugar necesitaremos crear una cadena con tantos ? marca tantos elementos hay en su matriz. Para esto usaríamos str_repeat() función que es muy útil para el propósito.
  • Luego, esta cadena con signos de interrogación separados por comas debe agregarse a la consulta. Aunque es una variable, en este caso es segura ya que contiene solo valores constantes
  • entonces esta consulta debe prepararse como cualquier otra consulta
  • luego necesitaremos crear una cadena con tipos para usar con bind_param(). Tenga en cuenta que generalmente no hay razón para usar diferentes tipos para las variables vinculadas: mysql las aceptará felizmente como cadenas. Hay casos extremos, pero extremadamente raros. Para el uso diario, siempre puede mantenerlo simple y usar “s” para todo. str_repeat() es de nuevo al rescate.
  • entonces necesitamos vincular nuestros valores de matriz a la declaración. Desafortunadamente, no puedes simplemente escribirlo como una sola variable, así $stmt->bind_param("s", $array)solo se permiten variables escalares en bind_param(). Por suerte, hay un operador de desempaquetado de argumentos eso hace exactamente lo que necesitamos: ¡envía una matriz de valores a una función como si fuera un conjunto de variables distintas!
  • el resto es como de costumbre: ejecute la consulta, obtenga el resultado y obtenga sus datos.

Así que el código de ejemplo correcto sería

$array = ['Nashville','Knoxville']; // our array
$in    = str_repeat('?,', count($array) - 1) . '?'; // placeholders
$sql   = "SELECT name FROM table WHERE city IN ($in)"; // sql
$stmt  = $mysqli->prepare($sql); // prepare
$types = str_repeat('s', count($array)); //types
$stmt->bind_param($types, ...$array); // bind array at once
$stmt->execute();
$result = $stmt->get_result(); // get the mysqli result
$data = $result->fetch_all(MYSQLI_ASSOC); // fetch the data   

Aunque este código es bastante grande, es incomparablemente más pequeño que cualquier otra solución plausible ofrecida en este tema hasta ahora.

  • No entiendo el primer párrafo de tu respuesta. No creo que se suponga que una pregunta proporcione una respuesta, él está buscando una respuesta que usted y otros respondieron. Lo que estaba buscando no era cuántos signos de interrogación necesito agregar, que ya tenía. No pude encontrar una explicación adecuada de bind_param, ya que parece que al documento php.net no le importaba. Quería saber si puedo pasarle una matriz como parámetro.

    – Aaaa

    17 abr 2020 a las 14:00

  • @AaA hay otro párrafo en mi respuesta, uno menciona un operador de desempaquetado de argumentos. respondio tu pregunta?

    – Tu sentido común

    18 de abril de 2020 a las 3:39


  • Gracias, el “operador de argumento desempaquetado” fue la respuesta para mí. Esta respuesta resuelve muchas otras preguntas similares con soluciones muy elaboradas.

    – Marco Muciño

    23 de julio de 2020 a las 22:02

  • esto no funciona$stmt->bind_param($paramstring, $params ); dame Array to string conversion cada vez

    – djack109

    31 de diciembre de 2020 a las 17:01

avatar de usuario de moskito-x
moskito-x

No puedes vincular dos variables con una. signo de interrogación!

Por cada variable que vinculas, necesitas una signo de interrogación.

“bind_param” verifica cada variable si cumple con los requisitos. Posteriormente, el valor de la cadena se coloca entre comillas.

Esto no funcionará:

"SELECT name FROM table WHERE city IN (?)"; ( becomes too )
$q_prepared->bind_param("s", $cities);
"SELECT name FROM table WHERE city IN ('city1,city2,city3,city4')";

debe ser:

"SELECT name FROM table WHERE city IN (?,?,?,?)"; ( becomes too )
$q_prepared->bind_param("ssss", $city1, $city2, $city3, $city4);
"SELECT name FROM table WHERE city IN ('city1', 'city2', 'city3', 'city4')";

$query_prepared->bind_param cita los parámetros de cadena uno por uno. Y el número de variables y la longitud de los tipos de cadena deben coincidir con los parámetros de la declaración.

$query_str = "SELECT name FROM table WHERE city IN ('Nashville','Knoxville')";

se convertirá

$query_str = "SELECT name FROM table WHERE city IN (?,?)";

Ahora bind_param debe ser

bind_param("ss", $arg1, $arg2)

con este

$query_str = "SELECT name FROM table WHERE city IN (?)";

y bind_param con

bind_param("s", $cities)

Usted obtiene:

$query_str = "SELECT name FROM table WHERE city IN ('Nashville,Knoxville')";

Es por eso que una matriz no funciona. La única solución para este hecho es call_user_func_array.

Si inicializa una declaración, lo siguiente es innecesario:

$query_prepared = $mysqli->stmt_init();
if($query_prepared && $query_prepared->prepare($query_str)) {

Esto es correcto:

$query_prepared = $mysqli->stmt_init();
if($query_prepared->prepare($query_str)) {

Si no quieres usar call_user_func_array y solo tiene una pequeña cantidad de argumentos, puede hacerlo con el siguiente código.

[...]
$cities = explode(",", $_GET['cities']);
if (count($cities) > 3) { echo "too many arguments"; }
else
{
    $count = count($cities);
    $SetIn = "(";
    for($i = 0; $i < $count; ++$i)
    {
        $code .= 's';
        if ($i>0) {$SetIn.=",?";} else {$SetIn.="?";}
    }
    $SetIn .= ")";
    $query_str = "SELECT name FROM table WHERE city IN " . $SetIn;
    // With two arguments, $query_str will look like
    // SELECT name FROM table WHERE city IN (?,?)
    $query_prepared = $mysqli->stmt_init();
    if($query_prepared->prepare($query_str))
    {
        if ($count==1) { $query_prepared->bind_param($code, $cities[0]);}
        if ($count==2) { $query_prepared->bind_param($code, $cities[0], $cities[1]);}
        if ($count==3) { $query_prepared->bind_param($code, $cities[0], $cities[1], $cities[2]);
        // With two arguments, $query_prepared->bind_param() will look like
        // $query_prepared->bind_param("ss", $cities[0], $cities[1])
        }
        $query_prepared->execute();
    }
    [...]
}

Te sugiero que lo pruebes con call_user_func_array alcanzar.

Busca la solución de nick9v.

mysqli_stmt::bind_param

  • sugieres usar call_user_func_array pero nunca se muestra aquí cómo hacerlo. =_=’

    – ggDeGran

    24 de octubre de 2015 a las 5:24

  • aquí hay un caso de uso de call_user_func_array y todo el problema explicado pontikis.net/blog/dynamically-bind_param-array-mysqli

    – Mochi

    11 de abril de 2020 a las 10:09

  • “Se debe tener cuidado al usar mysqli_stmt_bind_param() junto con call_user_func_array(). Tenga en cuenta que mysqli_stmt_bind_param() requiere que los parámetros se pasen por referencia, mientras que call_user_func_array() puede aceptar como parámetro una lista de variables que pueden representar referencias o valores. ” Fuente -> php.net/manual/en/mysqli-stmt.bind-param.php

    – Me esfuerzo mucho pero lloro más fuerte

    3 de julio de 2020 a las 18:20

avatar de usuario de miken32
miken32

A partir de la versión 8.1 de PHP, ya no se requiere encuadernación. Al igual que con PDO desde la versión 5.0, ahora puede pasar parámetros como una matriz directamente a el método de ejecución.

$mysqli       = new mysqli("localhost", "root", "root", "db");
$params       = ['Nashville','Knoxville'];
$placeholders = str_repeat('?,', count($params) - 1) . '?'
$query        = "SELECT name FROM table WHERE city IN ($placeholders)";
$stmt         = $mysqli->prepare($query);

$stmt->execute($params);

Otro ejemplo, si tiene una matriz asociativa con claves que coinciden con los nombres de las columnas:

$mysqli       = new mysqli("localhost", "root", "root", "db");
$data         = ["bar" => 23, "baz" => "some data"];
$params       = array_values($data);
$placeholders = str_repeat('?,', count($params) - 1) . '?'
$columns      = implode("`,`", array_keys($data));
$query        = "INSERT INTO foo (`$columns`) VALUES ($placeholders)";
$stmt         = $mysqli->prepare($query);

$stmt->execute($params);

También vale la pena mencionar que la biblioteca ahora por defecto a lanzar excepciones en caso de errores. Antes de la versión 8.1, este no era el caso.

Avatar de usuario de SingSong
Cantar la canción

Usar call_user_func_array como esto:

$stmt = $mysqli->prepare("INSERT INTO t_file_result VALUES(?,?,?,?)");

$id = '1111';
$type = 2;
$result = 1;
$path="/root";

$param = array('siis', &$id, &$type, &$result, &$path);
call_user_func_array(array($stmt, 'bind_param'), $param);

$stmt->execute();

printf("%d row inserted. \n", $stmt->effected_rows);
$stmt->close;

avatar de usuario de mickadoo
mickadoo

Yo también estaba teniendo problemas con esto, y lo hice funcionar con eval antes de descubrir que la mayoría de la gente está usando call_user_func_array:

$fields = array('model', 'title', 'price'); // Fields in WHERE clause
$values = array( // Type and value for each field
    array('s', 'ABCD-1001'),
    array('s', '[CD] Test Title'),
    array('d', '16.00')
);
$sql = "SELECT * FROM products_info WHERE "; // Start of query
foreach ($fields as $current) { // Build where clause from fields
    $sql .= '`' . $current . '` = ? AND ';
}
$sql = rtrim($sql, 'AND '); // Remove last AND
$stmt = $db->prepare($sql);
$types=""; $vals="";
foreach ($values as $index => $current_val) { // Build type string and parameters
    $types .= $current_val[0];
    $vals .= '$values[' . $index . '][1],';
}
$vals = rtrim($vals, ','); // Remove last comma
$sql_stmt="$stmt->bind_param("" . $types . '",' . $vals . ');'; // Put bind_param line together
eval($sql_stmt); // Execute bind_param
$stmt->execute();
$stmt->bind_result($col1, $col2, $col3, $col4, $col5, $col6); // This could probably also be done dynamically in the same way
while ($stmt->fetch()) {
    printf("%s %s %s %s %s %s\n", $col1, $col2, $col3, $col4, $col5, $col6);
}

  • Definitivamente requiere jugar un poco, pero en realidad me gusta mucho esta respuesta. Puede ser un poco más voluminoso que call_user_func_array pero honestamente es más fácil de leer y averiguar qué está pasando.

    –Ethan Moore

    20 de julio de 2020 a las 0:35

php 8.2 resolvió casi todo con execute_query

$this->execute_query($query, $parms);

como

$query = "SELECT * FROM users WHERE `email` IN (?) LIMIT 1";
$parms = ["xyx@gmailx.com"];
$this->execute_query($query, $parms);

Recordar ? es marcador de posición y número de ? (marcador de posición) es el mismo número que count($parms) y se puede hacer de varias maneras.

Así que en tu caso debería ser

$query = "SELECT name FROM table WHERE city IN (? , ?)";
$parms = ['Nashville','Knoxville'];
$this->execute_query($query, $parms);

https://www.php.net/manual/en/mysqli.ejecutar-consulta

si desea crear una consulta $ dinámica primero, luego

$query = "SELECT name FROM table WHERE city IN (".implode(",",array_map(fn()=>"?",$parms)).")";

  • Definitivamente requiere jugar un poco, pero en realidad me gusta mucho esta respuesta. Puede ser un poco más voluminoso que call_user_func_array pero honestamente es más fácil de leer y averiguar qué está pasando.

    –Ethan Moore

    20 de julio de 2020 a las 0:35

Avatar de usuario de Peter Mortensen
Pedro Mortensen

La forma en que lo hice: prepare la consulta con todos sus signos de interrogación separados, así como la cadena de caracteres.

$cities = array('Nashville', 'Knoxville');
$dibs="";
$query = "SELECT name FROM table WHERE city IN (";
$marks = array();

foreach ($cities as $k => $city) {
    // i, s, b, d type based on the variables to bind.
    $dibs .= 's';
    array_push($marks, '?');
}

$query .= implode(',', $marks) . ')';

Conectar.

$mysql = new mysqli($host, $user, $pass, $dbname);
$statement =
    $mysql->prepare($query)
OR    die(sprintf(
        'Query error (%s) %s', $mysql->errno, $mysql->error
    ))
;

Luego usas “…” token / puntos suspensivos (documentación) para vincular la matriz.

if ($statement) {
    $statement->bind_param($dibs, ...$cities);
    $statement->execute();

    $statement->close();
}
$mysql->close();

Sé que anula un poco el propósito de vincular para escapar (pero al menos funciona bien con una lista de números enteros, es decir, ID).

¿Ha sido útil esta solución?