Comment utiliser WHERE IN avec Doctrine 2

125

J'ai le code suivant qui me donne l'erreur:

Message: Invalid parameter number: number of bound variables does not match number of tokens 

Code:

public function getCount($ids, $outcome)
{
    if (!is_array($ids)) {
        $ids = array($ids);
    }
    $qb = $this->getEntityManager()->createQueryBuilder();
    $qb->add('select', $qb->expr()->count('r.id'))
       ->add('from', '\My\Entity\Rating r');
    if ($outcome === 'wins') { 
        $qb->add('where', $qb->expr()->in('r.winner', array('?1')));
    }
    if ($outcome === 'fails') {
        $qb->add('where', $qb->expr()->in('r.loser', array('?1')));
    }
    $qb->setParameter(1, $ids);
    $query = $qb->getQuery();
    //die('q = ' . $qb);
    return $query->getSingleScalarResult();
}

Données (ou $ ids):

Array
(
    [0] => 566
    [1] => 569
    [2] => 571
)

Résultat DQL:

q = SELECT COUNT(r.id) FROM \My\Entity\Rating r WHERE r.winner IN('?1')
Tjorriemorrie
la source
1
Je pense que c'est la méthode recommandée docs.doctrine-project.org/projects/doctrine-dbal/en/latest/…
martin

Réponses:

114

En recherchant ce problème, j'ai trouvé quelque chose qui sera important pour quiconque rencontrant ce même problème et cherchant une solution.

À partir du message d'origine, la ligne de code suivante:

$qb->add('where', $qb->expr()->in('r.winner', array('?1')));

L'emballage du paramètre nommé en tant que tableau provoque le problème de numéro de paramètre lié. En le supprimant de son enveloppe de tableau:

$qb->add('where', $qb->expr()->in('r.winner', '?1'));

Ce problème devrait être résolu. Cela a pu être un problème dans les versions précédentes de Doctrine, mais il est corrigé dans les versions les plus récentes de 2.0.

Buster Neece
la source
5
Je pense que $qb->expr()->in()c'est seulement dans Doctrine 2 ORM, mais pas dans Doctrine DBAL.
martin le
3
$qb->expr()->in()est en effet à DBAL
JamesHalsall
345

Le moyen le plus simple de le faire est de lier le tableau lui-même en tant que paramètre:

$queryBuilder->andWhere('r.winner IN (:ids)')
             ->setParameter('ids', $ids);
Maciej Pyszyński
la source
41
Pas seulement mais à partir de 2.1
Maciej Pyszyński
7
@ MaciejPyszyński +1. Les moyens les plus simples sont souvent les meilleurs!
Andrzej Ośmiałowski
2
Mention rapide: Cela fonctionne par défaut avec -> setParameter ('ids', $ ids) mais pas avec -> setParameters ('ids' => $ ids). Cela m'a pris quelques minutes de débogage.
larrydahooster
3
faire est travailler avec -> setParameters (...) ->where('b.status IN (:statuses)') ->setParameters([ 'customerId' => $customerId, 'storeId' => $storeId, 'statuses' => [Status::OPEN, Status::AWAITING_APPROVAL, Status::APPROVED] ]);
George Mylonas
5
Je tiens à souligner l'importance de passer également le 3ème paramètre setParameterà forceConnection::PARAM_STR_ARRAY
Luc Wollants
58

et pour compléter la solution de chaîne

$qb->andWhere('foo.field IN (:string)');
$qb->setParameter('string', array('foo', 'bar'), \Doctrine\DBAL\Connection::PARAM_STR_ARRAY);
Daniel Espendiller
la source
Certainement la meilleure solution pour moi :-)
Francesco Casula
3
Peut également utiliser \ Doctrine \ DBAL \ Connection :: PARAM_INT_ARRAY si vous avez un tableau d'entiers et non de chaînes.
Omn
12

Je sais que c'est un ancien message mais peut être utile pour quelqu'un. Je voterais et améliorerais la réponse de @Daniel Espendiller en répondant à la question posée dans les commentaires sur les ints

Pour que cela fonctionne correctement pour les int, assurez-vous que les valeurs du tableau sont de type int, vous pouvez taper cast en int avant de passer ...

 $qb->andWhere('foo.field IN (:ints)');
 $qb->setParameter('ints', array(1, 2), 
 \Doctrine\DBAL\Connection::PARAM_INT_ARRAY);

