Come usare WHERE IN con Doctrine 2


125

Ho il seguente codice che mi dà l'errore:

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

Codice:

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();
}

Dati (o $ ids):

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

Risultato DQL:

q = SELECT COUNT(r.id) FROM \My\Entity\Rating r WHERE r.winner IN('?1')

1
Penso che questo sia il modo consigliato docs.doctrine-project.org/projects/doctrine-dbal/en/latest/…
martin

Risposte:


114

Durante la ricerca di questo problema, ho trovato qualcosa che sarà importante per chiunque si imbatta nello stesso problema e cerca una soluzione.

Dal post originale, la seguente riga di codice:

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

Il wrapping del parametro denominato come matrice causa il problema del numero di parametro associato. Rimuovendolo dal suo wrapping dell'array:

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

Questo problema dovrebbe essere risolto. Questo potrebbe essere stato un problema nelle versioni precedenti di Doctrine, ma è stato risolto nelle versioni più recenti della 2.0.


5
Penso che $qb->expr()->in()sia solo in Doctrine 2 ORM, ma non in Doctrine DBAL.
martin

3
$qb->expr()->in()è effettivamente in DBAL
JamesHalsall

345

Il modo più semplice per farlo è associare l'array stesso come parametro:

$queryBuilder->andWhere('r.winner IN (:ids)')
             ->setParameter('ids', $ids);

41
Non solo, ma a partire dal 2.1
Maciej Pyszyński

7
@ MaciejPyszyński +1. I modi più semplici sono spesso i migliori!
Andrzej Ośmiałowski

2
Menzione rapida: funziona di default con -> setParameter ('ids', $ ids) ma non con -> setParameters ('ids' => $ ids). Mi ci sono voluti alcuni minuti per il debug.
larrydahooster

3
fare è lavorare con -> setParameters (...) ->where('b.status IN (:statuses)') ->setParameters([ 'customerId' => $customerId, 'storeId' => $storeId, 'statuses' => [Status::OPEN, Status::AWAITING_APPROVAL, Status::APPROVED] ]);
George Mylonas

5
Vorrei sottolineare l'importanza di passare anche il 3 ° parametro setParameteralla forzaConnection::PARAM_STR_ARRAY
Luc Wollants

58

e per completare la soluzione di stringa

$qb->andWhere('foo.field IN (:string)');
$qb->setParameter('string', array('foo', 'bar'), \Doctrine\DBAL\Connection::PARAM_STR_ARRAY);

Sicuramente la soluzione migliore per me :-)
Francesco Casula

3
Puoi anche usare \ Doctrine \ DBAL \ Connection :: PARAM_INT_ARRAY se hai un array di numeri interi non stringhe.
Omn


12

So che è un vecchio post ma potrebbe essere utile per qualcuno. Voterei e migliorerei la risposta di @Daniel Espendiller affrontando la domanda posta nei commenti sugli int

Per fare in modo che funzioni correttamente per gli int, assicurati che i valori in array siano di tipo int, puoi digitare cast in int prima di passare ...

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

Testato per selezionare / eliminare in symfony 3.4 e doctrine-bundle: 1.8


8

So che l'esempio dell'OP utilizza DQL e il generatore di query, ma mi sono imbattuto in questo alla ricerca di come farlo da un controller o al di fuori della classe del repository, quindi forse questo aiuterà gli altri.

Puoi anche eseguire un'operazione WHERE INdal controller in questo modo:

// Symfony example
$ids    = [1, 2, 3, 4];
$repo   = $this->getDoctrine()->getRepository('AppBundle:RepoName');
$result = $repo->findBy([
    'id' => $ids
]);

1
Questo è un modo perfettamente accettabile per fare un where in senza usare DQL, ma la sua domanda era in riferimento al suo codice DQL. Sta facendo più di un semplice dammi tutte le cose basate su questi ID.
spetz83

6

Il modo migliore per farlo, soprattutto se stai aggiungendo più di una condizione, è:

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

Se il tuo array di valori contiene stringhe, non puoi usare il metodo setParameter con una stringa implosa, perché le virgolette verranno sfuggite!


6

Ecco come l'ho usato:

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

5

Ho trovato come farlo nell'anno 2016: https://redbeardtechnologies.wordpress.com/2011/07/01/doctrine-2-dql-in-statement/

Citazione:

Ecco come farlo correttamente:

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

Il metodo setParametersprenderà l'array dato e lo imploderà in modo appropriato per essere utilizzato nell'istruzione "IN".


2
Questo ha risolto il mio problema (le parentesi intorno :userids)
Mihai Răducanu,

2

Preferisco:

$qb->andWhere($qb->expr()->in('t.user_role_id', [
    User::USER_ROLE_ID_ADVERTISER,
    User::USER_ROLE_ID_MANAGER,
]));

0
->where($qb->expr()->in('foo.bar', ':data'))
            ->setParameter('participants', $data);

Funziona anche con:

 ->andWhere($qb->expr()->in('foo.bar', ':users'))
                ->setParameter('data', $data);

0

Ho lottato con questo stesso scenario in cui ho dovuto eseguire una query su una serie di valori.

Quanto segue ha funzionato per me:

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

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

Esempio di dati di matrice (lavorato con stringhe e numeri interi):

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

Esempio di query (adatta a dove ti serve):

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

$q->execute();

0

Sono passati anni, lavorando su un sito legacy ... Per quanto mi riguarda, non sono riuscito a far funzionare le soluzioni ->andWhere()o ->expr()->in().

Alla fine ho guardato nel repository Doctrine mongodb-odb e ho trovato alcuni test molto rivelatori:

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());
}

Ha funzionato per me!

Puoi trovare i test su GitHub qui . Utile per chiarire ogni sorta di assurdità.

Nota: la mia configurazione utilizza Doctrine MongoDb ODM v1.0.dev per quanto posso capire.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.