Cómo usar WHERE IN con Doctrine 2

124

Tengo el siguiente código que me da el error:

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

Código:

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

Datos (o $ ids):

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

Resultado DQL:

q = SELECT COUNT(r.id) FROM \My\Entity\Rating r WHERE r.winner IN('?1')
Tjorriemorrie
fuente
1
Creo que esta es la forma recomendada docs.doctrine-project.org/projects/doctrine-dbal/en/latest/…
martin

Respuestas:

114

Al investigar este problema, encontré algo que será importante para cualquiera que se encuentre con este mismo problema y busque una solución.

De la publicación original, la siguiente línea de código:

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

Ajustar el parámetro con nombre como una matriz causa el problema del número del parámetro vinculado. Al eliminarlo de su envoltorio de matriz:

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

Este problema debe ser solucionado. Esto podría haber sido un problema en versiones anteriores de Doctrine, pero se solucionó en las versiones más recientes de 2.0.

Buster Neece
fuente
55
Creo que $qb->expr()->in()solo está en Doctrine 2 ORM, pero no en Doctrine DBAL.
Martin
3
$qb->expr()->in()está de hecho en DBAL
JamesHalsall
343

La forma más fácil de hacerlo es enlazando la matriz como un parámetro:

$queryBuilder->andWhere('r.winner IN (:ids)')
             ->setParameter('ids', $ids);
Maciej Pyszyński
fuente
41
No solo sino a partir de 2.1
Maciej Pyszyński
77
@ MaciejPyszyński +1. ¡Las formas más fáciles son a menudo las mejores!
Andrzej Ośmiałowski
2
Mención rápida: esto funciona por defecto con -> setParameter ('ids', $ ids) pero no con -> setParameters ('ids' => $ ids). Me tomó algunos minutos de depuración.
larrydahooster
3
hacer es trabajar con -> setParameters (...) ->where('b.status IN (:statuses)') ->setParameters([ 'customerId' => $customerId, 'storeId' => $storeId, 'statuses' => [Status::OPEN, Status::AWAITING_APPROVAL, Status::APPROVED] ]);
George Mylonas
55
Me gustaría señalar la importancia de pasar también el tercer parámetro setParametera forzarConnection::PARAM_STR_ARRAY
Luc Wollants
58

y para completar la solución de cadena

$qb->andWhere('foo.field IN (:string)');
$qb->setParameter('string', array('foo', 'bar'), \Doctrine\DBAL\Connection::PARAM_STR_ARRAY);
Daniel Espendiller
fuente
Definitivamente la mejor solución para mí :-)
Francesco Casula
3
También puede usar \ Doctrine \ DBAL \ Connection :: PARAM_INT_ARRAY si tiene una matriz de enteros, no cadenas.
Omn
12

Sé que es una publicación antigua, pero puede ser útil para alguien. Yo votaría y mejoraría la respuesta de @Daniel Espendiller abordando la pregunta que se hace en los comentarios sobre ints

Para que esto funcione para int de manera adecuada, asegúrese de que los valores en la matriz sean del tipo int, puede escribir cast to int antes de pasar ...

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

Probado para seleccionar / eliminar en Symfony 3.4 y doctrine-bundle: 1.8

Azhar Khattak
fuente
8

Sé que el ejemplo del OP está usando DQL y el generador de consultas, pero me topé con esto buscando cómo hacerlo desde un controlador o fuera de la clase de repositorio, por lo que tal vez esto ayude a otros.

También puede hacer una WHERE INdesde el controlador de esta manera:

// Symfony example
$ids    = [1, 2, 3, 4];
$repo   = $this->getDoctrine()->getRepository('AppBundle:RepoName');
$result = $repo->findBy([
    'id' => $ids
]);
Si barry
fuente
1
Esa es una manera perfectamente aceptable de hacer un lugar sin usar DQL, pero su pregunta fue en referencia a su código DQL. Él está haciendo más que un simple darme todas las cosas basadas en estas identificaciones.
spetz83
6

La mejor manera de hacerlo, especialmente si agrega más de una condición, es:

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

Si su matriz de valores contiene cadenas, no puede usar el método setParameter con una cadena implosionada, ¡porque sus comillas se escaparán!

ck1
fuente
6

Así es como lo usé:

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

Encontró cómo hacerlo en el año 2016: https://redbeardtechnologies.wordpress.com/2011/07/01/doctrine-2-dql-in-statement/

Citar:

Aquí está cómo hacerlo correctamente:

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

El método setParameterstomará la matriz dada y la implosionará adecuadamente para usarla en la declaración "IN".

Calamity Jane
fuente
2
Esto resolvió mi problema (los paréntesis :userids)
Mihai Răducanu
2

Yo prefiero:

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

También funciona con:

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

Luché con este mismo escenario en el que tuve que hacer una consulta contra una matriz de valores.

Lo siguiente funcionó para mí:

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

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

Ejemplo de datos de matriz (trabajado con cadenas y enteros):

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

Ejemplo de consulta (Adaptarse a donde lo necesita):

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

$q->execute();
Mortolian
fuente
0

Se trata de años más tarde, trabajando en un sitio de memoria ... Para la vida de mí no podía conseguir la ->andWhere()o ->expr()->in()las soluciones de trabajo.

Finalmente busqué en el repositorio de Doctrine mongodb-odb y encontré algunas pruebas muy reveladoras:

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

¡Funcionó para mí!

Puedes encontrar las pruebas en github aquí . Útil para aclarar todo tipo de tonterías.

Nota: Mi configuración está usando Doctrine MongoDb ODM v1.0.dev hasta donde puedo entender.

chichilatte
fuente