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

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.


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

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

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


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

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!


6

Así es como lo usé:

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

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


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

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

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

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.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.