Skip to content

Commit

Permalink
Merge release 2.17.2 into 2.18.x (#11131)
Browse files Browse the repository at this point in the history
  • Loading branch information
derrabus authored Dec 20, 2023
2 parents c2d29d5 + 393679a commit 9785cb8
Show file tree
Hide file tree
Showing 10 changed files with 92 additions and 20 deletions.
5 changes: 5 additions & 0 deletions docs/en/reference/dql-doctrine-query-language.rst
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,11 @@ hierarchies:
$query = $em->createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF Doctrine\Tests\Models\Company\CompanyEmployee');
$query = $em->createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF ?1');
$query = $em->createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u NOT INSTANCE OF ?1');
$query->setParameter(0, $em->getClassMetadata(CompanyEmployee::class));
.. note::
To use a class as parameter, you have to bind its class metadata:
``$query->setParameter(0, $em->getClassMetadata(CompanyEmployee::class);``.

Get all users visible on a given website that have chosen certain gender:

Expand Down
2 changes: 1 addition & 1 deletion lib/Doctrine/ORM/EntityManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -987,7 +987,7 @@ public static function create($connection, Configuration $config, ?EventManager
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/9961',
'%s() is deprecated. To boostrap a DBAL connection, call %s::getConnection() instead. Use the constructor to create an instance of %s.',
'%s() is deprecated. To bootstrap a DBAL connection, call %s::getConnection() instead. Use the constructor to create an instance of %s.',
__METHOD__,
DriverManager::class,
self::class
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ protected function configure()
->addOption('em', null, InputOption::VALUE_REQUIRED, 'Name of the entity manager to operate on')
->addOption('skip-mapping', null, InputOption::VALUE_NONE, 'Skip the mapping validation check')
->addOption('skip-sync', null, InputOption::VALUE_NONE, 'Skip checking if the mapping is in sync with the database')
->addOption('skip-property-types', null, InputOption::VALUE_NONE, 'Skip checking if property types match the Doctrine types')
->setHelp('Validate that the mapping files are correct and in sync with the database.');
}

Expand All @@ -39,7 +40,7 @@ private function doExecute(InputInterface $input, OutputInterface $output): int
$ui = (new SymfonyStyle($input, $output))->getErrorStyle();

$em = $this->getEntityManager($input);
$validator = new SchemaValidator($em);
$validator = new SchemaValidator($em, ! $input->getOption('skip-property-types'));
$exit = 0;

$ui->section('Mapping');
Expand Down
51 changes: 44 additions & 7 deletions lib/Doctrine/ORM/Tools/SchemaValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
use function get_class;
use function implode;
use function in_array;
use function interface_exists;
use function is_a;
use function sprintf;

Expand All @@ -56,6 +57,9 @@ class SchemaValidator
/** @var EntityManagerInterface */
private $em;

/** @var bool */
private $validatePropertyTypes;

/**
* It maps built-in Doctrine types to PHP types
*/
Expand All @@ -74,9 +78,10 @@ class SchemaValidator
TextType::class => 'string',
];

public function __construct(EntityManagerInterface $em)
public function __construct(EntityManagerInterface $em, bool $validatePropertyTypes = true)
{
$this->em = $em;
$this->em = $em;
$this->validatePropertyTypes = $validatePropertyTypes;
}

/**
Expand Down Expand Up @@ -136,7 +141,7 @@ public function validateClass(ClassMetadataInfo $class)
}

// PHP 7.4 introduces the ability to type properties, so we can't validate them in previous versions
if (PHP_VERSION_ID >= 70400) {
if (PHP_VERSION_ID >= 70400 && $this->validatePropertyTypes) {
array_push($ce, ...$this->validatePropertiesTypes($class));
}

Expand Down Expand Up @@ -389,10 +394,20 @@ function (array $fieldMapping) use ($class): ?string {
return null;
}

if (
is_a($propertyType, BackedEnum::class, true)
&& $metadataFieldType === (string) (new ReflectionEnum($propertyType))->getBackingType()
) {
if (is_a($propertyType, BackedEnum::class, true)) {
$backingType = (string) (new ReflectionEnum($propertyType))->getBackingType();

if ($metadataFieldType !== $backingType) {
return sprintf(
"The field '%s#%s' has the property type '%s' with a backing type of '%s' that differs from the metadata field type '%s'.",
$class->name,
$fieldName,
$propertyType,
$backingType,
$metadataFieldType
);
}

if (! isset($fieldMapping['enumType']) || $propertyType === $fieldMapping['enumType']) {
return null;
}
Expand All @@ -406,6 +421,28 @@ function (array $fieldMapping) use ($class): ?string {
);
}

if (
isset($fieldMapping['enumType'])
&& $propertyType !== $fieldMapping['enumType']
&& interface_exists($propertyType)
&& is_a($fieldMapping['enumType'], $propertyType, true)
) {
$backingType = (string) (new ReflectionEnum($fieldMapping['enumType']))->getBackingType();

if ($metadataFieldType === $backingType) {
return null;
}

return sprintf(
"The field '%s#%s' has the metadata enumType '%s' with a backing type of '%s' that differs from the metadata field type '%s'.",
$class->name,
$fieldName,
$fieldMapping['enumType'],
$backingType,
$metadataFieldType
);
}

if (
$fieldMapping['type'] === 'json'
&& in_array($propertyType, ['string', 'int', 'float', 'bool', 'true', 'false', 'null'], true)
Expand Down
27 changes: 18 additions & 9 deletions tests/Doctrine/Tests/ORM/Functional/Ticket/GH10661/GH10661Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,36 @@ final class GH10661Test extends OrmTestCase
/** @var EntityManagerInterface */
private $em;

/** @var SchemaValidator */
private $validator;

protected function setUp(): void
{
$this->em = $this->getTestEntityManager();
$this->validator = new SchemaValidator($this->em);
$this->em = $this->getTestEntityManager();
}

public function testMetadataFieldTypeNotCoherentWithEntityPropertyType(): void
{
$class = $this->em->getClassMetadata(InvalidEntity::class);
$ce = $this->validator->validateClass($class);
$ce = $this->bootstrapValidator()->validateClass($class);

self::assertEquals(
self::assertSame(
["The field 'Doctrine\Tests\ORM\Functional\Ticket\GH10661\InvalidEntity#property1' has the property type 'float' that differs from the metadata field type 'string' returned by the 'decimal' DBAL type."],
$ce
);
}

public function testPropertyTypeErrorsCanBeSilenced(): void
{
$class = $this->em->getClassMetadata(InvalidEntity::class);
$ce = $this->bootstrapValidator(false)->validateClass($class);

self::assertSame([], $ce);
}

public function testMetadataFieldTypeNotCoherentWithEntityPropertyTypeWithInheritance(): void
{
$class = $this->em->getClassMetadata(InvalidChildEntity::class);
$ce = $this->validator->validateClass($class);
$ce = $this->bootstrapValidator()->validateClass($class);

self::assertEquals(
self::assertSame(
[
"The field 'Doctrine\Tests\ORM\Functional\Ticket\GH10661\InvalidChildEntity#property1' has the property type 'float' that differs from the metadata field type 'string' returned by the 'decimal' DBAL type.",
"The field 'Doctrine\Tests\ORM\Functional\Ticket\GH10661\InvalidChildEntity#property2' has the property type 'int' that differs from the metadata field type 'string' returned by the 'string' DBAL type.",
Expand All @@ -50,4 +54,9 @@ public function testMetadataFieldTypeNotCoherentWithEntityPropertyTypeWithInheri
$ce
);
}

private function bootstrapValidator(bool $validatePropertyTypes = true): SchemaValidator
{
return new SchemaValidator($this->em, $validatePropertyTypes);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\ORM\Functional\Ticket\GH11037;

interface EntityStatus
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ public function testMetadataFieldTypeNotCoherentWithEntityPropertyType(): void

self::assertEquals(
[
"The field 'Doctrine\Tests\ORM\Functional\Ticket\GH11037\InvalidEntityWithTypedEnum#status1' has the property type 'Doctrine\Tests\ORM\Functional\Ticket\GH11037\StringEntityStatus' that differs from the metadata field type 'int' returned by the 'integer' DBAL type.",
"The field 'Doctrine\Tests\ORM\Functional\Ticket\GH11037\InvalidEntityWithTypedEnum#status1' has the property type 'Doctrine\Tests\ORM\Functional\Ticket\GH11037\StringEntityStatus' with a backing type of 'string' that differs from the metadata field type 'int'.",
"The field 'Doctrine\Tests\ORM\Functional\Ticket\GH11037\InvalidEntityWithTypedEnum#status2' has the property type 'Doctrine\Tests\ORM\Functional\Ticket\GH11037\IntEntityStatus' that differs from the metadata enumType 'Doctrine\Tests\ORM\Functional\Ticket\GH11037\StringEntityStatus'.",
"The field 'Doctrine\Tests\ORM\Functional\Ticket\GH11037\InvalidEntityWithTypedEnum#status3' has the metadata enumType 'Doctrine\Tests\ORM\Functional\Ticket\GH11037\StringEntityStatus' with a backing type of 'string' that differs from the metadata field type 'int'.",
],
$ce
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,9 @@ class InvalidEntityWithTypedEnum
* @Column(type="integer", enumType=StringEntityStatus::class)
*/
protected IntEntityStatus $status2;

/**
* @Column(type="integer", enumType=StringEntityStatus::class)
*/
protected EntityStatus $status3;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Doctrine\Tests\ORM\Functional\Ticket\GH11037;

enum StringEntityStatus: string
enum StringEntityStatus: string implements EntityStatus
{
case ACTIVE = 'active';
case INACTIVE = 'inactive';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,9 @@ class ValidEntityWithTypedEnum
* @Column(type="smallint", enumType=IntEntityStatus::class)
*/
protected IntEntityStatus $status2;

/**
* @Column(type="string", enumType=StringEntityStatus::class)
*/
protected EntityStatus $status3;
}

0 comments on commit 9785cb8

Please sign in to comment.