Verwendung von WHERE IN mit Doctrine 2

124

Ich habe den folgenden Code, der mir den Fehler gibt:

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

Daten (oder $ ids):

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

DQL-Ergebnis:

q = SELECT COUNT(r.id) FROM \My\Entity\Rating r WHERE r.winner IN('?1')
Tjorriemorrie
quelle
1
Ich denke, dies ist der empfohlene Weg docs.doctrine-project.org/projects/doctrine-dbal/en/latest/…
Martin

Antworten:

114

Bei der Untersuchung dieses Problems habe ich etwas gefunden, das für jeden wichtig ist, der auf dasselbe Problem stößt und nach einer Lösung sucht.

Aus dem ursprünglichen Beitrag die folgende Codezeile:

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

Das Umschließen des benannten Parameters als Array verursacht das Problem mit der gebundenen Parameternummer. Durch Entfernen aus dem Array-Wrapping:

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

Dieses Problem sollte behoben sein. Dies war möglicherweise ein Problem in früheren Versionen von Doctrine, wurde jedoch in den neuesten Versionen von 2.0 behoben.

Buster Neece
quelle
5
Ich denke, $qb->expr()->in()ist nur in Doctrine 2 ORM, aber nicht in Doctrine DBAL.
Martin
3
$qb->expr()->in()ist in der Tat in DBAL
JamesHalsall
343

Der einfachste Weg, dies zu tun, besteht darin, das Array selbst als Parameter zu binden:

$queryBuilder->andWhere('r.winner IN (:ids)')
             ->setParameter('ids', $ids);
Maciej Pyszyński
quelle
41
Nicht nur, sondern ab 2.1
Maciej Pyszyński
7
@ MaciejPyszyński +1. Die einfachsten Wege sind oft die besten!
Andrzej Ośmiałowski
2
Kurze Erwähnung: Dies funktioniert standardmäßig mit -> setParameter ('ids', $ ids), jedoch nicht mit -> setParameters ('ids' => $ ids). Ich habe einige Minuten debuggt.
Larrydahooster
3
zu machen ist Arbeit mit -> setParameters (...) ->where('b.status IN (:statuses)') ->setParameters([ 'customerId' => $customerId, 'storeId' => $storeId, 'statuses' => [Status::OPEN, Status::AWAITING_APPROVAL, Status::APPROVED] ]);
George Mylonas
5
Ich möchte darauf hinweisen, wie wichtig es ist, auch den 3. Parameter setParameteran Force zu übergebenConnection::PARAM_STR_ARRAY
Luc Wollants
58

und zur Vervollständigung die String-Lösung

$qb->andWhere('foo.field IN (:string)');
$qb->setParameter('string', array('foo', 'bar'), \Doctrine\DBAL\Connection::PARAM_STR_ARRAY);
Daniel Espendiller
quelle
Auf jeden Fall die beste Lösung für mich :-)
Francesco Casula
3
Kann auch \ Doctrine \ DBAL \ Connection :: PARAM_INT_ARRAY verwenden, wenn Sie ein Array von Ganzzahlen und keine Zeichenfolgen haben.
Omn
12

Ich weiß, dass es ein alter Beitrag ist, kann aber für jemanden hilfreich sein. Ich würde die Antwort von @Daniel Espendiller abstimmen und verbessern, indem ich die Frage beantworte, die in Kommentaren zu Ints gestellt wird

Damit dies für Ints ordnungsgemäß funktioniert, stellen Sie sicher, dass die Werte im Array vom Typ int sind. Sie können cast to int eingeben, bevor Sie ...

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

Getestet auf Auswählen / Löschen in Symfony 3.4 & Doctrine-Bundle: 1.8

Azhar Khattak
quelle
8

Ich weiß, dass das Beispiel des OP DQL und den Abfrage-Generator verwendet, aber ich bin darauf gestoßen, wie man es von einem Controller oder außerhalb der Repository-Klasse aus macht. Vielleicht hilft dies anderen.

Sie können eine WHERE INvom Controller auch folgendermaßen ausführen:

// Symfony example
$ids    = [1, 2, 3, 4];
$repo   = $this->getDoctrine()->getRepository('AppBundle:RepoName');
$result = $repo->findBy([
    'id' => $ids
]);
Ja, Barry
quelle
1
Das ist ein völlig akzeptabler Weg, um ein Where-In ohne Verwendung von DQL durchzuführen, aber seine Frage bezog sich auf seinen DQL-Code. Er tut mehr als nur ein einfaches Geben Sie mir alle Dinge, die auf diesen IDs basieren.
spetz83
6

Der beste Weg, dies zu tun - insbesondere wenn Sie mehr als eine Bedingung hinzufügen - ist:

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

Wenn Ihr Wertearray Zeichenfolgen enthält, können Sie die setParameter-Methode nicht mit einer implodierten Zeichenfolge verwenden, da Ihre Anführungszeichen maskiert werden!

ck1
quelle
6

So habe ich es benutzt:

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

So wurde es im Jahr 2016 gemacht: https://redbeardtechnologies.wordpress.com/2011/07/01/doctrine-2-dql-in-statement/

Zitat:

So geht's richtig:

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

Die Methode setParametersnimmt das angegebene Array und implodiert es ordnungsgemäß, um es in der Anweisung "IN" zu verwenden.

Unglück Jane
quelle
2
Dies löste mein Problem (die Klammern herum :userids)
Mihai Răducanu
2

Ich bevorzuge:

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

Funktioniert auch mit:

 ->andWhere($qb->expr()->in('foo.bar', ':users'))
                ->setParameter('data', $data);
John Smith
quelle
0

Ich hatte mit demselben Szenario zu kämpfen, in dem ich eine Abfrage für ein Array von Werten durchführen musste.

Folgendes hat bei mir funktioniert:

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

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

Beispiel für Array-Daten (mit Zeichenfolgen und Ganzzahlen gearbeitet):

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

Abfragebeispiel (An den gewünschten Ort anpassen):

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

$q->execute();
Mortolian
quelle
0

Dies ist Jahre später, als ich an einer Legacy-Site arbeite ... Für mein Leben konnte ich die ->andWhere()oder ->expr()->in()Lösungen nicht zum Laufen bringen.

Schließlich schaute in der Doctrine Mongodb-Odb Repo und fand einige sehr aufschlussreiche 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());
}

Es hat bei mir funktioniert!

Die Tests auf Github finden Sie hier . Nützlich, um alle Arten von Unsinn zu klären.

Hinweis: Mein Setup verwendet Doctrine MongoDb ODM v1.0.dev, soweit ich das beurteilen kann.

Chichilatte
quelle