How to use WHERE IN with Doctrine 2
Asked Answered
W

14

157

I have the following code which gives me the error:

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

Data (or $ids):

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

DQL result:

q = SELECT COUNT(r.id) FROM \My\Entity\Rating r WHERE r.winner IN('?1')
Waltraudwaltz answered 8/5, 2011 at 17:26 Comment(1)
I think this is the recommended way docs.doctrine-project.org/projects/doctrine-dbal/en/latest/…Welton
C
128

In researching this issue, I found something that will be important to anyone running into this same issue and looking for a solution.

From the original post, the following line of code:

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

Wrapping the named parameter as an array causes the bound parameter number issue. By removing it from its array wrapping:

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

This issue should be fixed. This might have been a problem in previous versions of Doctrine, but it is fixed in the most recent versions of 2.0.

Covered answered 12/6, 2011 at 1:50 Comment(2)
I think $qb->expr()->in() is only in Doctrine 2 ORM, but not in Doctrine DBAL.Welton
$qb->expr()->in() is indeed in DBALPicaresque
P
420

The easiest way to do that is by binding the array itself as a parameter:

$queryBuilder->andWhere('r.winner IN (:ids)')
             ->setParameter('ids', $ids);
Prady answered 8/8, 2012 at 22:42 Comment(10)
Not only but starting from 2.1Nephridium
@MaciejPyszyński +1. The easiest ways are often the best ones!Michelinemichell
Quick mention: This works per default with ->setParameter('ids', $ids) but not with ->setParameters('ids' => $ids). Took me some minutes of debugging.Intimist
to make is work with ->setParameters(...) ->where('b.status IN (:statuses)') ->setParameters([ 'customerId' => $customerId, 'storeId' => $storeId, 'statuses' => [Status::OPEN, Status::AWAITING_APPROVAL, Status::APPROVED] ]);Antigorite
I would like to point out the importance of also passing the 3th parameter to setParameter to force Connection::PARAM_STR_ARRAYSaltandpepper
@MaciejPyszyński that was my friend, extraordinary!Westleigh
The easiest way for sure!Ecumenical
dislike cause i'll got something like this: r.winner IN ('1, 2, 3, 4, 5')Balustrade
@VladimirCh It won't just pass an array not a stringNephridium
@VladmirCh see the comment from @Luc Wollants. That will signal doctrine to format the parameter correctly r.winner IN (1, 2, 3, 4, 5)Founder
C
128

In researching this issue, I found something that will be important to anyone running into this same issue and looking for a solution.

From the original post, the following line of code:

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

Wrapping the named parameter as an array causes the bound parameter number issue. By removing it from its array wrapping:

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

This issue should be fixed. This might have been a problem in previous versions of Doctrine, but it is fixed in the most recent versions of 2.0.

Covered answered 12/6, 2011 at 1:50 Comment(2)
I think $qb->expr()->in() is only in Doctrine 2 ORM, but not in Doctrine DBAL.Welton
$qb->expr()->in() is indeed in DBALPicaresque
U
81

and for completion the string solution

$qb->andWhere('foo.field IN (:string)');
$qb->setParameter('string', array('foo', 'bar'), \Doctrine\DBAL\Connection::PARAM_STR_ARRAY);
Univalve answered 21/5, 2014 at 11:36 Comment(2)
Can also use \Doctrine\DBAL\Connection::PARAM_INT_ARRAY if you have an array of integers not strings.Sardinian
\Doctrine\DBAL\Connection::PARAM_STR_ARRAY is currently deprecated, use \Doctrine\DBAL\ArrayParameterType::STRING insteadSnare
B
28

I found that, despite what the docs indicate, the only way to get this to work is like this:

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

http://groups.google.com/group/doctrine-dev/browse_thread/thread/fbf70837293676fb

Bosk answered 10/5, 2011 at 14:21 Comment(0)
C
24

I know it's an old post but may be helpful for someone. I would vote & enhance @Daniel Espendiller answer by addressing the question asked in comments about ints

To make this work for int's in proper way, make sure the values in array are of type int, you can type cast to int before passing...

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

Tested for select/delete in symfony 3.4 & doctrine-bundle: 1.8

Corinnacorinne answered 26/3, 2018 at 15:0 Comment(0)
W
12

I know the OP's example is using DQL and the query builder, but I stumbled upon this looking for how to do it from a controller or outside of the repository class, so maybe this will help others.

You can also do a WHERE IN from the controller this way:

// Symfony example
$ids    = [1, 2, 3, 4];
$repo   = $this->getDoctrine()->getRepository('AppBundle:RepoName');
$result = $repo->findBy([
    'id' => $ids
]);
Wattmeter answered 24/1, 2017 at 20:26 Comment(1)
That is a perfectly acceptable way to do a where in without using DQL, but his question was in reference to his DQL code. He is doing more than just a simple give me all the things based on these IDs.Doctrinal
A
8

This is how I used it:

->where('b.status IN (:statuses)')
->setParameters([
                'customerId' => $customerId,
                'storeId'    => $storeId,
                'statuses'   => [Status::OPEN, Status::AWAITING_APPROVAL, Status::APPROVED]
            ]);
Antigorite answered 9/2, 2016 at 16:2 Comment(0)
U
7

The best way doing this - especially if you're adding more than one condition - is:

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

If your array of values contains strings, you can't use the setParameter method with an imploded string, because your quotes will be escaped!

Unchaste answered 16/7, 2013 at 12:36 Comment(0)
B
7

Found how to do it in the year of 2016: https://redbeardtechnologies.wordpress.com/2011/07/01/doctrine-2-dql-in-statement/

Quote:

Here is how to do it properly:

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

The method setParameters will take the given array and implode it properly to be used in the “IN” statement.

Breastsummer answered 29/2, 2016 at 18:18 Comment(1)
This solved my problem (the parentheses around :userids )Szombathely
M
4
$qb->where($qb->expr()->in('r.winner', ':ids'))
    ->setParameter('ids', $ids);

Also works with:

$qb->andWhere($qb->expr()->in('r.winner', ':ids'))
    ->setParameter('ids', $ids);
Maddi answered 25/3, 2016 at 4:23 Comment(0)
I
2

I prefer:

$qb->andWhere($qb->expr()->in('t.user_role_id', [
    User::USER_ROLE_ID_ADVERTISER,
    User::USER_ROLE_ID_MANAGER,
]));
Ithaman answered 8/2, 2016 at 10:29 Comment(0)
H
0

I struggled with this same scenario where I had to do a query against an array of values.

The following worked for me:

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

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

Array data example (worked with strings and integers):

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

Query example (Adapt to where you need it):

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

$q->execute();
Hypnotism answered 7/2, 2017 at 7:49 Comment(0)
S
0

This is years later, working on a legacy site... For the life of me I couldn't get the ->andWhere() or ->expr()->in() solutions working.

Finally looked in the Doctrine mongodb-odb repo and found some very revealing tests:

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

It worked for me!

You can find the tests on github here. Useful for clarifying all sorts of nonsense.

Note: My setup is using Doctrine MongoDb ODM v1.0.dev as far as i can make out.

Swagsman answered 6/12, 2019 at 23:56 Comment(0)
U
0
$winnerIds = [1,3,5];
$qb->andWhere($qb->expr()->in('r.winner', ':winnerIds'))
   ->setParameter('winnerIds', $winnerIds)
;
Undersigned answered 25/11, 2021 at 19:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.