Compare commits

...

10 Commits

Author SHA1 Message Date
Alexander M. Turek
e3e96745cc Fix annotation 2024-03-03 16:49:00 +01:00
Alexander M. Turek
21221f73cc Bump CI workflows (#11336) 2024-03-03 16:46:12 +01:00
Rok Motaln
ab5e9e393b Fix SchemaTool::getSchemaFromMetadata() uniqueConstraint without a predefined name (#11314)
* Fix loading SchemaTool::getSchemaFromMetadata() uniqueConstraint without a name

Fixes a type miss-match exception when reading a UniqueConstraint defined on an Entity which doesn't have a predefined name.

* Fix deprecation on DBAL 3

---------

Co-authored-by: Alexander M. Turek <me@derrabus.de>
2024-03-03 16:02:48 +01:00
Alexander M. Turek
52a6a21387 Psalm 5.22.2 (#11326) 2024-03-01 10:47:18 +01:00
Alexander M. Turek
4fc8629414 PHPStan 1.10.59 (#11320) 2024-02-29 16:47:35 +01:00
Alexander M. Turek
feb27f00c1 Address deprecations from Collection 2.2 (#11315) 2024-02-27 17:37:52 +01:00
Grégoire Paris
719d007a81 Merge pull request #11298 from VincentLanglet/sqlWalkerPhpdoc
Fix sqlWalker::walkSimpleArithmeticExpression phpdoc
2024-02-26 08:21:47 +01:00
Grégoire Paris
76c4539ffa Merge pull request #11293 from greg0ire/wrong-type
Remove wrong annotation about return type
2024-02-24 13:05:08 +01:00
Vincent Langlet
0f8d193512 Fix sql walker phpdoc 2024-02-23 15:11:15 +01:00
Grégoire Paris
cc314d0fb7 Remove wrong annotation about return type
Although this method is guaranteed to return either null or something
that can be used as a fully qualified class name, it never actually
checks that the class actually exists. Adding such a check breaks
several tests, including some that expect a exceptions at some later
points in the execution.
2024-02-22 23:14:52 +01:00
26 changed files with 1107 additions and 845 deletions

View File

@@ -97,9 +97,9 @@ jobs:
ORM_PROXY_IMPLEMENTATION: "${{ matrix.proxy }}"
- name: "Upload coverage file"
uses: "actions/upload-artifact@v3"
uses: "actions/upload-artifact@v4"
with:
name: "phpunit-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage"
name: "phpunit-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-${{ matrix.proxy }}-coverage"
path: "coverage*.xml"
@@ -170,9 +170,9 @@ jobs:
run: "vendor/bin/phpunit -c ci/github/phpunit/pdo_pgsql.xml --coverage-clover=coverage.xml"
- name: "Upload coverage file"
uses: "actions/upload-artifact@v3"
uses: "actions/upload-artifact@v4"
with:
name: "${{ github.job }}-${{ matrix.postgres-version }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage"
name: "${{ github.job }}-${{ matrix.postgres-version }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-${{ matrix.extension }}-coverage"
path: "coverage.xml"
@@ -240,7 +240,7 @@ jobs:
run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --coverage-clover=coverage.xml"
- name: "Upload coverage file"
uses: "actions/upload-artifact@v3"
uses: "actions/upload-artifact@v4"
with:
name: "${{ github.job }}-${{ matrix.mariadb-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage"
path: "coverage.xml"
@@ -317,7 +317,7 @@ jobs:
ENABLE_SECOND_LEVEL_CACHE: 1
- name: "Upload coverage files"
uses: "actions/upload-artifact@v3"
uses: "actions/upload-artifact@v4"
with:
name: "${{ github.job }}-${{ matrix.mysql-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage"
path: "coverage*.xml"
@@ -372,7 +372,7 @@ jobs:
fetch-depth: 2
- name: "Download coverage files"
uses: "actions/download-artifact@v3"
uses: "actions/download-artifact@v4"
with:
path: "reports"

View File

@@ -27,7 +27,7 @@ jobs:
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
php-version: "8.2"
php-version: "8.3"
- name: "Remove existing composer file"
run: "rm composer.json"

View File

@@ -7,7 +7,7 @@ on:
jobs:
release:
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@3.0.0"
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@4.0.0"
secrets:
GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }}
GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }}

View File

@@ -48,7 +48,7 @@ jobs:
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
php-version: "8.2"
php-version: "8.3"
- name: "Require specific DBAL version"
run: "composer require doctrine/dbal ^${{ matrix.dbal-version }} --no-update"
@@ -89,7 +89,7 @@ jobs:
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
php-version: "8.2"
php-version: "8.3"
- name: "Require specific persistence version"
run: "composer require doctrine/persistence ^3.1 --no-update"

View File

@@ -42,14 +42,14 @@
"doctrine/annotations": "^1.13 || ^2",
"doctrine/coding-standard": "^9.0.2 || ^12.0",
"phpbench/phpbench": "^0.16.10 || ^1.0",
"phpstan/phpstan": "~1.4.10 || 1.10.35",
"phpstan/phpstan": "~1.4.10 || 1.10.59",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.6",
"psr/log": "^1 || ^2 || ^3",
"squizlabs/php_codesniffer": "3.7.2",
"symfony/cache": "^4.4 || ^5.4 || ^6.4 || ^7.0",
"symfony/var-exporter": "^4.4 || ^5.4 || ^6.2 || ^7.0",
"symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0",
"vimeo/psalm": "4.30.0 || 5.16.0"
"vimeo/psalm": "4.30.0 || 5.22.2"
},
"conflict": {
"doctrine/annotations": "<1.13 || >= 3.0"

View File

@@ -279,4 +279,9 @@
<!-- https://github.com/doctrine/orm/issues/8537 -->
<exclude-pattern>src/QueryBuilder.php</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.PHP.UselessParentheses">
<!-- We need those parentheses to make enum access seem like valid syntax on PHP 7 -->
<exclude-pattern>src/Mapping/Driver/XmlDriver.php</exclude-pattern>
</rule>
</ruleset>

View File

@@ -170,11 +170,6 @@ parameters:
count: 2
path: src/Mapping/ClassMetadataFactory.php
-
message: "#^Method Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadataInfo\\:\\:fullyQualifiedClassName\\(\\) should return class\\-string\\|null but returns string\\|null\\.$#"
count: 1
path: src/Mapping/ClassMetadataInfo.php
-
message: "#^Method Doctrine\\\\ORM\\\\Mapping\\\\NamingStrategy\\:\\:joinColumnName\\(\\) invoked with 2 parameters, 1 required\\.$#"
count: 2
@@ -236,7 +231,7 @@ parameters:
path: src/Mapping/Driver/XmlDriver.php
-
message: "#^Offset 'version' on \\*NEVER\\* in isset\\(\\) always exists and is always null\\.$#"
message: "#^Offset 'version' on \\*NEVER\\* in isset\\(\\) always exists and is not nullable\\.$#"
count: 1
path: src/Mapping/Driver/XmlDriver.php
@@ -331,22 +326,7 @@ parameters:
path: src/Query/AST/Functions/DateSubFunction.php
-
message: "#^Parameter \\#1 \\$simpleArithmeticExpr of method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:walkSimpleArithmeticExpression\\(\\) expects Doctrine\\\\ORM\\\\Query\\\\AST\\\\SimpleArithmeticExpression, Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node given\\.$#"
count: 1
path: src/Query/AST/Functions/LengthFunction.php
-
message: "#^Parameter \\#1 \\$simpleArithmeticExpr of method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:walkSimpleArithmeticExpression\\(\\) expects Doctrine\\\\ORM\\\\Query\\\\AST\\\\SimpleArithmeticExpression, Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node given\\.$#"
count: 1
path: src/Query/AST/Functions/LowerFunction.php
-
message: "#^Parameter \\#1 \\$simpleArithmeticExpr of method Doctrine\\\\ORM\\\\Query\\\\SqlWalker\\:\\:walkSimpleArithmeticExpression\\(\\) expects Doctrine\\\\ORM\\\\Query\\\\AST\\\\SimpleArithmeticExpression, Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node given\\.$#"
count: 1
path: src/Query/AST/Functions/UpperFunction.php
-
message: "#^Method Doctrine\\\\ORM\\\\Query\\\\AST\\\\IndexBy\\:\\:dispatch\\(\\) should return string but returns void\\.$#"
message: "#^Method Doctrine\\\\ORM\\\\Query\\\\AST\\\\IndexBy\\:\\:dispatch\\(\\) should return string but returns null\\.$#"
count: 1
path: src/Query/AST/IndexBy.php
@@ -395,11 +375,6 @@ parameters:
count: 1
path: src/Query/Expr/Select.php
-
message: "#^Comparison operation \"\\<\" between null and 102 is always true\\.$#"
count: 1
path: src/Query/Parser.php
-
message: "#^Method Doctrine\\\\ORM\\\\Query\\\\Parser\\:\\:ArithmeticFactor\\(\\) should return Doctrine\\\\ORM\\\\Query\\\\AST\\\\ArithmeticFactor but returns Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node\\|string\\.$#"
count: 1
@@ -415,14 +390,9 @@ parameters:
count: 1
path: src/Query/Parser.php
-
message: "#^Result of && is always true\\.$#"
count: 1
path: src/Query/Parser.php
-
message: "#^Unreachable statement \\- code above always terminates\\.$#"
count: 4
count: 3
path: src/Query/Parser.php
-

View File

@@ -31,6 +31,31 @@ parameters:
message: '/^Instanceof between Doctrine\\DBAL\\Platforms\\AbstractPlatform and Doctrine\\DBAL\\Platforms\\MySQLPlatform will always evaluate to false\.$/'
path: src/Utility/LockSqlHelper.php
# Forward compatibility with Collections 3
-
message: '#^Parameter \$order of anonymous function has invalid type Doctrine\\Common\\Collections\\Order\.$#'
path: src/Internal/CriteriaOrderings.php
-
message: '#^Anonymous function has invalid return type Doctrine\\Common\\Collections\\Order\.$#'
path: src/Internal/CriteriaOrderings.php
-
message: '#^Access to property \$value on an unknown class Doctrine\\Common\\Collections\\Order\.$#'
path: src/Internal/CriteriaOrderings.php
-
message: '#^Call to static method from\(\) on an unknown class Doctrine\\Common\\Collections\\Order\.$#'
path: src/Internal/CriteriaOrderings.php
-
message: '#^Call to an undefined method Doctrine\\Common\\Collections\\Criteria\:\:orderings\(\)\.$#'
path: src/Internal/CriteriaOrderings.php
-
message: '#^Method .+\:\:mapToOrderEnumIfAvailable\(\) has invalid return type Doctrine\\Common\\Collections\\Order\.$#'
path: src/Internal/CriteriaOrderings.php
# False positive
-
message: '/^Call to an undefined method Doctrine\\Common\\Cache\\Cache::deleteAll\(\)\.$/'

File diff suppressed because it is too large Load Diff

View File

@@ -243,6 +243,12 @@
<file name="src/Mapping/ClassMetadataFactory.php"/>
</errorLevel>
</ReferenceConstraintViolation>
<RiskyTruthyFalsyComparison>
<!-- TODO: Enable this new rule on higher branches. -->
<errorLevel type="suppress">
<directory name="src" />
</errorLevel>
</RiskyTruthyFalsyComparison>
<TooManyArguments>
<errorLevel type="suppress">
<!-- Symfony cache supports passing a key prefix to the clear method. -->

View File

@@ -16,6 +16,7 @@ use Doctrine\ORM\Cache\Region;
use Doctrine\ORM\Cache\TimestampCacheKey;
use Doctrine\ORM\Cache\TimestampRegion;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Internal\CriteriaOrderings;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\ORM\PersistentCollection;
@@ -30,6 +31,8 @@ use function sha1;
abstract class AbstractEntityPersister implements CachedEntityPersister
{
use CriteriaOrderings;
/** @var UnitOfWork */
protected $uow;
@@ -475,7 +478,7 @@ abstract class AbstractEntityPersister implements CachedEntityPersister
*/
public function loadCriteria(Criteria $criteria)
{
$orderBy = $criteria->getOrderings();
$orderBy = self::getCriteriaOrderings($criteria);
$limit = $criteria->getMaxResults();
$offset = $criteria->getFirstResult();
$query = $this->persister->getSelectSQL($criteria);

View File

@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Internal;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\Order;
use function array_map;
use function class_exists;
use function method_exists;
use function strtoupper;
trait CriteriaOrderings
{
/**
* @return array<string, string>
*
* @psalm-suppress DeprecatedMethod We need to call the deprecated API if the new one does not exist yet.
*/
private static function getCriteriaOrderings(Criteria $criteria): array
{
if (! method_exists(Criteria::class, 'orderings')) {
return $criteria->getOrderings();
}
return array_map(
static function (Order $order): string {
return $order->value;
},
$criteria->orderings()
);
}
/**
* @param array<string, string> $orderings
*
* @return array<string, string>|array<string, Order>
*/
private static function mapToOrderEnumIfAvailable(array $orderings): array
{
if (! class_exists(Order::class)) {
return $orderings;
}
return array_map(
static function (string $order): Order {
return Order::from(strtoupper($order));
},
$orderings
);
}
}

View File

@@ -3707,7 +3707,6 @@ class ClassMetadataInfo implements ClassMetadata
* @param string|null $className
*
* @return string|null null if the input value is null
* @psalm-return class-string|null
*/
public function fullyQualifiedClassName($className)
{

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Mapping;
use BackedEnum;
use DateInterval;
use DateTime;
use DateTimeImmutable;
@@ -16,6 +17,7 @@ use ReflectionProperty;
use function array_merge;
use function assert;
use function enum_exists;
use function is_a;
use const PHP_VERSION_ID;
@@ -54,18 +56,18 @@ final class DefaultTypedFieldMapper implements TypedFieldMapper
&& ($type instanceof ReflectionNamedType)
) {
if (PHP_VERSION_ID >= 80100 && ! $type->isBuiltin() && enum_exists($type->getName())) {
$mapping['enumType'] = $type->getName();
$reflection = new ReflectionEnum($type->getName());
if (! $reflection->isBacked()) {
throw MappingException::backedEnumTypeRequired(
$field->class,
$mapping['fieldName'],
$mapping['enumType']
$type->getName()
);
}
$type = $reflection->getBackingType();
assert(is_a($type->getName(), BackedEnum::class, true));
$mapping['enumType'] = $type->getName();
$type = $reflection->getBackingType();
assert($type instanceof ReflectionNamedType);
}

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Doctrine\ORM\Mapping\Driver;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\Order;
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\MappingException;
@@ -16,6 +17,7 @@ use LogicException;
use SimpleXMLElement;
use function assert;
use function class_exists;
use function constant;
use function count;
use function defined;
@@ -481,9 +483,10 @@ class XmlDriver extends FileDriver
if (isset($oneToManyElement->{'order-by'})) {
$orderBy = [];
foreach ($oneToManyElement->{'order-by'}->{'order-by-field'} ?? [] as $orderByField) {
/** @psalm-suppress DeprecatedConstant */
$orderBy[(string) $orderByField['name']] = isset($orderByField['direction'])
? (string) $orderByField['direction']
: Criteria::ASC;
: (class_exists(Order::class) ? (Order::Ascending)->value : Criteria::ASC);
}
$mapping['orderBy'] = $orderBy;
@@ -609,9 +612,10 @@ class XmlDriver extends FileDriver
if (isset($manyToManyElement->{'order-by'})) {
$orderBy = [];
foreach ($manyToManyElement->{'order-by'}->{'order-by-field'} ?? [] as $orderByField) {
/** @psalm-suppress DeprecatedConstant */
$orderBy[(string) $orderByField['name']] = isset($orderByField['direction'])
? (string) $orderByField['direction']
: Criteria::ASC;
: (class_exists(Order::class) ? (Order::Ascending)->value : Criteria::ASC);
}
$mapping['orderBy'] = $orderBy;

View File

@@ -9,6 +9,7 @@ use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\Selectable;
use Doctrine\ORM\Internal\CriteriaOrderings;
use Doctrine\ORM\Mapping\ClassMetadata;
use ReturnTypeWillChange;
use RuntimeException;
@@ -41,6 +42,8 @@ use function spl_object_id;
*/
final class PersistentCollection extends AbstractLazyCollection implements Selectable
{
use CriteriaOrderings;
/**
* A snapshot of the collection at the moment it was fetched from the database.
* This is used to create a diff of the collection at commit time.
@@ -671,7 +674,9 @@ final class PersistentCollection extends AbstractLazyCollection implements Selec
$criteria = clone $criteria;
$criteria->where($expression);
$criteria->orderBy($criteria->getOrderings() ?: $association['orderBy'] ?? []);
$criteria->orderBy(self::mapToOrderEnumIfAvailable(
self::getCriteriaOrderings($criteria) ?: $association['orderBy'] ?? []
));
$persister = $this->getUnitOfWork()->getEntityPersister($association['targetEntity']);

View File

@@ -8,6 +8,7 @@ use BadMethodCallException;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\Expr\Comparison;
use Doctrine\DBAL\Exception as DBALException;
use Doctrine\ORM\Internal\CriteriaOrderings;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Persisters\SqlValueVisitor;
@@ -30,6 +31,8 @@ use function sprintf;
*/
class ManyToManyPersister extends AbstractCollectionPersister
{
use CriteriaOrderings;
/**
* {@inheritDoc}
*/
@@ -745,7 +748,7 @@ class ManyToManyPersister extends AbstractCollectionPersister
private function getOrderingSql(Criteria $criteria, ClassMetadata $targetClass): string
{
$orderings = $criteria->getOrderings();
$orderings = self::getCriteriaOrderings($criteria);
if ($orderings) {
$orderBy = [];
foreach ($orderings as $name => $direction) {

View File

@@ -15,6 +15,7 @@ use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Types\Types;
use Doctrine\Deprecations\Deprecation;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Internal\CriteriaOrderings;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\ORM\Mapping\QuoteStrategy;
@@ -93,6 +94,7 @@ use function trim;
*/
class BasicEntityPersister implements EntityPersister
{
use CriteriaOrderings;
use LockSqlHelper;
/** @var array<string,string> */
@@ -884,7 +886,7 @@ class BasicEntityPersister implements EntityPersister
*/
public function loadCriteria(Criteria $criteria)
{
$orderBy = $criteria->getOrderings();
$orderBy = self::getCriteriaOrderings($criteria);
$limit = $criteria->getMaxResults();
$offset = $criteria->getFirstResult();
$query = $this->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy);

View File

@@ -2588,7 +2588,7 @@ class SqlWalker implements TreeWalker
/**
* Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL.
*
* @param AST\SimpleArithmeticExpression $simpleArithmeticExpr
* @param AST\Node|string $simpleArithmeticExpr
*
* @return string
*

View File

@@ -7,6 +7,7 @@ namespace Doctrine\ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Deprecations\Deprecation;
use Doctrine\ORM\Internal\CriteriaOrderings;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\Query\Parameter;
use Doctrine\ORM\Query\QueryExpressionVisitor;
@@ -39,6 +40,8 @@ use function substr;
*/
class QueryBuilder
{
use CriteriaOrderings;
/** @deprecated */
public const SELECT = 0;
@@ -1375,22 +1378,20 @@ class QueryBuilder
}
}
if ($criteria->getOrderings()) {
foreach ($criteria->getOrderings() as $sort => $order) {
$hasValidAlias = false;
foreach ($allAliases as $alias) {
if (str_starts_with($sort . '.', $alias . '.')) {
$hasValidAlias = true;
break;
}
foreach (self::getCriteriaOrderings($criteria) as $sort => $order) {
$hasValidAlias = false;
foreach ($allAliases as $alias) {
if (str_starts_with($sort . '.', $alias . '.')) {
$hasValidAlias = true;
break;
}
if (! $hasValidAlias) {
$sort = $allAliases[0] . '.' . $sort;
}
$this->addOrderBy($sort, $order);
}
if (! $hasValidAlias) {
$sort = $allAliases[0] . '.' . $sort;
}
$this->addOrderBy($sort, $order);
}
// Overwrite limits only if they was set in criteria

View File

@@ -365,7 +365,7 @@ class SchemaTool
if (isset($class->table['uniqueConstraints'])) {
foreach ($class->table['uniqueConstraints'] as $indexName => $indexData) {
$uniqIndex = new Index($indexName, $this->getIndexColumns($class, $indexData), true, false, [], $indexData['options'] ?? []);
$uniqIndex = new Index('tmp__' . $indexName, $this->getIndexColumns($class, $indexData), true, false, [], $indexData['options'] ?? []);
foreach ($table->getIndexes() as $tableIndexName => $tableIndex) {
$method = method_exists($tableIndex, 'isFulfilledBy') ? 'isFulfilledBy' : 'isFullfilledBy';

View File

@@ -6,6 +6,7 @@ namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\Order;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\UnitOfWork;
use Doctrine\Tests\Models\CMS\CmsGroup;
@@ -14,6 +15,7 @@ use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\OrmFunctionalTestCase;
use function assert;
use function class_exists;
use function get_class;
/**
@@ -436,7 +438,7 @@ class ManyToManyBasicAssociationTest extends OrmFunctionalTestCase
$user = $this->_em->find(get_class($user), $user->id);
$criteria = Criteria::create()
->orderBy(['name' => Criteria::ASC]);
->orderBy(['name' => class_exists(Order::class) ? Order::Ascending : Criteria::ASC]);
self::assertEquals(
['A', 'B', 'C', 'Developers_0'],
@@ -478,7 +480,7 @@ class ManyToManyBasicAssociationTest extends OrmFunctionalTestCase
$user = $this->_em->find(get_class($user), $user->id);
$criteria = Criteria::create()
->orderBy(['name' => Criteria::ASC]);
->orderBy(['name' => class_exists(Order::class) ? Order::Ascending : Criteria::ASC]);
self::assertEquals(
['A', 'B', 'C'],

View File

@@ -6,6 +6,8 @@ namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\Order;
use Doctrine\Common\Collections\Selectable;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\GeneratedValue;
@@ -16,6 +18,7 @@ use Doctrine\ORM\Mapping\OrderBy;
use Doctrine\Tests\OrmFunctionalTestCase;
use function assert;
use function class_exists;
/** @group GH7767 */
class GH7767Test extends OrmFunctionalTestCase
@@ -53,7 +56,9 @@ class GH7767Test extends OrmFunctionalTestCase
$parent = $this->_em->find(GH7767ParentEntity::class, 1);
assert($parent instanceof GH7767ParentEntity);
$children = $parent->getChildren()->matching(Criteria::create()->orderBy(['position' => 'DESC']));
$children = $parent->getChildren()->matching(
Criteria::create()->orderBy(['position' => class_exists(Order::class) ? Order::Descending : 'DESC'])
);
self::assertEquals(300, $children[0]->position);
self::assertEquals(200, $children[1]->position);
@@ -73,7 +78,7 @@ class GH7767ParentEntity
private $id;
/**
* @psalm-var Collection<int, GH7767ChildEntity>
* @psalm-var Collection<int, GH7767ChildEntity>&Selectable<int, GH7767ChildEntity>
* @OneToMany(targetEntity=GH7767ChildEntity::class, mappedBy="parent", fetch="EXTRA_LAZY", cascade={"persist"})
* @OrderBy({"position" = "ASC"})
*/
@@ -84,7 +89,7 @@ class GH7767ParentEntity
$this->children[] = new GH7767ChildEntity($this, $position);
}
/** @psalm-return Collection<int, GH7767ChildEntity> */
/** @psalm-return Collection<int, GH7767ChildEntity>&Selectable<int, GH7767ChildEntity> */
public function getChildren(): Collection
{
return $this->children;

View File

@@ -6,6 +6,8 @@ namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\Order;
use Doctrine\Common\Collections\Selectable;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\GeneratedValue;
@@ -16,6 +18,7 @@ use Doctrine\ORM\Mapping\OrderBy;
use Doctrine\Tests\OrmFunctionalTestCase;
use function assert;
use function class_exists;
/** @group GH7836 */
class GH7836Test extends OrmFunctionalTestCase
@@ -56,7 +59,13 @@ class GH7836Test extends OrmFunctionalTestCase
$parent = $this->_em->find(GH7836ParentEntity::class, 1);
assert($parent instanceof GH7836ParentEntity);
$children = $parent->getChildren()->matching(Criteria::create()->orderBy(['position' => 'DESC', 'name' => 'ASC']));
$children = $parent->getChildren()->matching(
Criteria::create()->orderBy(
class_exists(Order::class)
? ['position' => Order::Descending, 'name' => Order::Ascending]
: ['position' => 'DESC', 'name' => 'ASC']
)
);
self::assertSame(200, $children[0]->position);
self::assertSame('baz', $children[0]->name);
@@ -71,7 +80,13 @@ class GH7836Test extends OrmFunctionalTestCase
$parent = $this->_em->find(GH7836ParentEntity::class, 1);
assert($parent instanceof GH7836ParentEntity);
$children = $parent->getChildren()->matching(Criteria::create()->orderBy(['name' => 'ASC', 'position' => 'ASC']));
$children = $parent->getChildren()->matching(
Criteria::create()->orderBy(
class_exists(Order::class)
? ['name' => Order::Ascending, 'position' => Order::Ascending]
: ['name' => 'ASC', 'position' => 'ASC']
)
);
self::assertSame(100, $children[0]->position);
self::assertSame('bar', $children[0]->name);
@@ -94,7 +109,7 @@ class GH7836ParentEntity
private $id;
/**
* @var Collection<int, GH7836ChildEntity>
* @var Collection<int, GH7836ChildEntity>&Selectable<int, GH7836ChildEntity>
* @OneToMany(targetEntity=GH7836ChildEntity::class, mappedBy="parent", fetch="EXTRA_LAZY", cascade={"persist"})
* @OrderBy({"position" = "ASC", "name" = "ASC"})
*/
@@ -105,7 +120,7 @@ class GH7836ParentEntity
$this->children[] = new GH7836ChildEntity($this, $position, $name);
}
/** @psalm-return Collection<int, GH7836ChildEntity> */
/** @psalm-return Collection<int, GH7836ChildEntity>&Selectable<int, GH7836ChildEntity> */
public function getChildren(): Collection
{
return $this->children;

View File

@@ -6,6 +6,7 @@ namespace Doctrine\Tests\ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\Order;
use Doctrine\Deprecations\PHPUnit\VerifyDeprecations;
use Doctrine\ORM\Cache;
use Doctrine\ORM\Query;
@@ -22,6 +23,7 @@ use Doctrine\Tests\OrmTestCase;
use InvalidArgumentException;
use function array_filter;
use function class_exists;
use function get_class;
/**
@@ -576,7 +578,7 @@ class QueryBuilderTest extends OrmTestCase
->from(CmsUser::class, 'u');
$criteria = new Criteria();
$criteria->orderBy(['field' => Criteria::DESC]);
$criteria->orderBy(['field' => class_exists(Order::class) ? Order::Descending : Criteria::DESC]);
$qb->addCriteria($criteria);
@@ -593,7 +595,7 @@ class QueryBuilderTest extends OrmTestCase
->join('u.article', 'a');
$criteria = new Criteria();
$criteria->orderBy(['a.field' => Criteria::DESC]);
$criteria->orderBy(['a.field' => class_exists(Order::class) ? Order::Descending : Criteria::DESC]);
$qb->addCriteria($criteria);

View File

@@ -374,6 +374,27 @@ class SchemaToolTest extends OrmTestCase
self::assertTrue($schema->hasTable('first_entity'), 'Table first_entity should exist.');
self::assertFalse($schema->hasTable('second_entity'), 'Table second_entity should not exist.');
}
/** @group GH-11314 */
public function testLoadUniqueConstraintWithoutName(): void
{
$em = $this->getTestEntityManager();
$entity = $em->getClassMetadata(GH11314Entity::class);
$schemaTool = new SchemaTool($em);
$schema = $schemaTool->getSchemaFromMetadata([$entity]);
self::assertTrue($schema->hasTable('GH11314Entity'));
$tableEntity = $schema->getTable('GH11314Entity');
self::assertTrue($tableEntity->hasIndex('uniq_2d81a3ed5bf54558875f7fd5'));
$tableIndex = $tableEntity->getIndex('uniq_2d81a3ed5bf54558875f7fd5');
self::assertTrue($tableIndex->isUnique());
self::assertSame(['field', 'anotherField'], $tableIndex->getColumns());
}
}
/**
@@ -559,6 +580,32 @@ class IndexByFieldEntity
public $fieldName;
}
/**
* @Entity
* @Table(uniqueConstraints={@UniqueConstraint(columns={"field", "anotherField"})})
*/
class GH11314Entity
{
/**
* @Column(type="integer")
* @Id
* @var int
*/
private $id;
/**
* @Column(name="field", type="string")
* @var string
*/
private $field;
/**
* @Column(name="anotherField", type="string")
* @var string
*/
private $anotherField;
}
class IncorrectIndexByFieldEntity
{
/** @var int */