From 66a6f3ff7f6502bd06e3225c147df1f93c4d3676 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 Nov 2023 08:56:36 +0100 Subject: [PATCH] Merge release 2.5.5 into 2.6.x (#2585) * fix: Do not drop/re-create 2dsphere indexes (#2575) * Validate that targetDocument can be resolved (#2577) * fix: Validate that targetDocument can be resolved * refactor: Use exception factory * fix: Validate that all values in a discriminatorMap can be resolved * fix: PHPStan errors for intentionally missing class * fix: psalm/phpunit CI tests --------- Co-authored-by: buffcode Co-authored-by: Andreas Braun --- .../ODM/MongoDB/Mapping/ClassMetadata.php | 17 +++ .../ODM/MongoDB/Mapping/MappingException.php | 10 ++ lib/Doctrine/ODM/MongoDB/SchemaManager.php | 1 + .../MongoDB/Tests/Functional/IndexesTest.php | 39 ++++++ .../Tests/Functional/TargetDocumentTest.php | 124 ++++++++++++++++++ .../Tests/Functional/TestTargetDocument.php | 56 -------- .../ODM/MongoDB/Tests/SchemaManagerTest.php | 6 + 7 files changed, 197 insertions(+), 56 deletions(-) create mode 100644 tests/Doctrine/ODM/MongoDB/Tests/Functional/TargetDocumentTest.php delete mode 100644 tests/Doctrine/ODM/MongoDB/Tests/Functional/TestTargetDocument.php diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php b/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php index cccdb5ef07..2cc251620b 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php @@ -41,6 +41,7 @@ use function enum_exists; use function extension_loaded; use function in_array; +use function interface_exists; use function is_array; use function is_string; use function is_subclass_of; @@ -2310,6 +2311,22 @@ public function mapField(array $mapping): array $mapping['discriminatorField'] = self::DEFAULT_DISCRIMINATOR_FIELD; } + if (isset($mapping['targetDocument']) && ! class_exists($mapping['targetDocument']) && ! interface_exists($mapping['targetDocument'])) { + throw MappingException::invalidTargetDocument( + $mapping['targetDocument'], + $this->name, + $mapping['fieldName'], + ); + } + + if (isset($mapping['discriminatorMap'])) { + foreach ($mapping['discriminatorMap'] as $value => $class) { + if (! class_exists($class) && ! interface_exists($class)) { + throw MappingException::invalidClassInReferenceDiscriminatorMap($class, $this->name, $mapping['fieldName']); + } + } + } + if (isset($mapping['version'])) { $mapping['notSaved'] = true; $this->setVersionMapping($mapping); diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/MappingException.php b/lib/Doctrine/ODM/MongoDB/Mapping/MappingException.php index 34ea4d7c51..af7d22a28f 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/MappingException.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/MappingException.php @@ -72,6 +72,11 @@ public static function invalidClassInDiscriminatorMap(string $className, string return new self(sprintf("Document class '%s' used in the discriminator map of class '%s' does not exist.", $className, $owningClass)); } + public static function invalidClassInReferenceDiscriminatorMap(string $className, string $owningClass, string $fieldName): self + { + return new self(sprintf("Document class '%s' used in the discriminator map of field '%s' in class '%s' does not exist.", $className, $fieldName, $owningClass)); + } + public static function unlistedClassInDiscriminatorMap(string $className): self { return new self(sprintf('Document class "%s" is unlisted in the discriminator map.', $className)); @@ -82,6 +87,11 @@ public static function invalidDiscriminatorValue(string $value, string $owningCl return new self(sprintf("Discriminator value '%s' used in the declaration of class '%s' does not exist.", $value, $owningClass)); } + public static function invalidTargetDocument(string $targetDocument, string $owningClass, string $owningField): self + { + return new self(sprintf("Target document class '%s' used in field '%s' of class '%s' does not exist.", $targetDocument, $owningField, $owningClass)); + } + public static function missingFieldName(string $className): self { return new self(sprintf("The Document class '%s' field mapping misses the 'fieldName' attribute.", $className)); diff --git a/lib/Doctrine/ODM/MongoDB/SchemaManager.php b/lib/Doctrine/ODM/MongoDB/SchemaManager.php index bfdf96f3bb..f2f8e8dc18 100644 --- a/lib/Doctrine/ODM/MongoDB/SchemaManager.php +++ b/lib/Doctrine/ODM/MongoDB/SchemaManager.php @@ -52,6 +52,7 @@ final class SchemaManager 'language_override', 'textIndexVersion', 'name', + '2dsphereIndexVersion', ]; public function __construct(protected DocumentManager $dm, protected ClassMetadataFactoryInterface $metadataFactory) diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/IndexesTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/IndexesTest.php index 4dd7567023..a0801f25f5 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/IndexesTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/IndexesTest.php @@ -239,6 +239,16 @@ public function testPartialIndexCreation(): void self::assertSame(['counter' => ['$gt' => 5]], $indexes[0]['options']['partialFilterExpression']); self::assertTrue($indexes[0]['options']['unique']); } + + public function testGeoIndexCreation(): void + { + $className = GeoIndexDocument::class; + $this->dm->getSchemaManager()->ensureDocumentIndexes(GeoIndexDocument::class); + + $indexes = $this->dm->getSchemaManager()->getDocumentIndexes($className); + self::assertSame(['coordinatesWith2DIndex' => '2d'], $indexes[0]['keys']); + self::assertSame(['coordinatesWithSphereIndex' => '2dsphere'], $indexes[1]['keys']); + } } /** @ODM\Document */ @@ -657,3 +667,32 @@ class DocumentWithIndexInDiscriminatedEmbeds */ public $embedded; } + +/** + * @ODM\Document + * @ODM\Index(keys={"coordinatesWith2DIndex"="2d"}) + * @ODM\Index(keys={"coordinatesWithSphereIndex"="2dsphere"}) + */ +class GeoIndexDocument +{ + /** + * @ODM\Id + * + * @var string|null + */ + public $id; + + /** + * @ODM\Field(type="hash") + * + * @var array + */ + public $coordinatesWith2DIndex; + + /** + * @ODM\Field(type="hash") + * + * @var array + */ + public $coordinatesWithSphereIndex; +} diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/TargetDocumentTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/TargetDocumentTest.php new file mode 100644 index 0000000000..3ab4183581 --- /dev/null +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/TargetDocumentTest.php @@ -0,0 +1,124 @@ +reference = new TargetDocumentTestReference(); + $this->dm->persist($test); + $this->dm->persist($test->reference); + $this->dm->flush(); + } + + public function testTargetDocumentIsResolvable(): void + { + self::expectExceptionObject( + MappingException::invalidTargetDocument( + 'Doctrine\ODM\MongoDB\Tests\Functional\SomeInvalidClass', + InvalidTargetDocumentTestDocument::class, + 'reference', + ), + ); + + $test = new InvalidTargetDocumentTestDocument(); + $test->reference = new stdClass(); + $this->dm->persist($test); + } + + public function testDiscriminatorTargetIsResolvable(): void + { + self::expectExceptionObject( + MappingException::invalidClassInReferenceDiscriminatorMap( + 'Doctrine\ODM\MongoDB\Tests\Functional\SomeInvalidClass', + InvalidDiscriminatorTargetsTestDocument::class, + 'reference', + ), + ); + + $test = new InvalidDiscriminatorTargetsTestDocument(); + $test->reference = new stdClass(); + $this->dm->persist($test); + } +} + +/** @ODM\Document */ +class TargetDocumentTestDocument +{ + /** + * @ODM\Id + * + * @var string|null + */ + public $id; + + /** + * @ODM\ReferenceOne(targetDocument=Doctrine\ODM\MongoDB\Tests\Functional\TargetDocumentTestReference::class) + * + * @var TargetDocumentTestReference|null + */ + public $reference; +} + +/** @ODM\MappedSuperclass */ +abstract class AbstractTargetDocumentTestReference +{ + /** + * @ODM\Id + * + * @var string|null + */ + public $id; +} + +/** @ODM\Document */ +class TargetDocumentTestReference extends AbstractTargetDocumentTestReference +{ +} + +/** @ODM\Document */ +class InvalidTargetDocumentTestDocument +{ + /** + * @ODM\Id + * + * @var string|null + */ + public $id; + + /** + * @ODM\ReferenceOne(targetDocument="Doctrine\ODM\MongoDB\Tests\Functional\SomeInvalidClass") + * + * @var object|null + */ + public $reference; +} + + +/** @ODM\Document */ +class InvalidDiscriminatorTargetsTestDocument +{ + /** + * @ODM\Id + * + * @var string|null + */ + public $id; + + /** + * @ODM\ReferenceOne(discriminatorField="referencedClass", discriminatorMap={"Foo"="Doctrine\ODM\MongoDB\Tests\Functional\SomeInvalidClass"}) + * + * @var object|null + */ + public $reference; +} diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/TestTargetDocument.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/TestTargetDocument.php deleted file mode 100644 index d7c6bad7e0..0000000000 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/TestTargetDocument.php +++ /dev/null @@ -1,56 +0,0 @@ -reference = new TargetDocumentTestReference(); - $this->dm->persist($test); - $this->dm->persist($test->reference); - $this->dm->flush(); - } -} - -/** @ODM\Document */ -class TargetDocumentTestDocument -{ - /** - * @ODM\Id - * - * @var string|null - */ - public $id; - - /** - * @ODM\ReferenceOne(targetDocument=Doctrine\ODM\MongoDB\Tests\Functional\TargetDocumentTestReference::class) - * - * @var TargetDocumentTestReference|null - */ - public $reference; -} - -/** @ODM\MappedSuperclass */ -abstract class AbstractTargetDocumentTestReference -{ - /** - * @ODM\Id - * - * @var string|null - */ - public $id; -} - -/** @ODM\Document */ -class TargetDocumentTestReference extends AbstractTargetDocumentTestReference -{ -} diff --git a/tests/Doctrine/ODM/MongoDB/Tests/SchemaManagerTest.php b/tests/Doctrine/ODM/MongoDB/Tests/SchemaManagerTest.php index c9e1a1d79e..d399db41c1 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/SchemaManagerTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/SchemaManagerTest.php @@ -934,6 +934,12 @@ public static function dataIsMongoIndexEquivalentToDocumentIndex(): array 'mongoIndex' => ['background' => true], 'documentIndex' => ['options' => ['background' => true]], ], + // 2dsphereIndexVersion index options + '2dsphereIndexVersionOptionsDifferent' => [ + 'expected' => true, + 'mongoIndex' => ['2dsphereIndexVersion' => 3], + 'documentIndex' => [], + ], ]; }