Skip to content

Commit feb27f0

Browse files
authored
Address deprecations from Collection 2.2 (#11315)
1 parent 719d007 commit feb27f0

13 files changed

+156
-30
lines changed

phpcs.xml.dist

+5
Original file line numberDiff line numberDiff line change
@@ -279,4 +279,9 @@
279279
<!-- https://github.com/doctrine/orm/issues/8537 -->
280280
<exclude-pattern>src/QueryBuilder.php</exclude-pattern>
281281
</rule>
282+
283+
<rule ref="SlevomatCodingStandard.PHP.UselessParentheses">
284+
<!-- We need those parentheses to make enum access seem like valid syntax on PHP 7 -->
285+
<exclude-pattern>src/Mapping/Driver/XmlDriver.php</exclude-pattern>
286+
</rule>
282287
</ruleset>

phpstan-persistence2.neon

+25
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,31 @@ parameters:
3131
message: '/^Instanceof between Doctrine\\DBAL\\Platforms\\AbstractPlatform and Doctrine\\DBAL\\Platforms\\MySQLPlatform will always evaluate to false\.$/'
3232
path: src/Utility/LockSqlHelper.php
3333

34+
# Forward compatibility with Collections 3
35+
-
36+
message: '#^Parameter \$order of anonymous function has invalid type Doctrine\\Common\\Collections\\Order\.$#'
37+
path: src/Internal/CriteriaOrderings.php
38+
39+
-
40+
message: '#^Anonymous function has invalid return type Doctrine\\Common\\Collections\\Order\.$#'
41+
path: src/Internal/CriteriaOrderings.php
42+
43+
-
44+
message: '#^Access to property \$value on an unknown class Doctrine\\Common\\Collections\\Order\.$#'
45+
path: src/Internal/CriteriaOrderings.php
46+
47+
-
48+
message: '#^Call to static method from\(\) on an unknown class Doctrine\\Common\\Collections\\Order\.$#'
49+
path: src/Internal/CriteriaOrderings.php
50+
51+
-
52+
message: '#^Call to an undefined method Doctrine\\Common\\Collections\\Criteria\:\:orderings\(\)\.$#'
53+
path: src/Internal/CriteriaOrderings.php
54+
55+
-
56+
message: '#^Method .+\:\:mapToOrderEnumIfAvailable\(\) has invalid return type Doctrine\\Common\\Collections\\Order\.$#'
57+
path: src/Internal/CriteriaOrderings.php
58+
3459
# False positive
3560
-
3661
message: '/^Call to an undefined method Doctrine\\Common\\Cache\\Cache::deleteAll\(\)\.$/'

src/Cache/Persister/Entity/AbstractEntityPersister.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Doctrine\ORM\Cache\TimestampCacheKey;
1717
use Doctrine\ORM\Cache\TimestampRegion;
1818
use Doctrine\ORM\EntityManagerInterface;
19+
use Doctrine\ORM\Internal\CriteriaOrderings;
1920
use Doctrine\ORM\Mapping\ClassMetadata;
2021
use Doctrine\ORM\Mapping\ClassMetadataFactory;
2122
use Doctrine\ORM\PersistentCollection;
@@ -30,6 +31,8 @@
3031

3132
abstract class AbstractEntityPersister implements CachedEntityPersister
3233
{
34+
use CriteriaOrderings;
35+
3336
/** @var UnitOfWork */
3437
protected $uow;
3538

@@ -475,7 +478,7 @@ public function count($criteria = [])
475478
*/
476479
public function loadCriteria(Criteria $criteria)
477480
{
478-
$orderBy = $criteria->getOrderings();
481+
$orderBy = self::getCriteriaOrderings($criteria);
479482
$limit = $criteria->getMaxResults();
480483
$offset = $criteria->getFirstResult();
481484
$query = $this->persister->getSelectSQL($criteria);

src/Internal/CriteriaOrderings.php

+54
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

+6-2
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;
@@ -16,6 +17,7 @@
1617
use SimpleXMLElement;
1718

1819
use function assert;
20+
use function class_exists;
1921
use function constant;
2022
use function count;
2123
use function defined;
@@ -481,9 +483,10 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad
481483
if (isset($oneToManyElement->{'order-by'})) {
482484
$orderBy = [];
483485
foreach ($oneToManyElement->{'order-by'}->{'order-by-field'} ?? [] as $orderByField) {
486+
/** @psalm-suppress DeprecatedConstant */
484487
$orderBy[(string) $orderByField['name']] = isset($orderByField['direction'])
485488
? (string) $orderByField['direction']
486-
: Criteria::ASC;
489+
: (class_exists(Order::class) ? (Order::Ascending)->value : Criteria::ASC);
487490
}
488491

489492
$mapping['orderBy'] = $orderBy;
@@ -609,9 +612,10 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad
609612
if (isset($manyToManyElement->{'order-by'})) {
610613
$orderBy = [];
611614
foreach ($manyToManyElement->{'order-by'}->{'order-by-field'} ?? [] as $orderByField) {
615+
/** @psalm-suppress DeprecatedConstant */
612616
$orderBy[(string) $orderByField['name']] = isset($orderByField['direction'])
613617
? (string) $orderByField['direction']
614-
: Criteria::ASC;
618+
: (class_exists(Order::class) ? (Order::Ascending)->value : Criteria::ASC);
615619
}
616620

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

src/PersistentCollection.php

+6-1
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\ClassMetadata;
1314
use ReturnTypeWillChange;
1415
use RuntimeException;
@@ -41,6 +42,8 @@
4142
*/
4243
final class PersistentCollection extends AbstractLazyCollection implements Selectable
4344
{
45+
use CriteriaOrderings;
46+
4447
/**
4548
* A snapshot of the collection at the moment it was fetched from the database.
4649
* This is used to create a diff of the collection at commit time.
@@ -671,7 +674,9 @@ public function matching(Criteria $criteria): Collection
671674

672675
$criteria = clone $criteria;
673676
$criteria->where($expression);
674-
$criteria->orderBy($criteria->getOrderings() ?: $association['orderBy'] ?? []);
677+
$criteria->orderBy(self::mapToOrderEnumIfAvailable(
678+
self::getCriteriaOrderings($criteria) ?: $association['orderBy'] ?? []
679+
));
675680

676681
$persister = $this->getUnitOfWork()->getEntityPersister($association['targetEntity']);
677682

src/Persisters/Collection/ManyToManyPersister.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Doctrine\Common\Collections\Criteria;
99
use Doctrine\Common\Collections\Expr\Comparison;
1010
use Doctrine\DBAL\Exception as DBALException;
11+
use Doctrine\ORM\Internal\CriteriaOrderings;
1112
use Doctrine\ORM\Mapping\ClassMetadata;
1213
use Doctrine\ORM\PersistentCollection;
1314
use Doctrine\ORM\Persisters\SqlValueVisitor;
@@ -30,6 +31,8 @@
3031
*/
3132
class ManyToManyPersister extends AbstractCollectionPersister
3233
{
34+
use CriteriaOrderings;
35+
3336
/**
3437
* {@inheritDoc}
3538
*/
@@ -745,7 +748,7 @@ private function expandCriteriaParameters(Criteria $criteria): array
745748

746749
private function getOrderingSql(Criteria $criteria, ClassMetadata $targetClass): string
747750
{
748-
$orderings = $criteria->getOrderings();
751+
$orderings = self::getCriteriaOrderings($criteria);
749752
if ($orderings) {
750753
$orderBy = [];
751754
foreach ($orderings as $name => $direction) {

src/Persisters/Entity/BasicEntityPersister.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Doctrine\DBAL\Types\Types;
1616
use Doctrine\Deprecations\Deprecation;
1717
use Doctrine\ORM\EntityManagerInterface;
18+
use Doctrine\ORM\Internal\CriteriaOrderings;
1819
use Doctrine\ORM\Mapping\ClassMetadata;
1920
use Doctrine\ORM\Mapping\MappingException;
2021
use Doctrine\ORM\Mapping\QuoteStrategy;
@@ -93,6 +94,7 @@
9394
*/
9495
class BasicEntityPersister implements EntityPersister
9596
{
97+
use CriteriaOrderings;
9698
use LockSqlHelper;
9799

98100
/** @var array<string,string> */
@@ -884,7 +886,7 @@ public function count($criteria = [])
884886
*/
885887
public function loadCriteria(Criteria $criteria)
886888
{
887-
$orderBy = $criteria->getOrderings();
889+
$orderBy = self::getCriteriaOrderings($criteria);
888890
$limit = $criteria->getMaxResults();
889891
$offset = $criteria->getFirstResult();
890892
$query = $this->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy);

src/QueryBuilder.php

+14-13
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Doctrine\Common\Collections\ArrayCollection;
88
use Doctrine\Common\Collections\Criteria;
99
use Doctrine\Deprecations\Deprecation;
10+
use Doctrine\ORM\Internal\CriteriaOrderings;
1011
use Doctrine\ORM\Query\Expr;
1112
use Doctrine\ORM\Query\Parameter;
1213
use Doctrine\ORM\Query\QueryExpressionVisitor;
@@ -39,6 +40,8 @@
3940
*/
4041
class QueryBuilder
4142
{
43+
use CriteriaOrderings;
44+
4245
/** @deprecated */
4346
public const SELECT = 0;
4447

@@ -1375,22 +1378,20 @@ public function addCriteria(Criteria $criteria)
13751378
}
13761379
}
13771380

1378-
if ($criteria->getOrderings()) {
1379-
foreach ($criteria->getOrderings() as $sort => $order) {
1380-
$hasValidAlias = false;
1381-
foreach ($allAliases as $alias) {
1382-
if (str_starts_with($sort . '.', $alias . '.')) {
1383-
$hasValidAlias = true;
1384-
break;
1385-
}
1386-
}
1387-
1388-
if (! $hasValidAlias) {
1389-
$sort = $allAliases[0] . '.' . $sort;
1381+
foreach (self::getCriteriaOrderings($criteria) as $sort => $order) {
1382+
$hasValidAlias = false;
1383+
foreach ($allAliases as $alias) {
1384+
if (str_starts_with($sort . '.', $alias . '.')) {
1385+
$hasValidAlias = true;
1386+
break;
13901387
}
1388+
}
13911389

1392-
$this->addOrderBy($sort, $order);
1390+
if (! $hasValidAlias) {
1391+
$sort = $allAliases[0] . '.' . $sort;
13931392
}
1393+
1394+
$this->addOrderBy($sort, $order);
13941395
}
13951396

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

tests/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php

+4-2
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\PersistentCollection;
1011
use Doctrine\ORM\UnitOfWork;
1112
use Doctrine\Tests\Models\CMS\CmsGroup;
@@ -14,6 +15,7 @@
1415
use Doctrine\Tests\OrmFunctionalTestCase;
1516

1617
use function assert;
18+
use function class_exists;
1719
use function get_class;
1820

1921
/**
@@ -436,7 +438,7 @@ public function testManyToManyOrderByIsNotIgnored(): void
436438
$user = $this->_em->find(get_class($user), $user->id);
437439

438440
$criteria = Criteria::create()
439-
->orderBy(['name' => Criteria::ASC]);
441+
->orderBy(['name' => class_exists(Order::class) ? Order::Ascending : Criteria::ASC]);
440442

441443
self::assertEquals(
442444
['A', 'B', 'C', 'Developers_0'],
@@ -478,7 +480,7 @@ public function testManyToManyOrderByHonorsFieldNameColumnNameAliases(): void
478480
$user = $this->_em->find(get_class($user), $user->id);
479481

480482
$criteria = Criteria::create()
481-
->orderBy(['name' => Criteria::ASC]);
483+
->orderBy(['name' => class_exists(Order::class) ? Order::Ascending : Criteria::ASC]);
482484

483485
self::assertEquals(
484486
['A', 'B', 'C'],

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

+8-3
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;
@@ -16,6 +18,7 @@
1618
use Doctrine\Tests\OrmFunctionalTestCase;
1719

1820
use function assert;
21+
use function class_exists;
1922

2023
/** @group GH7767 */
2124
class GH7767Test extends OrmFunctionalTestCase
@@ -53,7 +56,9 @@ public function testMatchingOverrulesCollectionOrdering(): void
5356
$parent = $this->_em->find(GH7767ParentEntity::class, 1);
5457
assert($parent instanceof GH7767ParentEntity);
5558

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

5863
self::assertEquals(300, $children[0]->position);
5964
self::assertEquals(200, $children[1]->position);
@@ -73,7 +78,7 @@ class GH7767ParentEntity
7378
private $id;
7479

7580
/**
76-
* @psalm-var Collection<int, GH7767ChildEntity>
81+
* @psalm-var Collection<int, GH7767ChildEntity>&Selectable<int, GH7767ChildEntity>
7782
* @OneToMany(targetEntity=GH7767ChildEntity::class, mappedBy="parent", fetch="EXTRA_LAZY", cascade={"persist"})
7883
* @OrderBy({"position" = "ASC"})
7984
*/
@@ -84,7 +89,7 @@ public function addChild(int $position): void
8489
$this->children[] = new GH7767ChildEntity($this, $position);
8590
}
8691

87-
/** @psalm-return Collection<int, GH7767ChildEntity> */
92+
/** @psalm-return Collection<int, GH7767ChildEntity>&Selectable<int, GH7767ChildEntity> */
8893
public function getChildren(): Collection
8994
{
9095
return $this->children;

0 commit comments

Comments
 (0)