Cara menggunakan WHERE IN dengan Doktrin 2

125

Saya memiliki kode berikut yang memberi saya kesalahan:

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

Kode:

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

Data (atau $ ids):

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

Hasil DQL:

q = SELECT COUNT(r.id) FROM \My\Entity\Rating r WHERE r.winner IN('?1')
Tjorriemorrie
sumber
1
Saya pikir ini adalah cara yang disarankan docs.doctrine-project.org/projects/doctrine-dbal/en/latest/…
martin

Jawaban:

114

Dalam meneliti masalah ini, saya menemukan sesuatu yang akan menjadi penting bagi siapa pun yang mengalami masalah yang sama ini dan mencari solusi.

Dari postingan asli, baris kode berikut:

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

Membungkus parameter bernama sebagai array menyebabkan masalah nomor parameter terikat. Dengan menghapusnya dari pembungkus lariknya:

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

Masalah ini harus diperbaiki. Ini mungkin menjadi masalah di versi Doktrin sebelumnya, tetapi telah diperbaiki di versi terbaru 2.0.

Buster Neece
sumber
5
Menurut saya $qb->expr()->in()hanya ada di Doctrine 2 ORM, tapi tidak di Doctrine DBAL.
martin
3
$qb->expr()->in()memang ada di DBAL
JamesHalsall
345

Cara termudah untuk melakukannya adalah dengan mengikat array itu sendiri sebagai parameter:

$queryBuilder->andWhere('r.winner IN (:ids)')
             ->setParameter('ids', $ids);
Maciej Pyszyński
sumber
41
Tidak hanya tetapi mulai dari 2.1
Maciej Pyszyński
7
@ MaciejPzyński +1. Cara termudah seringkali merupakan cara terbaik!
Andrzej Ośmiałowski
2
Sebutan cepat: Ini berfungsi secara default dengan -> setParameter ('ids', $ ids) tetapi tidak dengan -> setParameters ('ids' => $ ids). Butuh beberapa menit untuk debugging.
larrydahooster
3
membuatnya bekerja dengan -> setParameters (...) ->where('b.status IN (:statuses)') ->setParameters([ 'customerId' => $customerId, 'storeId' => $storeId, 'statuses' => [Status::OPEN, Status::AWAITING_APPROVAL, Status::APPROVED] ]);
George Mylonas
5
Saya ingin menunjukkan pentingnya juga meneruskan parameter ke-3 setParameteruntuk memaksaConnection::PARAM_STR_ARRAY
Luc Wollants
58

dan untuk menyelesaikan solusi string

$qb->andWhere('foo.field IN (:string)');
$qb->setParameter('string', array('foo', 'bar'), \Doctrine\DBAL\Connection::PARAM_STR_ARRAY);
Daniel Espendiller
sumber
Jelas solusi terbaik untuk saya :-)
Francesco Casula
3
Bisa juga menggunakan \ Doctrine \ DBAL \ Connection :: PARAM_INT_ARRAY jika Anda memiliki larik bilangan bulat bukan string.
Omn
12

Saya tahu ini adalah posting lama tetapi mungkin berguna untuk seseorang. Saya akan memilih & meningkatkan jawaban @Daniel Espendiller dengan menjawab pertanyaan yang diajukan dalam komentar tentang int

Untuk membuat ini berfungsi untuk int dengan cara yang tepat, pastikan nilai dalam array bertipe int, Anda dapat mengetik cast to int sebelum meneruskan ...

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

Diuji untuk memilih / menghapus di symfony 3.4 & doktrin-bundle: 1.8

Azhar Khattak
sumber
8

Saya tahu contoh OP menggunakan DQL dan pembuat kueri, tetapi saya menemukan ini mencari cara melakukannya dari pengontrol atau di luar kelas repositori, jadi mungkin ini akan membantu orang lain.

Anda juga dapat melakukan a WHERE INdari pengontrol dengan cara ini:

// Symfony example
$ids    = [1, 2, 3, 4];
$repo   = $this->getDoctrine()->getRepository('AppBundle:RepoName');
$result = $repo->findBy([
    'id' => $ids
]);
Ya Barry
sumber
1
Itu adalah cara yang dapat diterima untuk melakukan di mana tanpa menggunakan DQL, tetapi pertanyaannya mengacu pada kode DQL-nya. Dia melakukan lebih dari sekedar memberi saya semua hal sederhana berdasarkan ID ini.
spetz83
6

Cara terbaik melakukan ini - terutama jika Anda menambahkan lebih dari satu ketentuan - adalah:

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

Jika larik nilai Anda berisi string, Anda tidak dapat menggunakan metode setParameter dengan string yang di-implode, karena tanda kutip Anda akan di-escape!

ck1
sumber
6

Beginilah cara saya menggunakannya:

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

Menemukan cara melakukannya di tahun 2016: https://redbeardtechnologies.wordpress.com/2011/07/01/doctrine-2-dql-in-statement/

Mengutip:

Berikut cara melakukannya dengan benar:

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

Metode ini setParametersakan mengambil array yang diberikan dan mengimpode dengan benar untuk digunakan dalam pernyataan "IN".

Bencana Jane
sumber
2
Ini memecahkan masalah saya (di sekitar tanda kurung :userids)
Mihai Răducanu
2

Saya lebih memilih:

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

Juga bekerja dengan:

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

Saya berjuang dengan skenario yang sama di mana saya harus melakukan kueri terhadap berbagai nilai.

Yang berikut berhasil untuk saya:

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

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

Contoh data array (bekerja dengan string dan integer):

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

Contoh kueri (Sesuaikan dengan kebutuhan Anda):

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

$q->execute();
Mortolian
sumber
0

Ini bertahun-tahun kemudian, mengerjakan situs warisan ... Selama hidup saya, saya tidak bisa mendapatkan solusi ->andWhere()atau ->expr()->in()bekerja.

Akhirnya melihat di Doctrine mongodb-odb repo dan menemukan beberapa tes yang sangat terbuka:

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

Itu berhasil untuk saya!

Anda dapat menemukan pengujian di github di sini . Berguna untuk mengklarifikasi segala macam omong kosong.

Catatan: Setup saya menggunakan Doctrine MongoDb ODM v1.0.dev sejauh yang saya bisa.

chichilatte
sumber