diff --git a/composer.lock b/composer.lock index c833faf44..ff525b0a8 100644 --- a/composer.lock +++ b/composer.lock @@ -512,16 +512,16 @@ }, { "name": "fakerphp/faker", - "version": "v1.21.0", + "version": "v1.22.0", "source": { "type": "git", "url": "https://github.com/FakerPHP/Faker.git", - "reference": "92efad6a967f0b79c499705c69b662f738cc9e4d" + "reference": "f85772abd508bd04e20bb4b1bbe260a68d0066d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/92efad6a967f0b79c499705c69b662f738cc9e4d", - "reference": "92efad6a967f0b79c499705c69b662f738cc9e4d", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/f85772abd508bd04e20bb4b1bbe260a68d0066d2", + "reference": "f85772abd508bd04e20bb4b1bbe260a68d0066d2", "shasum": "" }, "require": { @@ -574,9 +574,9 @@ ], "support": { "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.21.0" + "source": "https://github.com/FakerPHP/Faker/tree/v1.22.0" }, - "time": "2022-12-13T13:54:32+00:00" + "time": "2023-05-14T12:31:37+00:00" }, { "name": "laravel/pint", @@ -705,16 +705,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.15.4", + "version": "v4.15.5", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e", + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e", "shasum": "" }, "require": { @@ -755,9 +755,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5" }, - "time": "2023-03-05T19:49:14+00:00" + "time": "2023-05-19T20:20:00+00:00" }, { "name": "pcov/clobber", @@ -906,16 +906,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.14", + "version": "1.10.15", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "d232901b09e67538e5c86a724be841bea5768a7c" + "reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d232901b09e67538e5c86a724be841bea5768a7c", - "reference": "d232901b09e67538e5c86a724be841bea5768a7c", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/762c4dac4da6f8756eebb80e528c3a47855da9bd", + "reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd", "shasum": "" }, "require": { @@ -964,7 +964,7 @@ "type": "tidelift" } ], - "time": "2023-04-19T13:47:27+00:00" + "time": "2023-05-09T15:28:01+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1286,16 +1286,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.7", + "version": "9.6.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2" + "reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c993f0d3b0489ffc42ee2fe0bd645af1538a63b2", - "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/17d621b3aff84d0c8b62539e269e87d8d5baa76e", + "reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e", "shasum": "" }, "require": { @@ -1369,7 +1369,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.7" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.8" }, "funding": [ { @@ -1385,7 +1385,7 @@ "type": "tidelift" } ], - "time": "2023-04-14T08:58:40+00:00" + "time": "2023-05-11T05:14:45+00:00" }, { "name": "psr/container", @@ -1786,16 +1786,16 @@ }, { "name": "sebastian/diff", - "version": "4.0.4", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", "shasum": "" }, "require": { @@ -1840,7 +1840,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" }, "funding": [ { @@ -1848,7 +1848,7 @@ "type": "github" } ], - "time": "2020-10-26T13:10:38+00:00" + "time": "2023-05-07T05:35:17+00:00" }, { "name": "sebastian/environment", @@ -2673,5 +2673,5 @@ "php": ">=8.0" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.2.0" } diff --git a/src/Database/Database.php b/src/Database/Database.php index 986f263d5..053b41d39 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -1474,6 +1474,13 @@ public function createRelationship( if (\strtolower($attribute->getId()) === \strtolower($id)) { throw new DuplicateException('Attribute already exists'); } + + if ($attribute->getAttribute('type') === self::VAR_RELATIONSHIP + && \strtolower($attribute->getAttribute('options')['twoWayKey']) === \strtolower($twoWayKey) + && $attribute->getAttribute('options')['relatedCollection'] === $relatedCollection->getId() + ) { + throw new DuplicateException('Related attribute already exists'); + } } if ( @@ -1663,7 +1670,7 @@ public function updateRelationship( !\is_null($newTwoWayKey) && \in_array($newTwoWayKey, \array_map(fn ($attribute) => $attribute['key'], $relatedAttributes)) ) { - throw new DuplicateException('Attribute already exists'); + throw new DuplicateException('Related attribute already exists'); } $type = $attribute['options']['relationType']; diff --git a/tests/Database/Base.php b/tests/Database/Base.php index edbaf7d84..9697fabb7 100644 --- a/tests/Database/Base.php +++ b/tests/Database/Base.php @@ -4733,6 +4733,103 @@ public function testOneToOneTwoWayRelationship(): void $this->assertEquals(null, $country); } + public function testIdenticalTwoWayKeyRelationship(): void + { + if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { + $this->expectNotToPerformAssertions(); + return; + } + + static::getDatabase()->createCollection('parent'); + static::getDatabase()->createCollection('child'); + + static::getDatabase()->createRelationship( + collection: 'parent', + relatedCollection: 'child', + type: Database::RELATION_ONE_TO_ONE, + id: 'child1' + ); + + try { + static::getDatabase()->createRelationship( + collection: 'parent', + relatedCollection: 'child', + type: Database::RELATION_ONE_TO_MANY, + id: 'children', + ); + $this->fail('Failed to throw Exception'); + } catch (Exception $e) { + $this->assertEquals('Related attribute already exists', $e->getMessage()); + } + + static::getDatabase()->createRelationship( + collection: 'parent', + relatedCollection: 'child', + type: Database::RELATION_ONE_TO_MANY, + id: 'children', + twoWayKey: 'parent_id' + ); + + $collection = static::getDatabase()->getCollection('parent'); + $attributes = $collection->getAttribute('attributes', []); + foreach ($attributes as $attribute) { + if ($attribute['key'] === 'child1') { + $this->assertEquals('parent', $attribute['options']['twoWayKey']); + } + + if ($attribute['key'] === 'children') { + $this->assertEquals('parent_id', $attribute['options']['twoWayKey']); + } + } + + static::getDatabase()->createDocument('parent', new Document([ + '$permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'child1' => [ + '$id' => 'foo', + '$permissions' => [Permission::read(Role::any())], + ], + 'children' => [ + [ + '$id' => 'bar', + '$permissions' => [Permission::read(Role::any())], + ], + ], + ])); + + $documents = static::getDatabase()->find('parent', []); + $document = array_pop($documents); + $this->assertArrayHasKey('child1', $document); + $this->assertEquals('foo', $document->getAttribute('child1')->getId()); + $this->assertArrayHasKey('children', $document); + $this->assertEquals('bar', $document->getAttribute('children')[0]->getId()); + + try { + static::getDatabase()->updateRelationship( + collection: 'parent', + id: 'children', + newKey: 'child1' + ); + $this->fail('Failed to throw Exception'); + } catch (Exception $e) { + $this->assertEquals('Attribute already exists', $e->getMessage()); + } + + try { + static::getDatabase()->updateRelationship( + collection: 'parent', + id: 'children', + newTwoWayKey: 'parent' + ); + $this->fail('Failed to throw Exception'); + } catch (Exception $e) { + $this->assertEquals('Related attribute already exists', $e->getMessage()); + } + } + public function testOneToManyOneWayRelationship(): void { if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) { @@ -10164,7 +10261,7 @@ public function testUpdateRelationshipToExistingKey(): void static::getDatabase()->updateRelationship('ovens', 'cakes', newTwoWayKey: 'height'); $this->fail('Failed to throw exception'); } catch (DuplicateException $e) { - $this->assertEquals('Attribute already exists', $e->getMessage()); + $this->assertEquals('Related attribute already exists', $e->getMessage()); } }