Skip to content

Commit 4174c5c

Browse files
committed
Fix one-way relationship twoWayKey handling in createRelationship
1 parent bb89e8c commit 4174c5c

File tree

3 files changed

+93
-24
lines changed

3 files changed

+93
-24
lines changed

src/Database/Database.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3563,8 +3563,6 @@ public function createRelationship(
35633563

35643564
$id ??= $relatedCollection->getId();
35653565

3566-
$twoWayKey ??= $collection->getId();
3567-
35683566
$attributes = $collection->getAttribute('attributes', []);
35693567
/** @var array<Document> $attributes */
35703568
foreach ($attributes as $attribute) {
@@ -3573,14 +3571,18 @@ public function createRelationship(
35733571
}
35743572

35753573
if (
3576-
$attribute->getAttribute('type') === self::VAR_RELATIONSHIP
3574+
$twoWay
3575+
&& $attribute->getAttribute('type') === self::VAR_RELATIONSHIP
3576+
&& isset($attribute->getAttribute('options')['twoWayKey'])
35773577
&& \strtolower($attribute->getAttribute('options')['twoWayKey']) === \strtolower($twoWayKey)
35783578
&& $attribute->getAttribute('options')['relatedCollection'] === $relatedCollection->getId()
35793579
) {
35803580
throw new DuplicateException('Related attribute already exists');
35813581
}
35823582
}
35833583

3584+
$twoWayKey ??= $collection->getId();
3585+
35843586
$relationship = new Document([
35853587
'$id' => ID::custom($id),
35863588
'key' => $id,

tests/e2e/Adapter/Scopes/Relationships/ManyToOneTests.php

Lines changed: 86 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Exception;
66
use Utopia\Database\Database;
77
use Utopia\Database\Document;
8+
use Utopia\Database\Exception\Duplicate as DuplicateException;
89
use Utopia\Database\Exception\Restricted as RestrictedException;
910
use Utopia\Database\Exception\Structure;
1011
use Utopia\Database\Helpers\ID;
@@ -35,8 +36,7 @@ public function testManyToOneOneWayRelationship(): void
3536
$database->createRelationship(
3637
collection: 'review',
3738
relatedCollection: 'movie',
38-
type: Database::RELATION_MANY_TO_ONE,
39-
twoWayKey: 'reviews'
39+
type: Database::RELATION_MANY_TO_ONE
4040
);
4141

4242
// Check metadata for collection
@@ -50,18 +50,18 @@ public function testManyToOneOneWayRelationship(): void
5050
$this->assertEquals('movie', $attribute['options']['relatedCollection']);
5151
$this->assertEquals(Database::RELATION_MANY_TO_ONE, $attribute['options']['relationType']);
5252
$this->assertEquals(false, $attribute['options']['twoWay']);
53-
$this->assertEquals('reviews', $attribute['options']['twoWayKey']);
53+
$this->assertEquals('review', $attribute['options']['twoWayKey']);
5454
}
5555
}
5656

5757
// Check metadata for related collection
5858
$collection = $database->getCollection('movie');
5959
$attributes = $collection->getAttribute('attributes', []);
6060
foreach ($attributes as $attribute) {
61-
if ($attribute['key'] === 'reviews') {
61+
if ($attribute['key'] === 'review') {
6262
$this->assertEquals('relationship', $attribute['type']);
63-
$this->assertEquals('reviews', $attribute['$id']);
64-
$this->assertEquals('reviews', $attribute['key']);
63+
$this->assertEquals('review', $attribute['$id']);
64+
$this->assertEquals('review', $attribute['key']);
6565
$this->assertEquals('review', $attribute['options']['relatedCollection']);
6666
$this->assertEquals(Database::RELATION_MANY_TO_ONE, $attribute['options']['relationType']);
6767
$this->assertEquals(false, $attribute['options']['twoWay']);
@@ -816,6 +816,86 @@ public function testManyToOneTwoWayRelationship(): void
816816
$this->assertEquals(null, $products);
817817
}
818818

819+
public function testManyToOneOneWayRelationshipDoesNotValidateTwoWayKeyDuplicates(): void
820+
{
821+
/** @var Database $database */
822+
$database = $this->getDatabase();
823+
824+
if (!$database->getAdapter()->getSupportForRelationships()) {
825+
$this->expectNotToPerformAssertions();
826+
return;
827+
}
828+
829+
$database->createCollection('critic');
830+
$database->createCollection('film');
831+
832+
$database->createAttribute('critic', 'name', Database::VAR_STRING, 255, true);
833+
$database->createAttribute('film', 'name', Database::VAR_STRING, 255, true);
834+
835+
$database->createRelationship(
836+
collection: 'critic',
837+
relatedCollection: 'film',
838+
type: Database::RELATION_MANY_TO_ONE,
839+
twoWay: false,
840+
id: 'favoriteFilm',
841+
twoWayKey: 'films'
842+
);
843+
844+
$database->createRelationship(
845+
collection: 'critic',
846+
relatedCollection: 'film',
847+
type: Database::RELATION_MANY_TO_ONE,
848+
twoWay: false,
849+
id: 'leastFavoriteFilm',
850+
twoWayKey: 'films'
851+
);
852+
853+
$collection = $database->getCollection('critic');
854+
$attributes = $collection->getAttribute('attributes', []);
855+
$keys = \array_map(fn (Document $attribute) => $attribute->getId(), $attributes);
856+
857+
$this->assertContains('favoriteFilm', $keys);
858+
$this->assertContains('leastFavoriteFilm', $keys);
859+
}
860+
861+
public function testManyToOneTwoWayRelationshipStillValidatesTwoWayKeyDuplicates(): void
862+
{
863+
/** @var Database $database */
864+
$database = $this->getDatabase();
865+
866+
if (!$database->getAdapter()->getSupportForRelationships()) {
867+
$this->expectNotToPerformAssertions();
868+
return;
869+
}
870+
871+
$database->createCollection('employee');
872+
$database->createCollection('department');
873+
874+
$database->createAttribute('employee', 'name', Database::VAR_STRING, 255, true);
875+
$database->createAttribute('department', 'name', Database::VAR_STRING, 255, true);
876+
877+
$database->createRelationship(
878+
collection: 'employee',
879+
relatedCollection: 'department',
880+
type: Database::RELATION_MANY_TO_ONE,
881+
twoWay: true,
882+
id: 'primaryDepartment',
883+
twoWayKey: 'employees'
884+
);
885+
886+
$this->expectException(DuplicateException::class);
887+
$this->expectExceptionMessage('Related attribute already exists');
888+
889+
$database->createRelationship(
890+
collection: 'employee',
891+
relatedCollection: 'department',
892+
type: Database::RELATION_MANY_TO_ONE,
893+
twoWay: true,
894+
id: 'secondaryDepartment',
895+
twoWayKey: 'employees'
896+
);
897+
}
898+
819899
public function testNestedManyToOne_OneToOneRelationship(): void
820900
{
821901
/** @var Database $database */

tests/e2e/Adapter/Scopes/Relationships/OneToOneTests.php

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,24 +1024,11 @@ public function testIdenticalTwoWayKeyRelationship(): void
10241024
id: 'child1'
10251025
);
10261026

1027-
try {
1028-
$database->createRelationship(
1029-
collection: 'parent',
1030-
relatedCollection: 'child',
1031-
type: Database::RELATION_ONE_TO_MANY,
1032-
id: 'children',
1033-
);
1034-
$this->fail('Failed to throw Exception');
1035-
} catch (Exception $e) {
1036-
$this->assertEquals('Related attribute already exists', $e->getMessage());
1037-
}
1038-
10391027
$database->createRelationship(
10401028
collection: 'parent',
10411029
relatedCollection: 'child',
10421030
type: Database::RELATION_ONE_TO_MANY,
1043-
id: 'children',
1044-
twoWayKey: 'parent_id'
1031+
id: 'children'
10451032
);
10461033

10471034
$collection = $database->getCollection('parent');
@@ -1052,7 +1039,7 @@ public function testIdenticalTwoWayKeyRelationship(): void
10521039
}
10531040

10541041
if ($attribute['key'] === 'children') {
1055-
$this->assertEquals('parent_id', $attribute['options']['twoWayKey']);
1042+
$this->assertEquals('parent', $attribute['options']['twoWayKey']);
10561043
}
10571044
}
10581045

0 commit comments

Comments
 (0)