Як використовувати WHERE IN з доктриною 2


124

У мене є такий код, який дає мені помилку:

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

Дані (або $ id):

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

Результат DQL:

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

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()це лише в доктрині 2 ORM, але не в доктрині DBAL.
Мартін

3
$qb->expr()->in()насправді в DBAL
JamesHalsall

344

Найпростіший спосіб зробити це - прив’язати масив як параметр:

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

41
Не тільки, але починаючи з 2.1
Мацей Пішинський

7
@ MaciejPyszyński +1. Найпростіші способи часто є найкращими!
Анджей Ośmiałowski

2
Швидка згадка: це працює за замовчуванням з -> setParameter ('ids', $ ids), але не з -> setParameters ('ids' => $ ids). Мені потрібні кілька хвилин налагодження.
larrydahooster

3
зробити - це робота з -> 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);

Однозначно найкраще рішення для мене :-)
Франческо Касула

3
Можна також використовувати \ Doctrine \ DBAL \ Connection :: PARAM_INT_ARRAY, якщо у вас масив цілих чисел, а не рядків.
Омн


12

Я знаю, що це стара публікація, але може бути корисною для когось. Я б проголосував і покращив відповідь @Daniel Espendiller, звернувшись до питання, заданого в коментарях про ints

Щоб зробити цю роботу для int's належним чином, переконайтеся, що значення масиву мають тип int, ви можете ввести cast до int перед тим, як пройти ...

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

Тестовано для вибору / видалення у програмі symfony 3.4 та доктрині: 1.8


8

Я знаю, що в прикладі ОП використовується 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 з вбудованою рядком, оскільки ваші лапки будуть уникнуті!


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/

Цитата:

Ось як це зробити правильно:

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

Метод setParametersвізьме заданий масив та імпровізує його належним чином для використання в операторі "IN".


2
Це вирішило мою проблему (круглі дужки навколо :userids)
Mihai Răducanu

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

0

Це роки, працюючи над успадкованим сайтом ... Для життя мене я не міг отримати ->andWhere()або ->expr()->in()рішення працює.

Нарешті заглянув у doctrine mongodb-odb repo і виявив кілька дуже показових тестів:

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, наскільки я можу розібратися.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.