638 votes

Puis-je lier un tableau des conditions d’IN() ?

Je suis curieux de savoir si il est possible de lier un tableau de valeurs dans un espace réservé à l'aide de l'AOP. Le cas d'utilisation ici est d'essayer de transmettre un tableau de valeurs pour une utilisation avec une (des) condition. Je ne suis pas très douée pour expliquer, alors voici quelques psuedocode à démontrer... j'aimerais être capable de faire quelque chose comme ceci:

<?php
$ids=array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN(:an_array)'
);
$stmt->bindParam('an_array',$ids);
$stmt->execute();
?>

Et ont AOP lier et de citer toutes les valeurs dans le tableau.

Pour le moment je suis en train de faire:

<?php
$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
foreach($ids as &$val)
    $val=$db->quote($val); //iterate through array and quote
$in = implode(',',$ids); //create comma separated list
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN('.$in.')'
);
$stmt->execute();
?>

Ce qui a certainement fait le boulot, mais je me demandais si il y a une construite dans la solution je suis absent?

Cheers!

304voto

stefs Points 8257

je pense que soulmerge est droit. vous aurez à construire la chaîne de requête.

<?php
$ids     = array(1, 2, 3, 7, 8, 9);
$inQuery = implode(',', array_fill(0, count($ids), '?'));

$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN(' . $inQuery . ')'
);

// bindvalue is 1-indexed, so $k+1
foreach ($ids as $k => $id)
    $stmt->bindValue(($k+1), $id);

$stmt->execute();
?>

correctif: dan, vous avez eu raison. fixe le code (ne pas le tester tout de même)

edit: les deux chris (commentaires) et somebodyisintrouble a suggéré que le foreach en boucle ...

(...)
// bindvalue is 1-indexed, so $k+1
foreach ($ids as $k => $id)
    $stmt->bindValue(($k+1), $id);

$stmt->execute();

... peut-être redondante, l' foreach boucle et de l' $stmt->execute pourrait être remplacé par tout simplement ...

<?php 
  (...)
  $stmt->execute($ids);
?>

(encore une fois, je n'ai pas tester)

194voto

DonamiteIsTnt Points 5121

Quelque chose de rapide :

47voto

Est-ce si important d’utiliser dans l’instruction ? Utilisez FIND_IN_SET au lieu de.

Par exemple, en AOP votre requête sera considérée comme

Puis vient lier tableau de valeurs a implosé avec la virgule comme celui-ci

et ainsi de suite

17voto

Sergey Galkin Points 1

La solution d'EvilRygy n'a pas fonctionné pour moi. Dans Postgres vous pouvez faire une autre solution de contournement:

 
$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id = ANY (string_to_array(:an_array, ','))'
);
$stmt->bindParam(':an_array', implode(',', $ids));
$stmt->execute();
 

12voto

Chris Points 1017

J'ai étendu PDO pour faire quelque chose de similaire à ce que les stefs suggèrent, et cela a été plus facile pour moi à long terme:

 class Array_Capable_PDO extends PDO {
    /**
     * Both prepare a statement and bind array values to it
     * @param string $statement mysql query with colon-prefixed tokens
     * @param array $arrays associatve array with string tokens as keys and integer-indexed data arrays as values 
     * @param array $driver_options see php documention
     * @return PDOStatement with given array values already bound 
     */
    public function prepare_with_arrays($statement, array $arrays, $driver_options = array()) {

        $replace_strings = array();
        $x = 0;
        foreach($arrays as $token => $data) {
            // just for testing...
            //// tokens should be legit
            //assert('is_string($token)');
            //assert('$token !== ""');
            //// a given token shouldn't appear more than once in the query
            //assert('substr_count($statement, $token) === 1');
            //// there should be an array of values for each token
            //assert('is_array($data)');
            //// empty data arrays aren't okay, they're a SQL syntax error
            //assert('count($data) > 0');

            // replace array tokens with a list of value tokens
            $replace_string_pieces = array();
            foreach($data as $y => $value) {
                //// the data arrays have to be integer-indexed
                //assert('is_int($y)');
                $replace_string_pieces[] = ":{$x}_{$y}";
            }
            $replace_strings[] = '('.implode(', ', $replace_string_pieces).')';
            $x++;
        }
        $statement = str_replace(array_keys($arrays), $replace_strings, $statement);
        $prepared_statement = $this->prepare($statement, $driver_options);

        // bind values to the value tokens
        $x = 0;
        foreach($arrays as $token => $data) {
            foreach($data as $y => $value) {
                $prepared_statement->bindValue(":{$x}_{$y}", $value);
            }
            $x++;
        }

        return $prepared_statement;
    }
}
 

Vous pouvez l'utiliser comme ceci:

 $db_link = new Array_Capable_PDO($dsn, $username, $password);

$query = '
    SELECT     *
    FROM       test
    WHERE      field1 IN :array1
     OR        field2 IN :array2
     OR        field3 = :value
';

$pdo_query = $db_link->prepare_with_arrays(
    $query,
    array(
        ':array1' => array(1,2,3),
        ':array2' => array(7,8,9)
    )
);

$pdo_query->bindValue(':value', '10');

$pdo_query->execute();
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X