mirror of
https://github.com/doctrine/orm.git
synced 2026-03-24 15:02:22 +01:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
393679a479 | ||
|
|
e50ae06fe7 | ||
|
|
05ef1f4f96 | ||
|
|
2b91edc525 | ||
|
|
6af7f9f7bf | ||
|
|
46cb9a980b | ||
|
|
ed1df148c2 | ||
|
|
44e943e100 | ||
|
|
23d36c0d52 | ||
|
|
212edaa80b | ||
|
|
1a4fe6e0bb | ||
|
|
0a7b939623 | ||
|
|
6be65ebc70 | ||
|
|
7613f25d57 |
@@ -46,10 +46,10 @@
|
||||
"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.0",
|
||||
"symfony/var-exporter": "^4.4 || ^5.4 || ^6.2",
|
||||
"symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0",
|
||||
"vimeo/psalm": "4.30.0 || 5.15.0"
|
||||
"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"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/annotations": "<1.13 || >= 3.0"
|
||||
|
||||
@@ -127,7 +127,7 @@ the targetEntity resolution will occur reliably:
|
||||
// Add the ResolveTargetEntityListener
|
||||
$evm->addEventListener(Doctrine\ORM\Events::loadClassMetadata, $rtel);
|
||||
|
||||
$connection = \Doctrine\DBAL\DriverManager::createConnection($connectionOptions, $config, $evm);
|
||||
$connection = \Doctrine\DBAL\DriverManager::getConnection($connectionOptions, $config, $evm);
|
||||
$em = new \Doctrine\ORM\EntityManager($connection, $config, $evm);
|
||||
|
||||
Final Thoughts
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -987,7 +987,7 @@ class EntityManager implements EntityManagerInterface
|
||||
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
|
||||
|
||||
@@ -11,7 +11,9 @@ use Doctrine\DBAL\Types\Type;
|
||||
|
||||
use function array_diff;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function array_values;
|
||||
use function str_replace;
|
||||
|
||||
/**
|
||||
* Base class for SQL statement executors.
|
||||
@@ -84,7 +86,9 @@ abstract class AbstractSqlExecutor
|
||||
serialized representation becomes compatible with 3.0.x, meaning
|
||||
there will not be a deprecation warning about a missing property
|
||||
when unserializing data */
|
||||
return array_values(array_diff(array_keys((array) $this), ["\0*\0_sqlStatements"]));
|
||||
return array_values(array_diff(array_map(static function (string $prop): string {
|
||||
return str_replace("\0*\0", '', $prop);
|
||||
}, array_keys((array) $this)), ['_sqlStatements']));
|
||||
}
|
||||
|
||||
public function __wakeup(): void
|
||||
|
||||
@@ -141,7 +141,9 @@ class ParserResult
|
||||
{
|
||||
foreach (self::LEGACY_PROPERTY_MAPPING as $property => $legacyProperty) {
|
||||
$this->$property = $data[sprintf("\0%s\0%s", self::class, $legacyProperty)]
|
||||
?? $data[self::class][$legacyProperty]
|
||||
?? $data[sprintf("\0%s\0%s", self::class, $property)]
|
||||
?? $data[self::class][$property]
|
||||
?? $this->$property
|
||||
?? null;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ class ValidateSchemaCommand extends AbstractEntityManagerCommand
|
||||
->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.');
|
||||
}
|
||||
|
||||
@@ -39,7 +40,7 @@ class ValidateSchemaCommand extends AbstractEntityManagerCommand
|
||||
$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');
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM\Tools;
|
||||
|
||||
use BackedEnum;
|
||||
use Doctrine\DBAL\Types\AsciiStringType;
|
||||
use Doctrine\DBAL\Types\BigIntType;
|
||||
use Doctrine\DBAL\Types\BooleanType;
|
||||
@@ -21,6 +22,7 @@ use Doctrine\Deprecations\Deprecation;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataInfo;
|
||||
use ReflectionEnum;
|
||||
use ReflectionNamedType;
|
||||
|
||||
use function array_diff;
|
||||
@@ -37,6 +39,8 @@ use function count;
|
||||
use function get_class;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function interface_exists;
|
||||
use function is_a;
|
||||
use function sprintf;
|
||||
|
||||
use const PHP_VERSION_ID;
|
||||
@@ -53,6 +57,9 @@ class SchemaValidator
|
||||
/** @var EntityManagerInterface */
|
||||
private $em;
|
||||
|
||||
/** @var bool */
|
||||
private $validatePropertyTypes;
|
||||
|
||||
/**
|
||||
* It maps built-in Doctrine types to PHP types
|
||||
*/
|
||||
@@ -71,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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,7 +141,7 @@ class SchemaValidator
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
@@ -368,7 +376,7 @@ class SchemaValidator
|
||||
}
|
||||
|
||||
// If the property type is not a named type, we cannot check it
|
||||
if (! ($propertyType instanceof ReflectionNamedType)) {
|
||||
if (! ($propertyType instanceof ReflectionNamedType) || $propertyType->getName() === 'mixed') {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -386,6 +394,62 @@ class SchemaValidator
|
||||
return null;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
"The field '%s#%s' has the property type '%s' that differs from the metadata enumType '%s'.",
|
||||
$class->name,
|
||||
$fieldName,
|
||||
$propertyType,
|
||||
$fieldMapping['enumType']
|
||||
);
|
||||
}
|
||||
|
||||
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)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
"The field '%s#%s' has the property type '%s' that differs from the metadata field type '%s' returned by the '%s' DBAL type.",
|
||||
$class->name,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<files psalm-version="5.15.0@5c774aca4746caf3d239d9c8cadb9f882ca29352">
|
||||
<files psalm-version="5.16.0@2897ba636551a8cb61601cc26f6ccfbba6c36591">
|
||||
<file src="lib/Doctrine/ORM/AbstractQuery.php">
|
||||
<DeprecatedClass>
|
||||
<code>IterableResult</code>
|
||||
@@ -590,7 +590,6 @@
|
||||
<DocblockTypeContradiction>
|
||||
<code><![CDATA[! $mapping['isOwningSide']]]></code>
|
||||
<code><![CDATA[! $this->table]]></code>
|
||||
<code><![CDATA[! class_exists($mapping['targetEntity'])]]></code>
|
||||
<code><![CDATA[$this->table]]></code>
|
||||
<code><![CDATA[isset($mapping['id']) && $mapping['id'] === true && ! $mapping['isOwningSide']]]></code>
|
||||
<code><![CDATA[isset($mapping['orderBy']) && ! is_array($mapping['orderBy'])]]></code>
|
||||
@@ -626,9 +625,6 @@
|
||||
<code>$quotedColumnNames</code>
|
||||
<code><![CDATA[$this->namespace . '\\' . $className]]></code>
|
||||
</LessSpecificReturnStatement>
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
<MoreSpecificReturnType>
|
||||
<code>FieldMapping</code>
|
||||
<code>class-string|null</code>
|
||||
@@ -869,6 +865,9 @@
|
||||
<MoreSpecificReturnType>
|
||||
<code>class-string</code>
|
||||
</MoreSpecificReturnType>
|
||||
<NoValue>
|
||||
<code>$metadata</code>
|
||||
</NoValue>
|
||||
<PossiblyNullArrayAccess>
|
||||
<code><![CDATA[$this->tables[$tableName]]]></code>
|
||||
<code><![CDATA[$this->tables[$tableName]]]></code>
|
||||
@@ -1730,11 +1729,6 @@
|
||||
<code>$sqlWalker</code>
|
||||
</ParamNameMismatch>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/AST/Node.php">
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/AST/NullComparisonExpression.php">
|
||||
<ParamNameMismatch>
|
||||
<code>$sqlWalker</code>
|
||||
@@ -1942,36 +1936,15 @@
|
||||
<code>$parts</code>
|
||||
</NonInvariantDocblockPropertyType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Expr/Base.php">
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Expr/Comparison.php">
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Expr/Composite.php">
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
<PossiblyInvalidCast>
|
||||
<code>$part</code>
|
||||
</PossiblyInvalidCast>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Expr/From.php">
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Expr/Func.php">
|
||||
<LessSpecificReturnStatement>
|
||||
<code><![CDATA[$this->arguments]]></code>
|
||||
</LessSpecificReturnStatement>
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
<MoreSpecificReturnType>
|
||||
<code><![CDATA[list<mixed>]]></code>
|
||||
</MoreSpecificReturnType>
|
||||
@@ -1982,9 +1955,6 @@
|
||||
</NonInvariantDocblockPropertyType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Expr/Join.php">
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
<PossiblyNullArgument>
|
||||
<code><![CDATA[$this->conditionType]]></code>
|
||||
</PossiblyNullArgument>
|
||||
@@ -1994,16 +1964,6 @@
|
||||
<code>$parts</code>
|
||||
</NonInvariantDocblockPropertyType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Expr/Math.php">
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Expr/OrderBy.php">
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Expr/Orx.php">
|
||||
<NonInvariantDocblockPropertyType>
|
||||
<code>$allowedClasses</code>
|
||||
@@ -2017,9 +1977,6 @@
|
||||
</NonInvariantDocblockPropertyType>
|
||||
</file>
|
||||
<file src="lib/Doctrine/ORM/Query/Filter/SQLFilter.php">
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
<MissingClosureParamType>
|
||||
<code>$value</code>
|
||||
</MissingClosureParamType>
|
||||
@@ -2127,9 +2084,6 @@
|
||||
<code>addNamedNativeQueryResultClassMapping</code>
|
||||
<code>addNamedNativeQueryResultSetMapping</code>
|
||||
</DeprecatedMethod>
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
<PossiblyUndefinedArrayOffset>
|
||||
<code><![CDATA[$associationMapping['joinColumns']]]></code>
|
||||
<code><![CDATA[$associationMapping['joinColumns']]]></code>
|
||||
@@ -2192,12 +2146,18 @@
|
||||
<ImplicitToStringCast>
|
||||
<code>$expr</code>
|
||||
</ImplicitToStringCast>
|
||||
<InvalidArrayOffset>
|
||||
<code><![CDATA[$this->queryComponents[$expression]]]></code>
|
||||
</InvalidArrayOffset>
|
||||
<InvalidNullableReturnType>
|
||||
<code>string</code>
|
||||
</InvalidNullableReturnType>
|
||||
<MoreSpecificImplementedParamType>
|
||||
<code>$query</code>
|
||||
</MoreSpecificImplementedParamType>
|
||||
<NoValue>
|
||||
<code>$expression</code>
|
||||
</NoValue>
|
||||
<PossiblyInvalidArgument>
|
||||
<code><![CDATA[$aggExpression->pathExpression]]></code>
|
||||
</PossiblyInvalidArgument>
|
||||
@@ -2293,9 +2253,6 @@
|
||||
<InvalidPropertyAssignmentValue>
|
||||
<code>new ArrayCollection($parameters)</code>
|
||||
</InvalidPropertyAssignmentValue>
|
||||
<MethodSignatureMustProvideReturnType>
|
||||
<code>__toString</code>
|
||||
</MethodSignatureMustProvideReturnType>
|
||||
<PossiblyFalseArgument>
|
||||
<code>$spacePos</code>
|
||||
<code>$spacePos</code>
|
||||
@@ -2748,6 +2705,7 @@
|
||||
<NoValue>
|
||||
<code>$entityState</code>
|
||||
<code>$entityState</code>
|
||||
<code>$object</code>
|
||||
</NoValue>
|
||||
<PossiblyInvalidArgument>
|
||||
<code>$value</code>
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional;
|
||||
|
||||
use Closure;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\Query\Exec\SingleSelectExecutor;
|
||||
use Doctrine\ORM\Query\ParserResult;
|
||||
@@ -12,6 +13,8 @@ use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
use Generator;
|
||||
use ReflectionMethod;
|
||||
use ReflectionProperty;
|
||||
use Symfony\Component\VarExporter\Instantiator;
|
||||
use Symfony\Component\VarExporter\VarExporter;
|
||||
|
||||
use function file_get_contents;
|
||||
use function rtrim;
|
||||
@@ -27,14 +30,18 @@ class ParserResultSerializationTest extends OrmFunctionalTestCase
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testSerializeParserResult(): void
|
||||
/**
|
||||
* @param Closure(ParserResult): ParserResult $toSerializedAndBack
|
||||
*
|
||||
* @dataProvider provideToSerializedAndBack
|
||||
*/
|
||||
public function testSerializeParserResult(Closure $toSerializedAndBack): void
|
||||
{
|
||||
$query = $this->_em
|
||||
->createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyEmployee u WHERE u.name = :name');
|
||||
|
||||
$parserResult = self::parseQuery($query);
|
||||
$serialized = serialize($parserResult);
|
||||
$unserialized = unserialize($serialized);
|
||||
$unserialized = $toSerializedAndBack($parserResult);
|
||||
|
||||
$this->assertInstanceOf(ParserResult::class, $unserialized);
|
||||
$this->assertInstanceOf(ResultSetMapping::class, $unserialized->getResultSetMapping());
|
||||
@@ -42,6 +49,27 @@ class ParserResultSerializationTest extends OrmFunctionalTestCase
|
||||
$this->assertInstanceOf(SingleSelectExecutor::class, $unserialized->getSqlExecutor());
|
||||
}
|
||||
|
||||
/** @return Generator<string, array{Closure(ParserResult): ParserResult}> */
|
||||
public function provideToSerializedAndBack(): Generator
|
||||
{
|
||||
yield 'native serialization function' => [
|
||||
static function (ParserResult $parserResult): ParserResult {
|
||||
return unserialize(serialize($parserResult));
|
||||
},
|
||||
];
|
||||
|
||||
$instantiatorMethod = new ReflectionMethod(Instantiator::class, 'instantiate');
|
||||
if ($instantiatorMethod->getReturnType() === null) {
|
||||
$this->markTestSkipped('symfony/var-exporter 5.4+ is required.');
|
||||
}
|
||||
|
||||
yield 'symfony/var-exporter' => [
|
||||
static function (ParserResult $parserResult): ParserResult {
|
||||
return eval('return ' . VarExporter::export($parserResult) . ';');
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
public function testItSerializesParserResultWithAForwardCompatibleFormat(): void
|
||||
{
|
||||
$query = $this->_em
|
||||
@@ -88,6 +116,25 @@ class ParserResultSerializationTest extends OrmFunctionalTestCase
|
||||
yield '2.17.0' => [rtrim(file_get_contents(__DIR__ . '/ParserResults/single_select_2_17_0.txt'), "\n")];
|
||||
}
|
||||
|
||||
public function testSymfony44ProvidedData(): void
|
||||
{
|
||||
$sqlExecutor = $this->createMock(SingleSelectExecutor::class);
|
||||
$resultSetMapping = $this->createMock(ResultSetMapping::class);
|
||||
|
||||
$parserResult = new ParserResult();
|
||||
$parserResult->setSqlExecutor($sqlExecutor);
|
||||
$parserResult->setResultSetMapping($resultSetMapping);
|
||||
$parserResult->addParameterMapping('name', 0);
|
||||
|
||||
$exported = VarExporter::export($parserResult);
|
||||
$unserialized = eval('return ' . $exported . ';');
|
||||
|
||||
$this->assertInstanceOf(ParserResult::class, $unserialized);
|
||||
$this->assertInstanceOf(ResultSetMapping::class, $unserialized->getResultSetMapping());
|
||||
$this->assertEquals(['name' => [0]], $unserialized->getParameterMappings());
|
||||
$this->assertInstanceOf(SingleSelectExecutor::class, $unserialized->getSqlExecutor());
|
||||
}
|
||||
|
||||
private static function parseQuery(Query $query): ParserResult
|
||||
{
|
||||
$r = new ReflectionMethod($query, 'parse');
|
||||
|
||||
@@ -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.",
|
||||
@@ -50,4 +54,9 @@ final class GH10661Test extends OrmTestCase
|
||||
$ce
|
||||
);
|
||||
}
|
||||
|
||||
private function bootstrapValidator(bool $validatePropertyTypes = true): SchemaValidator
|
||||
{
|
||||
return new SchemaValidator($this->em, $validatePropertyTypes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket\GH11037;
|
||||
|
||||
interface EntityStatus
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket\GH11037;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Tools\SchemaValidator;
|
||||
use Doctrine\Tests\OrmTestCase;
|
||||
|
||||
/**
|
||||
* @requires PHP >= 8.1
|
||||
*/
|
||||
final class GH11037Test extends OrmTestCase
|
||||
{
|
||||
/** @var EntityManagerInterface */
|
||||
private $em;
|
||||
|
||||
/** @var SchemaValidator */
|
||||
private $validator;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->em = $this->getTestEntityManager();
|
||||
$this->validator = new SchemaValidator($this->em);
|
||||
}
|
||||
|
||||
public function testMetadataFieldTypeCoherentWithEntityPropertyType(): void
|
||||
{
|
||||
$class = $this->em->getClassMetadata(ValidEntityWithTypedEnum::class);
|
||||
$ce = $this->validator->validateClass($class);
|
||||
|
||||
self::assertEquals([], $ce);
|
||||
}
|
||||
|
||||
public function testMetadataFieldTypeNotCoherentWithEntityPropertyType(): void
|
||||
{
|
||||
$class = $this->em->getClassMetadata(InvalidEntityWithTypedEnum::class);
|
||||
$ce = $this->validator->validateClass($class);
|
||||
|
||||
self::assertEquals(
|
||||
[
|
||||
"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
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket\GH11037;
|
||||
|
||||
enum IntEntityStatus: int
|
||||
{
|
||||
case ACTIVE = 0;
|
||||
case INACTIVE = 1;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket\GH11037;
|
||||
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\Id;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class InvalidEntityWithTypedEnum
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @Column
|
||||
*/
|
||||
protected int $id;
|
||||
|
||||
/**
|
||||
* @Column(type="integer", enumType=StringEntityStatus::class)
|
||||
*/
|
||||
protected StringEntityStatus $status1;
|
||||
|
||||
/**
|
||||
* @Column(type="integer", enumType=StringEntityStatus::class)
|
||||
*/
|
||||
protected IntEntityStatus $status2;
|
||||
|
||||
/**
|
||||
* @Column(type="integer", enumType=StringEntityStatus::class)
|
||||
*/
|
||||
protected EntityStatus $status3;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket\GH11037;
|
||||
|
||||
enum StringEntityStatus: string implements EntityStatus
|
||||
{
|
||||
case ACTIVE = 'active';
|
||||
case INACTIVE = 'inactive';
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket\GH11037;
|
||||
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\Id;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class ValidEntityWithTypedEnum
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @Column
|
||||
*/
|
||||
protected int $id;
|
||||
|
||||
/**
|
||||
* @Column(type="string", enumType=StringEntityStatus::class)
|
||||
*/
|
||||
protected StringEntityStatus $status1;
|
||||
|
||||
/**
|
||||
* @Column(type="smallint", enumType=IntEntityStatus::class)
|
||||
*/
|
||||
protected IntEntityStatus $status2;
|
||||
|
||||
/**
|
||||
* @Column(type="string", enumType=StringEntityStatus::class)
|
||||
*/
|
||||
protected EntityStatus $status3;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket\GH11072;
|
||||
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class GH11072EntityAdvanced extends GH11072EntityBasic
|
||||
{
|
||||
/** @Column(type="json") */
|
||||
public mixed $anything;
|
||||
|
||||
/** @Column(type="json") */
|
||||
public true $alwaysTrue = true;
|
||||
|
||||
/** @Column(type="json") */
|
||||
public false $alwaysFalse = false;
|
||||
|
||||
/** @Column(type="json") */
|
||||
public null $alwaysNull = null;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket\GH11072;
|
||||
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\GeneratedValue;
|
||||
use Doctrine\ORM\Mapping\Id;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class GH11072EntityBasic
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @Column(type="integer")
|
||||
* @GeneratedValue
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/** @Column(type="json") */
|
||||
public string $jsonString = 'test';
|
||||
|
||||
/** @Column(type="json") */
|
||||
public int $age = 99;
|
||||
|
||||
/** @Column(type="json") */
|
||||
public float $score = 0.0;
|
||||
|
||||
/** @Column(type="json", nullable=true) */
|
||||
public ?bool $trinary = null;
|
||||
|
||||
/** @Column(type="json") */
|
||||
public array $metadata = [];
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket\GH11072;
|
||||
|
||||
use Doctrine\ORM\Tools\SchemaValidator;
|
||||
use Doctrine\Tests\OrmFunctionalTestCase;
|
||||
|
||||
/**
|
||||
* @requires PHP >= 7.4
|
||||
*/
|
||||
final class GH11072Test extends OrmFunctionalTestCase
|
||||
{
|
||||
/** @var SchemaValidator */
|
||||
private $validator;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->_em = $this->getTestEntityManager();
|
||||
$this->validator = new SchemaValidator($this->_em);
|
||||
}
|
||||
|
||||
public function testAcceptsSubsetOfBuiltinTypesWithoutErrors(): void
|
||||
{
|
||||
$class = $this->_em->getClassMetadata(GH11072EntityBasic::class);
|
||||
$ce = $this->validator->validateClass($class);
|
||||
|
||||
self::assertSame([], $ce);
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP >= 8.2
|
||||
*/
|
||||
public function testAcceptsAdvancedSubsetOfBuiltinTypesWithoutErrors(): void
|
||||
{
|
||||
$class = $this->_em->getClassMetadata(GH11072EntityAdvanced::class);
|
||||
$ce = $this->validator->validateClass($class);
|
||||
|
||||
self::assertSame([], $ce);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user