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')

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.


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

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

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


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

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!


6

Tak to wykorzystałem:

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

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”.


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,
]));

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

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

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ć.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.