Testé pour la sélection / suppression dans symfony 3.4 et doctrine-bundle: 1.8

Azhar Khattak
la source
8

Je sais que l'exemple de l'OP utilise DQL et le générateur de requêtes, mais je suis tombé sur cela pour savoir comment le faire à partir d'un contrôleur ou en dehors de la classe de référentiel, alors peut-être que cela aidera les autres.

Vous pouvez également faire un à WHERE INpartir du contrôleur de cette façon:

// Symfony example
$ids    = [1, 2, 3, 4];
$repo   = $this->getDoctrine()->getRepository('AppBundle:RepoName');
$result = $repo->findBy([
    'id' => $ids
]);
Oui Barry
la source
1
C'est une manière parfaitement acceptable de faire un where in sans utiliser DQL, mais sa question était en référence à son code DQL. Il fait plus que simplement me donner toutes les choses basées sur ces identifiants.
spetz83
6

La meilleure façon de procéder, surtout si vous ajoutez plus d'une condition, est:

$values = array(...); // array of your values
$qb->andWhere('where', $qb->expr()->in('r.winner', $values));

Si votre tableau de valeurs contient des chaînes, vous ne pouvez pas utiliser la méthode setParameter avec une chaîne implosée, car vos guillemets seront échappés!

ck1
la source
6

Voici comment je l'ai utilisé:

->where('b.status IN (:statuses)')
->setParameters([
                'customerId' => $customerId,
                'storeId'    => $storeId,
                'statuses'   => [Status::OPEN, Status::AWAITING_APPROVAL, Status::APPROVED]
            ]);
George Mylonas
la source
5

J'ai trouvé comment le faire en 2016: https://redbeardtechnologies.wordpress.com/2011/07/01/doctrine-2-dql-in-statement/

Citation:

Voici comment le faire correctement:

$em->createQuery(“SELECT users 
     FROM Entities\User users 
     WHERE 
         users.id IN (:userids)”)
->setParameters(
     array(‘userids => $userIds)
);

La méthode setParametersprendra le tableau donné et l'implosera correctement pour être utilisé dans l'instruction «IN».

Calamity Jane
la source
2
Cela a résolu mon problème (les parenthèses autour :userids)
Mihai Răducanu
2

Je préfère:

$qb->andWhere($qb->expr()->in('t.user_role_id', [
    User::USER_ROLE_ID_ADVERTISER,
    User::USER_ROLE_ID_MANAGER,
]));
mnv
la source
0
->where($qb->expr()->in('foo.bar', ':data'))
            ->setParameter('participants', $data);

Fonctionne également avec:

 ->andWhere($qb->expr()->in('foo.bar', ':users'))
                ->setParameter('data', $data);
John Smith
la source
0

J'ai eu du mal avec ce même scénario où je devais faire une requête sur un tableau de valeurs.

Ce qui suit a fonctionné pour moi:

http://docs.doctrine-project.org/projects/doctrine1/en/latest/en/manual/dql-doctrine-query-language.html#where-clause

->andWhereIn("[fieldname]", [array[]])

Exemple de données de tableau (travaillé avec des chaînes et des entiers):

$ids = array(1, 2, 3, 4);

Exemple de requête (adaptez-vous là où vous en avez besoin):

$q = dataTable::getInstance()
    ->createQuery()
    ->where("name = ?",'John')
    ->andWhereIn("image_id", $ids)
    ->orderBy('date_created ASC')
    ->limit(100);

$q->execute();
Mortolien
la source
0

C'est des années plus tard, travaillant sur un ancien site ... Pendant toute ma vie, je n'ai pas pu faire fonctionner les solutions ->andWhere()ou ->expr()->in().

Finalement regardé dans le repo Doctrine mongodb-odb et trouvé quelques tests très révélateurs:

public function testQueryWhereIn()
{ 
  $qb = $this->dm->createQueryBuilder('Documents\User');
  $choices = array('a', 'b');
  $qb->field('username')->in($choices);
  $expected = [
    'username' => ['$in' => $choices],
  ];
  $this->assertSame($expected, $qb->getQueryArray());
}

Cela a fonctionné pour moi!

Vous pouvez trouver les tests sur github ici . Utile pour clarifier toutes sortes d'absurdités.

Remarque: ma configuration utilise Doctrine MongoDb ODM v1.0.dev pour autant que je sache.

chichilatte
la source