Jak korzystać z WHERE IN w Doctrine 2

124

Mam następujący kod, który daje mi błąd:

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

Kod:

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

Dane (lub $ ids):

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

Wynik DQL:

q = SELECT COUNT(r.id) FROM \My\Entity\Rating r WHERE r.winner IN('?1')
Tjorriemorrie
źródło
1
Myślę, że jest to zalecany sposób docs.doctrine-project.org/projects/doctrine-dbal/en/latest/ ...
martin

Odpowiedzi:

114

Badając ten problem, znalazłem coś, co będzie ważne dla każdego, kto napotka ten sam problem i szuka rozwiązania.

Z oryginalnego posta następujący wiersz kodu:

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

Zawijanie nazwanego parametru jako tablicy powoduje problem z numerem powiązanego parametru. Usuwając go z opakowania tablicy:

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

Ten problem powinien zostać naprawiony. To mógł być problem w poprzednich wersjach Doctrine, ale został rozwiązany w najnowszych wersjach 2.0.

Buster Neece
źródło
5
Myślę, że $qb->expr()->in()jest tylko w Doctrine 2 ORM, ale nie w Doctrine DBAL.
martin
3
$qb->expr()->in()jest rzeczywiście w DBAL
JamesHalsall
344

Najłatwiej to zrobić, wiążąc tablicę jako parametr:

$queryBuilder->andWhere('r.winner IN (:ids)')
             ->setParameter('ids', $ids);
Maciej Pyszyński
źródło
41
Nie tylko, ale zaczynając od 2.1
Maciej Pyszyński
7
@ MaciejPyszyński +1. Najłatwiejsze sposoby są często najlepsze!
Andrzej Ośmiałowski
2
Krótka wzmianka: Domyślnie działa to z -> setParameter ('ids', $ ids), ale nie z -> setParameters ('ids' => $ ids). Debugowanie zajęło mi kilka minut.
larrydahooster
3
zrobić to praca z -> setParameters (...) ->where('b.status IN (:statuses)') ->setParameters([ 'customerId' => $customerId, 'storeId' => $storeId, 'statuses' => [Status::OPEN, Status::AWAITING_APPROVAL, Status::APPROVED] ]);
George Mylonas
5
Chciałbym zwrócić uwagę, jak ważne jest również przekazanie 3 parametru setParameterdo forceConnection::PARAM_STR_ARRAY
Luc Wollants
58

i na zakończenie rozwiązanie string

$qb->andWhere('foo.field IN (:string)');
$qb->setParameter('string', array('foo', 'bar'), \Doctrine\DBAL\Connection::PARAM_STR_ARRAY);
Daniel Espendiller
źródło
Zdecydowanie najlepsze rozwiązanie dla mnie :-)
Francesco Casula
3
Można również użyć \ Doctrine \ DBAL \ Connection :: PARAM_INT_ARRAY, jeśli masz tablicę liczb całkowitych, a nie łańcuchów.
Omn
12

Wiem, że to stary post, ale może być pomocny dla kogoś. Zagłosowałbym i ulepszyłbym odpowiedź @Daniel Espendiller, odpowiadając na pytanie zadane w komentarzach na temat int

Aby to działało dla int we właściwy sposób, upewnij się, że wartości w tablicy są typu int, możesz wpisać rzut na int przed przekazaniem ...

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

Przetestowane pod kątem zaznaczania / usuwania w symfony 3.4 i pakiecie doctrine: 1.8

Azhar Khattak
źródło
8

Wiem, że przykład OP używa DQL i konstruktora zapytań, ale natknąłem się na to, szukając, jak to zrobić z kontrolera lub poza klasą repozytorium, więc może to pomoże innym.

Możesz również zrobić WHERE INz kontrolera w ten sposób:

// Symfony example
$ids    = [1, 2, 3, 4];
$repo   = $this->getDoctrine()->getRepository('AppBundle:RepoName');
$result = $repo->findBy([
    'id' => $ids
]);
Tak, Barry
źródło
1
Jest to całkowicie akceptowalny sposób na zrobienie gdzie indziej bez użycia DQL, ale jego pytanie dotyczyło jego kodu DQL. Robi więcej niż tylko proste podanie mi wszystkich rzeczy na podstawie tych identyfikatorów.
spetz83
6

Najlepszym sposobem na to - zwłaszcza jeśli dodajesz więcej niż jeden warunek - jest:

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

Jeśli twoja tablica wartości zawiera ciągi, nie możesz użyć metody setParameter z implodowanym ciągiem, ponieważ twoje cudzysłowy zostaną zmienione!

ck1
źródło
6

Tak to wykorzystałem:

->where('b.status IN (:statuses)')
->setParameters([
                'customerId' => $customerId,
                'storeId'    => $storeId,
                'statuses'   => [Status::OPEN, Status::AWAITING_APPROVAL, Status::APPROVED]
            ]);
George Mylonas
źródło
5

Stwierdziliśmy, jak to zrobić w 2016 roku: https://redbeardtechnologies.wordpress.com/2011/07/01/doctrine-2-dql-in-statement/

Zacytować:

Oto jak to zrobić poprawnie:

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

Metoda setParametersweźmie podaną tablicę i prawidłowo ją imploduje w celu użycia w instrukcji „IN”.

Calamity Jane
źródło
2
To rozwiązało mój problem (w nawiasach :userids)
Mihai Răducanu
2

Wolę:

$qb->andWhere($qb->expr()->in('t.user_role_id', [
    User::USER_ROLE_ID_ADVERTISER,
    User::USER_ROLE_ID_MANAGER,
]));
mnv
źródło
0
->where($qb->expr()->in('foo.bar', ':data'))
            ->setParameter('participants', $data);

Współpracuje również z:

 ->andWhere($qb->expr()->in('foo.bar', ':users'))
                ->setParameter('data', $data);
John Smith
źródło
0

Zmagałem się z tym samym scenariuszem, w którym musiałem wykonać zapytanie dotyczące tablicy wartości.

Pracowały dla mnie:

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

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

Przykład danych tablicy (działał z ciągami znaków i liczbami całkowitymi):

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

Przykład zapytania (dostosuj do miejsca, w którym go potrzebujesz):

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

$q->execute();
Mortolian
źródło
0

To jest lata później, pracując nad starszą witryną ... Za całe życie nie mogłem dostać ->andWhere()lub->expr()->in() roztworów roboczych.

W końcu zajrzałem do Doctrine mongodb-odb repo i znalazłem kilka bardzo odkrywczych testów:

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

U mnie zadziałało!

Możesz znaleźć testy na github tutaj . Przydatne do wyjaśniania wszelkiego rodzaju bzdur.

Uwaga: Moja konfiguracja korzysta z Doctrine MongoDb ODM v1.0.dev, o ile mogę to zrozumieć.

chichilatte
źródło