Como usar WHERE IN com Doutrina 2


124

Eu tenho o seguinte código que me dá o erro:

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

Dados (ou $ 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')

1
Eu acho que esta é a maneira recomendada docs.doctrine-project.org/projects/doctrine-dbal/en/latest/...
martin

Respostas:


114

Ao pesquisar esse problema, encontrei algo que será importante para quem se deparar com esse mesmo problema e procurar uma solução.

Na postagem original, a seguinte linha de código:

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

O agrupamento do parâmetro nomeado como uma matriz causa o problema do número do parâmetro vinculado. Removendo-o de seu empacotamento de matriz:

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

Este problema deve ser corrigido. Isso pode ter sido um problema nas versões anteriores do Doctrine, mas foi corrigido nas versões mais recentes do 2.0.


5
Eu acho que $qb->expr()->in()está apenas no Doctrine 2 ORM, mas não no Doctrine DBAL.
martin

3
$qb->expr()->in()está de fato em DBAL
JamesHalsall

343

A maneira mais fácil de fazer isso é vinculando a própria matriz como um parâmetro:

$queryBuilder->andWhere('r.winner IN (:ids)')
             ->setParameter('ids', $ids);

41
Não só, mas a partir de 2,1
Maciej Pyszyński

7
@ MaciejPyszyński +1. As maneiras mais fáceis são geralmente as melhores!
Andrzej Ośmiałowski

2
Menção rápida: Isso funciona por padrão com -> setParameter ('ids', $ ids), mas não com -> setParameters ('ids' => $ ids). Levei alguns minutos de depuração.
Larrydahooster 29/09/2015

3
que fazer é trabalhar com -> setParameters (...) ->where('b.status IN (:statuses)') ->setParameters([ 'customerId' => $customerId, 'storeId' => $storeId, 'statuses' => [Status::OPEN, Status::AWAITING_APPROVAL, Status::APPROVED] ]);
George Mylonas

5
Eu gostaria de salientar a importância de também passar o parâmetro 3ª para setParametera forçaConnection::PARAM_STR_ARRAY
Luc Wollants

58

e para concluir a solução de cadeia

$qb->andWhere('foo.field IN (:string)');
$qb->setParameter('string', array('foo', 'bar'), \Doctrine\DBAL\Connection::PARAM_STR_ARRAY);

Definitivamente a melhor solução para mim :-)
Francesco Casula

3
Também pode usar \ Doctrine \ DBAL \ Connection :: PARAM_INT_ARRAY se você tiver uma matriz de números inteiros e não cadeias.
Omn


12

Eu sei que é um post antigo, mas pode ser útil para alguém. Eu votaria e melhoraria a resposta de @Daniel Espendiller, abordando a pergunta feita nos comentários sobre ints

Para fazer isso funcionar corretamente para int's, verifique se os valores na matriz são do tipo int, você pode digitar cast para int antes de passar ...

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

Testado para selecionar / excluir no symfony 3.4 & doctrine-bundle: 1.8


8

Eu sei que o exemplo do OP está usando o DQL e o construtor de consultas, mas me deparei com isso procurando como fazê-lo a partir de um controlador ou fora da classe do repositório, então talvez isso ajude outras pessoas.

Você também pode fazer a WHERE INpartir do controlador desta maneira:

// Symfony example
$ids    = [1, 2, 3, 4];
$repo   = $this->getDoctrine()->getRepository('AppBundle:RepoName');
$result = $repo->findBy([
    'id' => $ids
]);

1
Essa é uma maneira perfeitamente aceitável de fazer um where in sem usar o DQL, mas sua pergunta estava relacionada ao seu código DQL. Ele está fazendo mais do que apenas um simples, me dê todas as coisas com base nessas identificações.
Spetz83 31/03/19

6

A melhor maneira de fazer isso - especialmente se você estiver adicionando mais de uma condição - é:

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

Se sua matriz de valores contiver cadeias, você não poderá usar o método setParameter com uma cadeia implodida, porque suas aspas serão escapadas!


6

Foi assim que eu o usei:

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

5

Descobriu como fazer isso no ano de 2016: https://redbeardtechnologies.wordpress.com/2011/07/01/doctrine-2-dql-in-statement/

Citar:

Aqui está como fazer isso corretamente:

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

O método setParameterspegará a matriz especificada e a implodirá adequadamente para ser usada na instrução "IN".


2
Isso resolveu meu problema (os parênteses :userids)
Mihai Răducanu

2

Eu prefiro:

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

Também trabalha com:

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

0

Lutei com esse mesmo cenário em que tive que fazer uma consulta contra uma matriz de valores.

O seguinte funcionou para mim:

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

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

Exemplo de dados de matriz (trabalhado com seqüências de caracteres e números inteiros):

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

Exemplo de consulta (adapte-se aonde você precisar):

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

$q->execute();

0

Isso foi anos depois, trabalhando em um site herdado ... Durante toda a minha vida, não consegui colocar as soluções ->andWhere()ou em ->expr()->in()funcionamento.

Finalmente, procurei no repositório Doctrine mongodb-odb e encontrei alguns testes muito reveladores:

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

Funcionou para mim!

Você pode encontrar os testes no github aqui . Útil para esclarecer todo tipo de bobagem.

Nota: Minha configuração está usando o Doctrine MongoDb ODM v1.0.dev, na medida do possível.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.