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')
Tjorriemorrie
fonte
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.

Buster Neece
fonte
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);
Maciej Pyszyński
fonte
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);
Daniel Espendiller
fonte
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

Azhar Khattak
fonte
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
]);
Sim Barry
fonte
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!

ck1
fonte
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]
            ]);
George Mylonas
fonte
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".

Calamity Jane
fonte
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,
]));
mnv
fonte
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);
John Smith
fonte
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();
Mortolian
fonte
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.

chichilatte
fonte