Skip to content

Commit 95da667

Browse files
committed
Merge remote-tracking branch 'origin/2.18.x' into 3.0.x
2 parents b187bc8 + feb27f0 commit 95da667

File tree

12 files changed

+131
-30
lines changed

12 files changed

+131
-30
lines changed

phpcs.xml.dist

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,4 +274,9 @@
274274
<!-- https://github.com/doctrine/orm/issues/8537 -->
275275
<exclude-pattern>src/QueryBuilder.php</exclude-pattern>
276276
</rule>
277+
278+
<rule ref="SlevomatCodingStandard.PHP.UselessParentheses">
279+
<!-- We need those parentheses to make enum access seem like valid syntax on PHP 7 -->
280+
<exclude-pattern>src/Mapping/Driver/XmlDriver.php</exclude-pattern>
281+
</rule>
277282
</ruleset>

src/Cache/Persister/Entity/AbstractEntityPersister.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Doctrine\ORM\Cache\TimestampCacheKey;
1818
use Doctrine\ORM\Cache\TimestampRegion;
1919
use Doctrine\ORM\EntityManagerInterface;
20+
use Doctrine\ORM\Internal\CriteriaOrderings;
2021
use Doctrine\ORM\Mapping\AssociationMapping;
2122
use Doctrine\ORM\Mapping\ClassMetadata;
2223
use Doctrine\ORM\Mapping\ClassMetadataFactory;
@@ -33,6 +34,8 @@
3334

