Skip to content

Commit cbde02f

Browse files
committed
Selectable should not call getters for initialized collections
1 parent bea454e commit cbde02f

File tree

1 file changed

+229
-0
lines changed

1 file changed

+229
-0
lines changed
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
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\Common\Collections\Expr\Comparison;
11+
use Doctrine\ORM\Mapping as ORM;
12+
use Doctrine\Tests\OrmFunctionalTestCase;
13+
use Generator;
14+
15+
class GH3591Test extends OrmFunctionalTestCase
16+
{
17+
protected function setUp(): void
18+
{
19+
parent::setUp();
20+
21+
$this->setUpEntitySchema([
22+
GH3591Entity::class,
23+
GH3591CollectionEntry::class,
24+
]);
25+
}
26+
27+
/**
28+
* @dataProvider provideFieldNamesAndInitializationState
29+
*/
30+
public function testMatchingApiOnUninitializedCollection(string $fieldName): void
31+
{
32+
$criteria = $this->createComparisonCriteria($fieldName, 'test value');
33+
$entity = $this->createAndLoadEntityWithCollectionEntry('test value');
34+
35+
$matches = $entity->entries->matching($criteria);
36+
37+
self::assertCount(1, $matches);
38+
}
39+
40+
/**
41+
* @dataProvider provideFieldNamesAndInitializationState
42+
*/
43+
public function testMatchingApiOnInitializedCollection(string $fieldName): void
44+
{
45+
$criteria = $this->createComparisonCriteria($fieldName, 'test value');
46+
$entity = $this->createAndLoadEntityWithCollectionEntry('test value');
47+
$entity->entries->initialize();
48+
49+
$matches = $entity->entries->matching($criteria);
50+
51+
self::assertCount(1, $matches);
52+
}
53+
54+
/**
55+
* @dataProvider provideFieldNamesAndInitializationState
56+
*/
57+
public function testMatchingApiOnUnpersistedCollection(string $fieldName): void
58+
{
59+
$criteria = $this->createComparisonCriteria($fieldName, 'test value');
60+
$entity = new GH3591Entity();
61+
$entry = new GH3591CollectionEntry('test value');
62+
$entity->addEntry($entry);
63+
64+
$matches = $entity->entries->matching($criteria);
65+
66+
self::assertCount(1, $matches);
67+
}
68+
69+
/**
70+
* @return Generator<string>
71+
*/
72+
public function provideFieldNamesAndInitializationState(): Generator
73+
{
74+
yield ['privateField'];
75+
yield ['protectedField'];
76+
yield ['publicField'];
77+
yield ['fieldWithAwkwardGetter'];
78+
yield ['duplicatePrivateField'];
79+
}
80+
81+
private function createAndLoadEntityWithCollectionEntry(string $entryFieldValue): GH3591Entity
82+
{
83+
$entity = new GH3591Entity();
84+
$entry = new GH3591CollectionEntry($entryFieldValue);
85+
86+
$entity->addEntry($entry);
87+
$this->_em->persist($entity);
88+
$this->_em->flush();
89+
$this->_em->clear();
90+
91+
return $this->_em->find(GH3591Entity::class, $entity->id);
92+
}
93+
94+
private function createComparisonCriteria(string $fieldName, string $expectedValue): Criteria
95+
{
96+
$expr = new Comparison($fieldName, '=', $expectedValue);
97+
$criteria = new Criteria();
98+
$criteria->where($expr);
99+
100+
return $criteria;
101+
}
102+
}
103+
104+
/**
105+
* @ORM\Entity()
106+
*/
107+
class GH3591Entity
108+
{
109+
/**
110+
* @ORM\Id
111+
* @ORM\Column(type="integer")
112+
* @ORM\GeneratedValue
113+
*
114+
* @var int
115+
*/
116+
public $id;
117+
118+
/**
119+
* @ORM\OneToMany(targetEntity="GH3591CollectionEntry", mappedBy="entity", cascade={"persist"})
120+
*
121+
* @var Collection<int, GH3591CollectionEntry>
122+
*/
123+
public $entries;
124+
125+
public function __construct()
126+
{
127+
$this->entries = new ArrayCollection();
128+
}
129+
130+
public function addEntry(GH3591CollectionEntry $child): void
131+
{
132+
if (! $this->entries->contains($child)) {
133+
$this->entries->add($child);
134+
$child->setEntity($this);
135+
}
136+
}
137+
}
138+
139+
/**
140+
* @ORM\MappedSuperclass()
141+
*/
142+
class GH3591CollectionEntrySuperclass
143+
{
144+
/**
145+
* @ORM\Column(type="string")
146+
*
147+
* @var string
148+
*/
149+
private $duplicatePrivateField;
150+
151+
public function __construct(string $duplicatePrivateField)
152+
{
153+
$this->duplicatePrivateField = $duplicatePrivateField;
154+
}
155+
}
156+
157+
/**
158+
* @ORM\Entity()
159+
*/
160+
class GH3591CollectionEntry extends GH3591CollectionEntrySuperclass
161+
{
162+
/**
163+
* @ORM\Id
164+
* @ORM\Column(type="integer")
165+
* @ORM\GeneratedValue
166+
*
167+
* @var int
168+
*/
169+
private $id;
170+
171+
/**
172+
* @ORM\ManyToOne(targetEntity="GH3591Entity", inversedBy="entries")
173+
*
174+
* @var GH3591Entity
175+
*/
176+
private $entity;
177+
178+
/**
179+
* @ORM\Column(type="string")
180+
*
181+
* @var string
182+
*/
183+
private $privateField;
184+
185+
/**
186+
* @ORM\Column(type="string")
187+
*
188+
* @var string
189+
*/
190+
protected $protectedField;
191+
192+
/**
193+
* @ORM\Column(type="string")
194+
*
195+
* @var string
196+
*/
197+
public $publicField;
198+
199+
/**
200+
* @ORM\Column(type="string")
201+
*
202+
* @var string
203+
*/
204+
public $fieldWithAwkwardGetter;
205+
206+
private $duplicatePrivateField;
207+
208+
public function __construct($value)
209+
{
210+
$this->privateField = $value;
211+
$this->protectedField = $value;
212+
$this->publicField = $value;
213+
$this->fieldWithAwkwardGetter = $value;
214+
215+
parent::__construct($value);
216+
$this->duplicatePrivateField = 'this is a transient field';
217+
}
218+
219+
public function setEntity(GH3591Entity $entity): void
220+
{
221+
$this->entity = $entity;
222+
$entity->addEntry($this);
223+
}
224+
225+
public function fieldWithAwkwardGetter(): string
226+
{
227+
return 'not what you expect' . $this->fieldWithAwkwardGetter;
228+
}
229+
}

0 commit comments

Comments
 (0)