diff --git a/docs/en/reference/working-with-associations.rst b/docs/en/reference/working-with-associations.rst index 0b80580669..ab588e4efb 100644 --- a/docs/en/reference/working-with-associations.rst +++ b/docs/en/reference/working-with-associations.rst @@ -716,6 +716,7 @@ methods: * ``in($field, array $values)`` * ``notIn($field, array $values)`` * ``contains($field, $value)`` +* ``memberOf($value, $field)`` * ``startsWith($field, $value)`` * ``endsWith($field, $value)`` diff --git a/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php b/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php index 8b00d998ea..0381ac037d 100644 --- a/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php +++ b/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php @@ -186,6 +186,8 @@ public function walkComparison(Comparison $comparison) $this->parameters[] = $parameter; return $this->expr->like($field, $placeholder); + case Comparison::MEMBER_OF: + return $this->expr->isMemberOf($comparison->getField(), $comparison->getValue()->getValue()); case Comparison::STARTS_WITH: $parameter->setValue($parameter->getValue() . '%', $parameter->getType()); $this->parameters[] = $parameter; diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7737Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7737Test.php new file mode 100644 index 0000000000..25207b44e4 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7737Test.php @@ -0,0 +1,100 @@ +setUpEntitySchema([GH7737Group::class, GH7737Person::class]); + + $group1 = new GH7737Group(1, 'Test 1'); + $person = new GH7737Person(1); + $person->groups->add($group1); + + $this->_em->persist($person); + $this->_em->persist($group1); + $this->_em->persist(new GH7737Group(2, 'Test 2')); + $this->_em->flush(); + $this->_em->clear(); + } + + /** + * @test + */ + public function memberOfCriteriaShouldBeCompatibleWithQueryBuilder() : void + { + $query = $this->_em->createQueryBuilder() + ->select('person') + ->from(GH7737Person::class, 'person') + ->addCriteria(Criteria::create()->where(Criteria::expr()->memberOf(':group', 'person.groups'))) + ->getQuery(); + + $group1 = $this->_em->find(GH7737Group::class, 1); + $matching = $query->setParameter('group', $group1)->getOneOrNullResult(); + + self::assertInstanceOf(GH7737Person::class, $matching); + self::assertSame(1, $matching->id); + + $group2 = $this->_em->find(GH7737Group::class, 2); + $notMatching = $query->setParameter('group', $group2)->getOneOrNullResult(); + + self::assertNull($notMatching); + } +} + +/** + * @Entity + */ +class GH7737Group +{ + /** + * @Id + * @Column(type="integer") + */ + public $id; + + /** @Column */ + public $name; + + public function __construct(int $id, string $name) + { + $this->id = $id; + $this->name = $name; + } +} + +/** + * @Entity + */ +class GH7737Person +{ + /** + * @Id + * @Column(type="integer") + */ + public $id; + + /** + * @ManyToMany(targetEntity=GH7737Group::class) + * @JoinTable(inverseJoinColumns={@JoinColumn(name="group_id", referencedColumnName="id", unique=true)}) + */ + public $groups; + + public function __construct(int $id) + { + $this->id = $id; + $this->groups = new ArrayCollection(); + } +} diff --git a/tests/Doctrine/Tests/ORM/Query/QueryExpressionVisitorTest.php b/tests/Doctrine/Tests/ORM/Query/QueryExpressionVisitorTest.php index 0e306ab06e..27dce7d6d9 100644 --- a/tests/Doctrine/Tests/ORM/Query/QueryExpressionVisitorTest.php +++ b/tests/Doctrine/Tests/ORM/Query/QueryExpressionVisitorTest.php @@ -67,6 +67,7 @@ public function comparisonData() [$cb->notIn('field', ['value']), $qb->notIn('o.field', ':field'), new Parameter('field', ['value'])], [$cb->contains('field', 'value'), $qb->like('o.field', ':field'), new Parameter('field', '%value%')], + [$cb->memberOf(':field', 'o.field'), $qb->isMemberOf(':field', 'o.field')], [$cb->startsWith('field', 'value'), $qb->like('o.field', ':field'), new Parameter('field', 'value%')], [$cb->endsWith('field', 'value'), $qb->like('o.field', ':field'), new Parameter('field', '%value')],