Skip to content

Commit 91201c0

Browse files
authored
Merge pull request doctrine#11722 from doctrine/2.20.x
Merge 2.20.x up into 2.21.x
2 parents a4a15ad + 82e2c98 commit 91201c0

File tree

12 files changed

+421
-10
lines changed

12 files changed

+421
-10
lines changed

.github/workflows/continuous-integration.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ jobs:
119119
- "default"
120120
- "3@dev"
121121
postgres-version:
122-
- "15"
122+
- "17"
123123
extension:
124124
- pdo_pgsql
125125
- pgsql
@@ -381,7 +381,7 @@ jobs:
381381
path: "reports"
382382

383383
- name: "Upload to Codecov"
384-
uses: "codecov/codecov-action@v4"
384+
uses: "codecov/codecov-action@v5"
385385
with:
386386
directory: reports
387387
env:

docs/en/reference/unitofwork.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ How Doctrine Detects Changes
102102
----------------------------
103103

104104
Doctrine is a data-mapper that tries to achieve persistence-ignorance (PI).
105-
This means you map php objects into a relational database that don't
105+
This means you map PHP objects into a relational database that don't
106106
necessarily know about the database at all. A natural question would now be,
107107
"how does Doctrine even detect objects have changed?".
108108

docs/en/tutorials/extra-lazy-associations.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ can be called without triggering a full load of the collection:
1818
- ``Collection#containsKey($key)``
1919
- ``Collection#count()``
2020
- ``Collection#get($key)``
21+
- ``Collection#isEmpty()``
2122
- ``Collection#slice($offset, $length = null)``
2223

2324
For each of the above methods the following semantics apply:

psalm.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
<referencedClass name="Doctrine\ORM\Tools\Console\Command\GenerateRepositoriesCommand"/>
5151
<referencedClass name="Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper"/>
5252
<referencedClass name="Doctrine\ORM\Tools\Console\EntityManagerProvider\HelperSetManagerProvider"/>
53+
<referencedClass name="Doctrine\Persistence\Mapping\StaticReflectionService"/>
5354
</errorLevel>
5455
</DeprecatedClass>
5556
<DeprecatedConstant>

src/Events.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,16 +103,14 @@ private function __construct()
103103
* The onFlush event occurs when the EntityManager#flush() operation is invoked,
104104
* after any changes to managed entities have been determined but before any
105105
* actual database operations are executed. The event is only raised if there is
106-
* actually something to do for the underlying UnitOfWork. If nothing needs to be done,
107-
* the onFlush event is not raised.
106+
* actually something to do for the underlying UnitOfWork.
108107
*/
109108
public const onFlush = 'onFlush';
110109

111110
/**
112111
* The postFlush event occurs when the EntityManager#flush() operation is invoked and
113112
* after all actual database operations are executed successfully. The event is only raised if there is
114-
* actually something to do for the underlying UnitOfWork. If nothing needs to be done,
115-
* the postFlush event is not raised. The event won't be raised if an error occurs during the
113+
* actually something to do for the underlying UnitOfWork. The event won't be raised if an error occurs during the
116114
* flush operation.
117115
*/
118116
public const postFlush = 'postFlush';

src/Persisters/Entity/BasicEntityPersister.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ class BasicEntityPersister implements EntityPersister
199199
/** @var CachedPersisterContext */
200200
private $noLimitsContext;
201201

202+
/** @var ?string */
203+
private $filterHash = null;
204+
202205
/**
203206
* Initializes a new <tt>BasicEntityPersister</tt> that uses the given EntityManager
204207
* and persists instances of the class described by the given ClassMetadata descriptor.
@@ -1271,7 +1274,7 @@ final protected function getOrderBySQL(array $orderBy, string $baseTableAlias):
12711274
*/
12721275
protected function getSelectColumnsSQL()
12731276
{
1274-
if ($this->currentPersisterContext->selectColumnListSql !== null) {
1277+
if ($this->currentPersisterContext->selectColumnListSql !== null && $this->filterHash === $this->em->getFilters()->getHash()) {
12751278
return $this->currentPersisterContext->selectColumnListSql;
12761279
}
12771280

@@ -1378,6 +1381,7 @@ protected function getSelectColumnsSQL()
13781381
}
13791382

13801383
$this->currentPersisterContext->selectColumnListSql = implode(', ', $columnList);
1384+
$this->filterHash = $this->em->getFilters()->getHash();
13811385

13821386
return $this->currentPersisterContext->selectColumnListSql;
13831387
}

src/UnitOfWork.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2473,13 +2473,13 @@ private function doRefresh($entity, array &$visited, ?int $lockMode = null): voi
24732473
throw ORMInvalidArgumentException::entityNotManaged($entity);
24742474
}
24752475

