Skip to content

Commit 9e37c78

Browse files
committed
Infer type from field instead of column
getTypeOfColumn() relies on getTypeOfField(), and does not suffer from mismatching issues caused by quoting, because you cannot quote a field. Since a field can be composite, that method returns an array, hence why we need to select the first element.
1 parent 3271d8f commit 9e37c78

File tree

2 files changed

+218
-1
lines changed

2 files changed

+218
-1
lines changed

lib/Doctrine/ORM/Persisters/Collection/ManyToManyPersister.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ public function loadCriteria(PersistentCollection $collection, Criteria $criteri
249249
$field = $this->quoteStrategy->getColumnName($name, $targetClass, $this->platform);
250250
$whereClauses[] = sprintf('te.%s %s ?', $field, $operator);
251251
$params[] = $value;
252-
$paramTypes[] = PersisterHelper::getTypeOfColumn($field, $targetClass, $this->em);
252+
$paramTypes[] = PersisterHelper::getTypeOfField($name, $targetClass, $this->em)[0];
253253
}
254254

255255
$tableName = $this->quoteStrategy->getTableName($targetClass, $this->platform);
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\Tests\ORM\Functional\Ticket;
6+
7+
use Doctrine\Common\Collections\ArrayCollection;
8+
use Doctrine\Common\Collections\Collection;
9+
use Doctrine\Common\Collections\Criteria;
10+
use Doctrine\ORM\Mapping\Column;
11+
use Doctrine\ORM\Mapping\Entity;
12+
use Doctrine\ORM\Mapping\GeneratedValue;
13+
use Doctrine\ORM\Mapping\Id;
14+
use Doctrine\ORM\Mapping\ManyToMany;
15+
use Doctrine\Tests\OrmFunctionalTestCase;
16+
17+
/**
18+
* @group GH-9109
19+
*/
20+
class GH9109Test extends OrmFunctionalTestCase
21+
{
22+
protected function setUp(): void
23+
{
24+
parent::setUp();
25+
26+
$this->_schemaTool->createSchema(
27+
[
28+
$this->_em->getClassMetadata(GH9109User::class),
29+
$this->_em->getClassMetadata(GH9109Product::class),
30+
]
31+
);
32+
}
33+
34+
protected function tearDown(): void
35+
{
36+
$this->_schemaTool->dropSchema(
37+
[
38+
$this->_em->getClassMetadata(GH9109User::class),
39+
$this->_em->getClassMetadata(GH9109Product::class),
40+
]
41+
);
42+
43+
parent::tearDown();
44+
}
45+
46+
public function testIssue(): void
47+
{
48+
$userFirstName = 'GH9109Test';
49+
$userLastName = 'UserGH9109';
50+
$productTitle = 'Test product';
51+
52+
$userRepository = $this->_em->getRepository(GH9109User::class);
53+
54+
$user = new GH9109User();
55+
$user->setFirstName($userFirstName);
56+
$user->setLastName($userLastName);
57+
58+
$product = new GH9109Product();
59+
$product->setTitle($productTitle);
60+
61+
$this->_em->persist($user);
62+
$this->_em->persist($product);
63+
$this->_em->flush();
64+
65+
$product->addBuyer($user);
66+
67+
$this->_em->persist($product);
68+
$this->_em->flush();
69+
70+
$this->_em->clear();
71+
72+
$persistedProduct = $this->_em->find(GH9109Product::class, $product->getId());
73+
74+
// assert Product was persisted
75+
self::assertInstanceOf(GH9109Product::class, $persistedProduct);
76+
self::assertEquals($productTitle, $persistedProduct->getTitle());
77+
78+
// assert Product has a Buyer
79+
$count = $persistedProduct->getBuyers()->count();
80+
self::assertEquals(1, $count);
81+
82+
// assert NOT QUOTED will WORK with findOneBy
83+
$user = $userRepository->findOneBy(['lastName' => $userLastName]);
84+
self::assertInstanceOf(GH9109User::class, $user);
85+
self::assertEquals($userLastName, $user->getLastName());
86+
87+
// assert NOT QUOTED will WORK with Criteria
88+
$criteria = Criteria::create();
89+
$criteria->where($criteria->expr()->eq('lastName', $userLastName));
90+
$user = $persistedProduct->getBuyers()->matching($criteria)->first();
91+
self::assertInstanceOf(GH9109User::class, $user);
92+
self::assertEquals($userLastName, $user->getLastName());
93+
94+
// assert QUOTED will WORK with findOneBy
95+
$user = $userRepository->findOneBy(['firstName' => $userFirstName]);
96+
self::assertInstanceOf(GH9109User::class, $user);
97+
self::assertEquals($userFirstName, $user->getFirstName());
98+
99+
// assert QUOTED will WORK with Criteria
100+
$criteria = Criteria::create();
101+
$criteria->where($criteria->expr()->eq('firstName', $userFirstName));
102+
$user = $persistedProduct->getBuyers()->matching($criteria)->first();
103+
self::assertInstanceOf(GH9109User::class, $user);
104+
self::assertEquals($userFirstName, $user->getFirstName());
105+
}
106+
}
107+
108+
/**
109+
* @Entity
110+
*/
111+
class GH9109Product
112+
{
113+
/**
114+
* @var int $id
115+
* @Column(name="`id`", type="integer")
116+
* @Id
117+
* @GeneratedValue(strategy="AUTO")
118+
*/
119+
private $id;
120+
121+
/**
122+
* @var string $title
123+
* @Column(name="`title`", type="string", length=255)
124+
*/
125+
private $title;
126+
127+
/**
128+
* @var Collection|GH9109User[]
129+
* @psalm-var Collection<int, GH9109User>
130+
* @ManyToMany(targetEntity="GH9109User")
131+
*/
132+
private $buyers;
133+
134+
public function __construct()
135+
{
136+
$this->buyers = new ArrayCollection();
137+
}
138+
139+
public function getId(): int
140+
{
141+
return $this->id;
142+
}
143+
144+
public function setTitle(string $title): void
145+
{
146+
$this->title = $title;
147+
}
148+
149+
public function getTitle(): string
150+
{
151+
return $this->title;
152+
}
153+
154+
/**
155+
* @psalm-return Collection<int, GH9109User>
156+
*/
157+
public function getBuyers(): Collection
158+
{
159+
return $this->buyers;
160+
}
161+
162+
public function addBuyer(GH9109User $buyer): void
163+
{
164+
$this->buyers[] = $buyer;
165+
}
166+
}
167+
168+
/**
169+
* @Entity
170+
*/
171+
class GH9109User
172+
{
173+
/**
174+
* @var int
175+
* @Column(name="`id`", type="integer")
176+
* @Id
177+
* @GeneratedValue(strategy="AUTO")
178+
*/
179+
private $id;
180+
181+
/**
182+
* @var string
183+
* @Column(name="`first_name`", type="string")
184+
*/
185+
private $firstName;
186+
187+
/**
188+
* @var string
189+
* @Column(name="last_name", type="string")
190+
*/
191+
private $lastName;
192+
193+
public function getId(): int
194+
{
195+
return $this->id;
196+
}
197+
198+
public function getFirstName(): string
199+
{
200+
return $this->firstName;
201+
}
202+
203+
public function setFirstName(string $firstName): void
204+
{
205+
$this->firstName = $firstName;
206+
}
207+
208+
public function getLastName(): string
209+
{
210+
return $this->lastName;
211+
}
212+
213+
public function setLastName(string $lastName): void
214+
{
215+
$this->lastName = $lastName;
216+
}
217+
}

0 commit comments

Comments
 (0)