3435
abstract class AbstractEntityPersister implements CachedEntityPersister
3536
{
37+
use CriteriaOrderings;
38+
3639
protected UnitOfWork $uow;
3740
protected ClassMetadataFactory $metadataFactory;
3841

@@ -426,7 +429,7 @@ public function count(array|Criteria $criteria = []): int
426429
*/
427430
public function loadCriteria(Criteria $criteria): array
428431
{
429-
$orderBy = $criteria->getOrderings();
432+
$orderBy = self::getCriteriaOrderings($criteria);
430433
$limit = $criteria->getMaxResults();
431434
$offset = $criteria->getFirstResult();
432435
$query = $this->persister->getSelectSQL($criteria);

src/Internal/CriteriaOrderings.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\ORM\Internal;
6+
7+
use Doctrine\Common\Collections\Criteria;
8+
use Doctrine\Common\Collections\Order;
9+
10+
use function array_map;
11+
use function class_exists;
12+
use function method_exists;
13+
use function strtoupper;
14+
15+
trait CriteriaOrderings
16+
{
17+
/**
18+
* @return array<string, string>
19+
*
20+
* @psalm-suppress DeprecatedMethod We need to call the deprecated API if the new one does not exist yet.
21+
*/
22+
private static function getCriteriaOrderings(Criteria $criteria): array
23+
{
24+
if (! method_exists(Criteria::class, 'orderings')) {
25+
return $criteria->getOrderings();
26+
}
27+
28+
return array_map(
29+
static function (Order $order): string {
30+
return $order->value;
31+
},
32+
$criteria->orderings(),
33+
);
34+
}
35+
36+
/**
37+
* @param array<string, string> $orderings
38+
*
39+
* @return array<string, string>|array<string, Order>
40+
*/
41+
private static function mapToOrderEnumIfAvailable(array $orderings): array
42+
{
43+
if (! class_exists(Order::class)) {
44+
return $orderings;
45+
}
46+
47+
return array_map(
48+
static function (string $order): Order {
49+
return Order::from(strtoupper($order));
50+
},
51+
$orderings,
52+
);
53+
}
54+
}

src/Mapping/Driver/XmlDriver.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Doctrine\ORM\Mapping\Driver;
66

77
use Doctrine\Common\Collections\Criteria;
8+
use Doctrine\Common\Collections\Order;
89
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
910
use Doctrine\ORM\Mapping\ClassMetadata;
1011
use Doctrine\ORM\Mapping\MappingException;
@@ -17,6 +18,7 @@
1718
use SimpleXMLElement;
1819

1920
use function assert;
21+
use function class_exists;
2022
use function constant;
2123
use function count;
2224
use function defined;
@@ -403,9 +405,10 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad
403405
if (isset($oneToManyElement->{'order-by'})) {
404406
$orderBy = [];
405407
foreach ($oneToManyElement->{'order-by'}->{'order-by-field'} ?? [] as $orderByField) {
408+
/** @psalm-suppress DeprecatedConstant */
406409
$orderBy[(string) $orderByField['name']] = isset($orderByField['direction'])
407410
? (string) $orderByField['direction']
408-
: Criteria::ASC;
411+
: (class_exists(Order::class) ? (Order::Ascending)->value : Criteria::ASC);
409412
}
410413

411414
$mapping['orderBy'] = $orderBy;
@@ -531,9 +534,10 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad
531534
if (isset($manyToManyElement->{'order-by'})) {
532535
$orderBy = [];
533536
foreach ($manyToManyElement->{'order-by'}->{'order-by-field'} ?? [] as $orderByField) {
537+
/** @psalm-suppress DeprecatedConstant */
534538
$orderBy[(string) $orderByField['name']] = isset($orderByField['direction'])
535539
? (string) $orderByField['direction']
536-
: Criteria::ASC;
540+
: (class_exists(Order::class) ? (Order::Ascending)->value : Criteria::ASC);
537541
}
538542

539543
$mapping['orderBy'] = $orderBy;

src/PersistentCollection.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Doctrine\Common\Collections\Collection;
1010
use Doctrine\Common\Collections\Criteria;
1111
use Doctrine\Common\Collections\Selectable;
12+
use Doctrine\ORM\Internal\CriteriaOrderings;
1213
use Doctrine\ORM\Mapping\AssociationMapping;
1314
use Doctrine\ORM\Mapping\ClassMetadata;
1415
use Doctrine\ORM\Mapping\ToManyAssociationMapping;
@@ -40,6 +41,8 @@
4041
*/
4142
final class PersistentCollection extends AbstractLazyCollection implements Selectable
4243
{
44+
use CriteriaOrderings;
45+
4346
/**
4447
* A snapshot of the collection at the moment it was fetched from the database.
4548
* This is used to create a diff of the collection at commit time.
@@ -585,7 +588,9 @@ public function matching(Criteria $criteria): Collection
585588

586589
$criteria = clone $criteria;
587590
$criteria->where($expression);
588-
$criteria->orderBy($criteria->getOrderings() ?: $association->orderBy());
591+
$criteria->orderBy(self::mapToOrderEnumIfAvailable(
592+
self::getCriteriaOrderings($criteria) ?: $association->orderBy(),
593+
));
589594

590595
$persister = $this->getUnitOfWork()->getEntityPersister($association->targetEntity);
591596

src/Persisters/Collection/ManyToManyPersister.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Doctrine\Common\Collections\Expr\Comparison;
1010
use Doctrine\DBAL\Exception as DBALException;
1111
use Doctrine\DBAL\LockMode;
12+
use Doctrine\ORM\Internal\CriteriaOrderings;
1213
use Doctrine\ORM\Mapping\AssociationMapping;
1314
use Doctrine\ORM\Mapping\ClassMetadata;
1415
use Doctrine\ORM\Mapping\InverseSideMapping;
@@ -32,6 +33,8 @@
3233
*/
3334
class ManyToManyPersister extends AbstractCollectionPersister
3435
{
36+
use CriteriaOrderings;
37+
3538
public function delete(PersistentCollection $collection): void
3639
{
3740
$mapping = $this->getMapping($collection);
@@ -732,7 +735,7 @@ private function expandCriteriaParameters(Criteria $criteria): array
732735

733736
private function getOrderingSql(Criteria $criteria, ClassMetadata $targetClass): string
734737
{
735-
$orderings = $criteria->getOrderings();
738+
$orderings = self::getCriteriaOrderings($criteria);
736739
if ($orderings) {
737740
$orderBy = [];
738741
foreach ($orderings as $name => $direction) {

src/Persisters/Entity/BasicEntityPersister.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Doctrine\DBAL\Types\Type;
1717
use Doctrine\DBAL\Types\Types;
1818
use Doctrine\ORM\EntityManagerInterface;
19+
use Doctrine\ORM\Internal\CriteriaOrderings;
1920
use Doctrine\ORM\Mapping\AssociationMapping;
2021
use Doctrine\ORM\Mapping\ClassMetadata;
2122
use Doctrine\ORM\Mapping\JoinColumnMapping;
@@ -97,6 +98,7 @@
9798
*/
9899
class BasicEntityPersister implements EntityPersister
99100
{
101+
use CriteriaOrderings;
100102
use LockSqlHelper;
101103

102104
/** @var array<string,string> */
@@ -842,7 +844,7 @@ public function count(array|Criteria $criteria = []): int
842844
*/
843845
public function loadCriteria(Criteria $criteria): array
844846
{
845-
$orderBy = $criteria->getOrderings();
847+
$orderBy = self::getCriteriaOrderings($criteria);
846848
$limit = $criteria->getMaxResults();
847849
$offset = $criteria->getFirstResult();
848850
$query = $this->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy);

src/QueryBuilder.php

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Doctrine\Common\Collections\Criteria;
99
use Doctrine\DBAL\ArrayParameterType;
1010
use Doctrine\DBAL\ParameterType;
11+
use Doctrine\ORM\Internal\CriteriaOrderings;
1112
use Doctrine\ORM\Internal\QueryType;
1213
use Doctrine\ORM\Query\Expr;
1314
use Doctrine\ORM\Query\Parameter;
@@ -40,6 +41,8 @@
4041
*/
4142
class QueryBuilder implements Stringable
4243
{
44+
use CriteriaOrderings;
45+
4346
/**
4447
* The array of DQL parts collected.
4548
*
@@ -1164,22 +1167,20 @@ public function addCriteria(Criteria $criteria): static
11641167
}
11651168
}
11661169

1167-
if ($criteria->getOrderings()) {
1168-
foreach ($criteria->getOrderings() as $sort => $order) {
1169-
$hasValidAlias = false;
1170-
foreach ($allAliases as $alias) {
1171-
if (str_starts_with($sort . '.', $alias . '.')) {
1172-
$hasValidAlias = true;
1173-
break;
1174-
}
1175-
}
1176-
1177-
if (! $hasValidAlias) {
1178-
$sort = $allAliases[0] . '.' . $sort;
1170+
foreach (self::getCriteriaOrderings($criteria) as $sort => $order) {
1171+
$hasValidAlias = false;
1172+
foreach ($allAliases as $alias) {
1173+
if (str_starts_with($sort . '.', $alias . '.')) {
1174+
$hasValidAlias = true;
1175+
break;
11791176
}
1177+
}
11801178

1181-
$this->addOrderBy($sort, $order);
1179+
if (! $hasValidAlias) {
1180+
$sort = $allAliases[0] . '.' . $sort;
11821181
}
1182+
1183+
$this->addOrderBy($sort, $order);
11831184
}
11841185

11851186
// Overwrite limits only if they was set in criteria

tests/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Doctrine\Common\Collections\ArrayCollection;
88
use Doctrine\Common\Collections\Criteria;
9+
use Doctrine\Common\Collections\Order;
910
use Doctrine\ORM\Mapping\AssociationMapping;
1011
use Doctrine\ORM\PersistentCollection;
1112
use Doctrine\ORM\UnitOfWork;
@@ -16,6 +17,7 @@
1617
use PHPUnit\Framework\Attributes\Group;
1718

1819
use function assert;
20+
use function class_exists;
1921

2022
/**
2123
* Basic many-to-many association tests.
@@ -435,7 +437,7 @@ public function testManyToManyOrderByIsNotIgnored(): void
435437
$user = $this->_em->find($user::class, $user->id);
436438

437439
$criteria = Criteria::create()
438-
->orderBy(['name' => Criteria::ASC]);
440+
->orderBy(['name' => class_exists(Order::class) ? Order::Ascending : Criteria::ASC]);
439441

440442
self::assertEquals(
441443
['A', 'B', 'C', 'Developers_0'],
@@ -475,7 +477,7 @@ public function testManyToManyOrderByHonorsFieldNameColumnNameAliases(): void
475477
$user = $this->_em->find($user::class, $user->id);
476478

477479
$criteria = Criteria::create()
478-
->orderBy(['name' => Criteria::ASC]);
480+
->orderBy(['name' => class_exists(Order::class) ? Order::Ascending : Criteria::ASC]);
479481

480482
self::assertEquals(
481483
['A', 'B', 'C'],

tests/Tests/ORM/Functional/Ticket/GH7767Test.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
use Doctrine\Common\Collections\Collection;
88
use Doctrine\Common\Collections\Criteria;
9+
use Doctrine\Common\Collections\Order;
10+
use Doctrine\Common\Collections\Selectable;
911
use Doctrine\ORM\Mapping\Column;
1012
use Doctrine\ORM\Mapping\Entity;
1113
use Doctrine\ORM\Mapping\GeneratedValue;
@@ -17,6 +19,7 @@
1719
use PHPUnit\Framework\Attributes\Group;
1820

1921
use function assert;
22+
use function class_exists;
2023

2124
#[Group('GH7767')]
2225
class GH7767Test extends OrmFunctionalTestCase
@@ -54,7 +57,9 @@ public function testMatchingOverrulesCollectionOrdering(): void
5457
$parent = $this->_em->find(GH7767ParentEntity::class, 1);
5558
assert($parent instanceof GH7767ParentEntity);
5659

57-
$children = $parent->getChildren()->matching(Criteria::create()->orderBy(['position' => 'DESC']));
60+
$children = $parent->getChildren()->matching(
61+
Criteria::create()->orderBy(['position' => class_exists(Order::class) ? Order::Descending : 'DESC']),
62+
);
5863

5964
self::assertEquals(300, $children[0]->position);
6065
self::assertEquals(200, $children[1]->position);
@@ -70,7 +75,7 @@ class GH7767ParentEntity
7075
#[GeneratedValue]
7176
private int $id;
7277

73-
/** @psalm-var Collection<int, GH7767ChildEntity> */
78+
/** @psalm-var Collection<int, GH7767ChildEntity>&Selectable<int, GH7767ChildEntity> */
7479
#[OneToMany(targetEntity: GH7767ChildEntity::class, mappedBy: 'parent', fetch: 'EXTRA_LAZY', cascade: ['persist'])]
7580
#[OrderBy(['position' => 'ASC'])]
7681
private $children;
@@ -80,7 +85,7 @@ public function addChild(int $position): void
8085
$this->children[] = new GH7767ChildEntity($this, $position);
8186
}
8287

83-
/** @psalm-return Collection<int, GH7767ChildEntity> */
88+
/** @psalm-return Collection<int, GH7767ChildEntity>&Selectable<int, GH7767ChildEntity> */
8489
public function getChildren(): Collection
8590
{
8691
return $this->children;

0 commit comments

Comments
 (0)