2476+
$this->cascadeRefresh($entity, $visited, $lockMode);
2477+
24762478
$this->getEntityPersister($class->name)->refresh(
24772479
array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
24782480
$entity,
24792481
$lockMode
24802482
);
2481-
2482-
$this->cascadeRefresh($entity, $visited, $lockMode);
24832483
}
24842484

24852485
/**
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
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\ORM\Mapping as ORM;
9+
use Doctrine\ORM\Mapping\Column;
10+
use Doctrine\ORM\Mapping\Entity;
11+
use Doctrine\ORM\Mapping\GeneratedValue;
12+
use Doctrine\ORM\Mapping\Id;
13+
use Doctrine\Tests\OrmFunctionalTestCase;
14+
15+
class LazyEagerCollectionTest extends OrmFunctionalTestCase
16+
{
17+
protected function setUp(): void
18+
{
19+
parent::setUp();
20+
21+
$this->createSchemaForModels(
22+
LazyEagerCollectionUser::class,
23+
LazyEagerCollectionAddress::class,
24+
LazyEagerCollectionPhone::class
25+
);
26+
}
27+
28+
public function testRefreshRefreshesBothLazyAndEagerCollections(): void
29+
{
30+
$user = new LazyEagerCollectionUser();
31+
$user->data = 'Guilherme';
32+
33+
$ph = new LazyEagerCollectionPhone();
34+
$ph->data = '12345';
35+
$user->addPhone($ph);
36+
37+
$ad = new LazyEagerCollectionAddress();
38+
$ad->data = '6789';
39+
$user->addAddress($ad);
40+
41+
$this->_em->persist($user);
42+
$this->_em->persist($ad);
43+
$this->_em->persist($ph);
44+
$this->_em->flush();
45+
$this->_em->clear();
46+
47+
$user = $this->_em->find(LazyEagerCollectionUser::class, $user->id);
48+
$ph = $user->phones[0];
49+
$ad = $user->addresses[0];
50+
51+
$ph->data = 'abc';
52+
$ad->data = 'def';
53+
54+
$this->_em->refresh($user);
55+
56+
self::assertSame('12345', $ph->data);
57+
self::assertSame('6789', $ad->data);
58+
}
59+
}
60+
61+
/**
62+
* @Entity
63+
*/
64+
class LazyEagerCollectionUser
65+
{
66+
/**
67+
* @var int
68+
* @Id
69+
* @Column(type="integer")
70+
* @GeneratedValue(strategy="AUTO")
71+
*/
72+
public $id;
73+
74+
/**
75+
* @var string
76+
* @Column(type="string", length=255)
77+
*/
78+
public $data;
79+
80+
/**
81+
* @ORM\OneToMany(targetEntity="LazyEagerCollectionPhone", cascade={"refresh"}, fetch="EAGER", mappedBy="user")
82+
*
83+
* @var LazyEagerCollectionPhone[]
84+
*/
85+
public $phones;
86+
87+
/**
88+
* @ORM\OneToMany(targetEntity="LazyEagerCollectionAddress", cascade={"refresh"}, mappedBy="user")
89+
*
90+
* @var LazyEagerCollectionAddress[]
91+
*/
92+
public $addresses;
93+
94+
public function __construct()
95+
{
96+
$this->addresses = new ArrayCollection();
97+
$this->phones = new ArrayCollection();
98+
}
99+
100+
public function addPhone(LazyEagerCollectionPhone $phone): void
101+
{
102+
$phone->user = $this;
103+
$this->phones[] = $phone;
104+
}
105+
106+
public function addAddress(LazyEagerCollectionAddress $address): void
107+
{
108+
$address->user = $this;
109+
$this->addresses[] = $address;
110+
}
111+
}
112+
113+
/** @Entity */
114+
class LazyEagerCollectionPhone
115+
{
116+
/**
117+
* @var int
118+
* @Id
119+
* @Column(type="integer")
120+
* @GeneratedValue(strategy="AUTO")
121+
*/
122+
public $id;
123+
124+
/**
125+
* @var string
126+
* @Column(type="string", length=255)
127+
*/
128+
public $data;
129+
130+
/**
131+
* @ORM\ManyToOne(targetEntity="LazyEagerCollectionUser", inversedBy="phones")
132+
*
133+
* @var LazyEagerCollectionUser
134+
*/
135+
public $user;
136+
}
137+
138+
/** @Entity */
139+
class LazyEagerCollectionAddress
140+
{
141+
/**
142+
* @var int
143+
* @Id
144+
* @Column(type="integer")
145+
* @GeneratedValue(strategy="AUTO")
146+
*/
147+
public $id;
148+
149+
/**
150+
* @var string
151+
* @Column(type="string", length=255)
152+
*/
153+
public $data;
154+
155+
/**
156+
* @ORM\ManyToOne(targetEntity="LazyEagerCollectionUser", inversedBy="addresses")
157+
*
158+
* @var LazyEagerCollectionUser
159+
*/
160+
public $user;
161+
}

0 commit comments

Comments
 (0)