Как использовать WHERE IN с Doctrine 2

125

У меня есть следующий код, который дает мне ошибку:

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

Код:

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

Данные (или $ ids):

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

Результат DQL:

q = SELECT COUNT(r.id) FROM \My\Entity\Rating r WHERE r.winner IN('?1')
Tjorriemorrie
источник
1
Я думаю, что это рекомендуемый способ docs.doctrine-project.org/projects/doctrine-dbal/en/latest/…
Мартин

Ответы:

114

Изучая эту проблему, я обнаружил кое-что, что будет важно для всех, кто сталкивается с этой же проблемой и ищет решение.

Из исходного сообщения следующая строка кода:

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

Заключение именованного параметра в массив вызывает проблему с номером связанного параметра. Удалив его из оболочки массива:

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

Эта проблема должна быть исправлена. Это могло быть проблемой в предыдущих версиях Doctrine, но она исправлена ​​в самых последних версиях 2.0.

Бастер Нис
источник
5
Думаю, $qb->expr()->in()есть только в Doctrine 2 ORM, но не в Doctrine DBAL.
Мартин
3
$qb->expr()->in()действительно находится в DBAL
JamesHalsall
345

Самый простой способ сделать это - привязать сам массив как параметр:

$queryBuilder->andWhere('r.winner IN (:ids)')
             ->setParameter('ids', $ids);
Мацей Пышинский
источник
41
Не только, но и начиная с 2.1
Maciej Pyszyński
7
@ MaciejPyszyński +1. Самые простые способы часто оказываются лучшими!
Анджей Осмяловски
2
Краткое упоминание: по умолчанию это работает с -> setParameter ('ids', $ ids), но не с -> setParameters ('ids' => $ ids). У меня ушло несколько минут на отладку.
larrydahooster
3
to make is work with -> setParameters (...) ->where('b.status IN (:statuses)') ->setParameters([ 'customerId' => $customerId, 'storeId' => $storeId, 'statuses' => [Status::OPEN, Status::AWAITING_APPROVAL, Status::APPROVED] ]);
Джордж Милонас
5
Я хотел бы отметить важность передачи 3-го параметра setParameterв силуConnection::PARAM_STR_ARRAY
Люк Волланц
58

и для завершения струнное решение

$qb->andWhere('foo.field IN (:string)');
$qb->setParameter('string', array('foo', 'bar'), \Doctrine\DBAL\Connection::PARAM_STR_ARRAY);
Дэниел Эспендиллер
источник
Определенно лучшее решение для меня :-)
Francesco Casula
3
Также можно использовать \ Doctrine \ DBAL \ Connection :: PARAM_INT_ARRAY, если у вас есть массив целых чисел, а не строк.
Омн,
12

Я знаю, что это старый пост, но он может кому-то помочь. Я бы проголосовал и улучшил ответ @Daniel Espendiller, ответив на вопрос, заданный в комментариях о ints

Чтобы это работало для int должным образом, убедитесь, что значения в массиве имеют тип int, вы можете ввести cast в int перед передачей ...

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

Проверено на выбор / удаление в symfony 3.4 и doctrine-bundle: 1.8

Ажар Хаттак
источник
8

Я знаю, что пример OP использует DQL и построитель запросов, но я наткнулся на это, пытаясь понять, как это сделать с контроллера или вне класса репозитория, так что, возможно, это поможет другим.

Вы также можете сделать это WHERE INс контроллера следующим образом:

// Symfony example
$ids    = [1, 2, 3, 4];
$repo   = $this->getDoctrine()->getRepository('AppBundle:RepoName');
$result = $repo->findBy([
    'id' => $ids
]);
Да, Барри
источник
1
Это совершенно приемлемый способ сделать это без использования DQL, но его вопрос касался его кода DQL. Он делает больше, чем просто дает мне все на основе этих идентификаторов.
spetz83
6

Лучший способ сделать это - особенно если вы добавляете более одного условия - это:

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

Если ваш массив значений содержит строки, вы не можете использовать метод setParameter с развернутой строкой, потому что ваши кавычки будут экранированы!

ck1
источник
6

Вот как я это использовал:

->where('b.status IN (:statuses)')
->setParameters([
                'customerId' => $customerId,
                'storeId'    => $storeId,
                'statuses'   => [Status::OPEN, Status::AWAITING_APPROVAL, Status::APPROVED]
            ]);
Джордж Милонас
источник
5

Нашел как это сделать в 2016 году: https://redbeardtechnologies.wordpress.com/2011/07/01/doctrine-2-dql-in-statement/

Quote:

Вот как это сделать правильно:

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

Метод setParametersвозьмет данный массив и должным образом взорвет его, чтобы использовать в операторе «IN».

Каламити Джейн
источник
2
Это решило мою проблему (заключены скобки :userids)
Михай Рэдукану
2

Я предпочитаю:

$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);

Также работает с:

 ->andWhere($qb->expr()->in('foo.bar', ':users'))
                ->setParameter('data', $data);
Джон Смит
источник
0

Я боролся с тем же сценарием, когда мне приходилось делать запрос к массиву значений.

У меня сработало следующее:

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

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

Пример данных массива (работает со строками и целыми числами):

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

Пример запроса (адаптируйтесь к нужному месту):

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

$q->execute();
Mortolian
источник
0

Это годы спустя, когда я работаю над устаревшим сайтом ... За всю жизнь я не мог заставить работать ->andWhere()или ->expr()->in()решения.

Наконец, заглянул в репозиторий Doctrine mongodb-odb и нашел несколько очень показательных тестов:

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

У меня это сработало!

Вы можете найти тесты на github здесь . Полезно для выяснения всякой чуши.

Примечание. Насколько я могу понять, в моей настройке используется Doctrine MongoDb ODM v1.0.dev.

chichilatte
источник