Skip to content

Commit 968fff6

Browse files
committed
Merge branch 'main' of github.com:utopia-php/database into unique-exception
# Conflicts: # tests/e2e/Adapter/Scopes/DocumentTests.php
2 parents 6a9c42a + 141338a commit 968fff6

8 files changed

Lines changed: 1269 additions & 113 deletions

File tree

composer.lock

Lines changed: 96 additions & 95 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Database/Adapter/Postgres.php

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,9 @@ public function create(string $name): bool
152152
$this->getPDO()->prepare('CREATE EXTENSION IF NOT EXISTS postgis;')->execute();
153153

154154
$collation = "
155-
CREATE COLLATION IF NOT EXISTS utf8_ci (
155+
CREATE COLLATION IF NOT EXISTS utf8_ci_ai (
156156
provider = icu,
157-
locale = 'und-u-ks-primary',
157+
locale = 'und-u-ks-level1',
158158
deterministic = false
159159
);
160160
";
@@ -245,14 +245,14 @@ public function createCollection(string $name, array $attributes = [], array $in
245245

246246
if ($this->sharedTables) {
247247
$collection .= "
248-
CREATE UNIQUE INDEX \"{$namespace}_{$this->tenant}_{$id}_uid\" ON {$this->getSQLTable($id)} (\"_uid\", \"_tenant\");
248+
CREATE UNIQUE INDEX \"{$namespace}_{$this->tenant}_{$id}_uid\" ON {$this->getSQLTable($id)} (\"_uid\" COLLATE utf8_ci_ai, \"_tenant\");
249249
CREATE INDEX \"{$namespace}_{$this->tenant}_{$id}_created\" ON {$this->getSQLTable($id)} (_tenant, \"_createdAt\");
250250
CREATE INDEX \"{$namespace}_{$this->tenant}_{$id}_updated\" ON {$this->getSQLTable($id)} (_tenant, \"_updatedAt\");
251251
CREATE INDEX \"{$namespace}_{$this->tenant}_{$id}_tenant_id\" ON {$this->getSQLTable($id)} (_tenant, _id);
252252
";
253253
} else {
254254
$collection .= "
255-
CREATE UNIQUE INDEX \"{$namespace}_{$id}_uid\" ON {$this->getSQLTable($id)} (\"_uid\");
255+
CREATE UNIQUE INDEX \"{$namespace}_{$id}_uid\" ON {$this->getSQLTable($id)} (\"_uid\" COLLATE utf8_ci_ai);
256256
CREATE INDEX \"{$namespace}_{$id}_created\" ON {$this->getSQLTable($id)} (\"_createdAt\");
257257
CREATE INDEX \"{$namespace}_{$id}_updated\" ON {$this->getSQLTable($id)} (\"_updatedAt\");
258258
";
@@ -280,7 +280,7 @@ public function createCollection(string $name, array $attributes = [], array $in
280280
} else {
281281
$permissions .= "
282282
CREATE UNIQUE INDEX \"{$namespace}_{$id}_ukey\"
283-
ON {$this->getSQLTable($id . '_perms')} USING btree (_document,_type,_permission);
283+
ON {$this->getSQLTable($id . '_perms')} USING btree (_document COLLATE utf8_ci_ai,_type,_permission);
284284
CREATE INDEX \"{$namespace}_{$id}_permission\"
285285
ON {$this->getSQLTable($id . '_perms')} USING btree (_permission,_type);
286286
";
@@ -853,15 +853,7 @@ public function createIndex(string $collection, string $id, string $type, array
853853
default => $this->filter($attr),
854854
};
855855

856-
if (Database::INDEX_UNIQUE === $type) {
857-
if (isset($indexAttributeTypes[$attr]) && $indexAttributeTypes[$attr] === Database::VAR_STRING) {
858-
$attributes[$i] = "\"{$attr}\" COLLATE utf8_ci {$order}";
859-
} else {
860-
$attributes[$i] = "\"{$attr}\" {$order}";
861-
}
862-
} else {
863-
$attributes[$i] = "\"{$attr}\" {$order}";
864-
}
856+
$attributes[$i] = "\"{$attr}\" {$order}";
865857
}
866858

867859
$sqlType = match ($type) {
@@ -909,7 +901,7 @@ public function createIndex(string $collection, string $id, string $type, array
909901
*/
910902
public function deleteIndex(string $collection, string $id): bool
911903
{
912-
$name = $this->filter($collection);
904+
$collection = $this->filter($collection);
913905
$id = $this->filter($id);
914906
$schemaName = $this->getDatabase();
915907

tests/e2e/Adapter/Scopes/DocumentTests.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4762,8 +4762,8 @@ public function testUniqueIndexDuplicate(): void
47624762

47634763
$this->fail('Failed to throw exception');
47644764
} catch (Throwable $e) {
4765-
$this->assertInstanceOf(UniqueException::class, $e);
47664765
$this->assertInstanceOf(DuplicateException::class, $e);
4766+
$this->assertInstanceOf(UniqueException::class, $e);
47674767
}
47684768
}
47694769
/**
@@ -4805,8 +4805,8 @@ public function testUniqueIndexDuplicateUpdate(): void
48054805

48064806
$this->fail('Failed to throw exception');
48074807
} catch (Throwable $e) {
4808-
$this->assertInstanceOf(UniqueException::class, $e);
48094808
$this->assertInstanceOf(DuplicateException::class, $e);
4809+
$this->assertInstanceOf(UniqueException::class, $e);
48104810
}
48114811
}
48124812

tests/e2e/Adapter/Scopes/RelationshipTests.php

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3252,6 +3252,252 @@ public function testRelationshipTypeQueries(): void
32523252
$database->deleteCollection('coursesMtm');
32533253
}
32543254

3255+
/**
3256+
* Test querying parent documents by relationship document $id
3257+
*/
3258+
public function testQueryByRelationshipId(): void
3259+
{
3260+
/** @var Database $database */
3261+
$database = static::getDatabase();
3262+
3263+
if (!$database->getAdapter()->getSupportForRelationships()) {
3264+
$this->expectNotToPerformAssertions();
3265+
return;
3266+
}
3267+
3268+
$database->createCollection('usersRelId');
3269+
$database->createCollection('postsRelId');
3270+
3271+
$database->createAttribute('usersRelId', 'name', Database::VAR_STRING, 255, true);
3272+
$database->createAttribute('postsRelId', 'title', Database::VAR_STRING, 255, true);
3273+
3274+
$database->createRelationship(
3275+
collection: 'postsRelId',
3276+
relatedCollection: 'usersRelId',
3277+
type: Database::RELATION_MANY_TO_ONE,
3278+
twoWay: true,
3279+
id: 'user',
3280+
twoWayKey: 'posts'
3281+
);
3282+
3283+
// Create test users
3284+
$user1 = $database->createDocument('usersRelId', new Document([
3285+
'$id' => 'user1',
3286+
'$permissions' => [
3287+
Permission::read(Role::any()),
3288+
Permission::update(Role::any()),
3289+
],
3290+
'name' => 'Alice',
3291+
]));
3292+
3293+
$user2 = $database->createDocument('usersRelId', new Document([
3294+
'$id' => 'user2',
3295+
'$permissions' => [
3296+
Permission::read(Role::any()),
3297+
Permission::update(Role::any()),
3298+
],
3299+
'name' => 'Bob',
3300+
]));
3301+
3302+
// Create posts related to users
3303+
$database->createDocument('postsRelId', new Document([
3304+
'$id' => 'post1',
3305+
'$permissions' => [
3306+
Permission::read(Role::any()),
3307+
Permission::update(Role::any()),
3308+
],
3309+
'title' => 'Alice Post 1',
3310+
'user' => 'user1',
3311+
]));
3312+
3313+
$database->createDocument('postsRelId', new Document([
3314+
'$id' => 'post2',
3315+
'$permissions' => [
3316+
Permission::read(Role::any()),
3317+
Permission::update(Role::any()),
3318+
],
3319+
'title' => 'Alice Post 2',
3320+
'user' => 'user1',
3321+
]));
3322+
3323+
$database->createDocument('postsRelId', new Document([
3324+
'$id' => 'post3',
3325+
'$permissions' => [
3326+
Permission::read(Role::any()),
3327+
Permission::update(Role::any()),
3328+
],
3329+
'title' => 'Bob Post',
3330+
'user' => 'user2',
3331+
]));
3332+
3333+
// Query posts by user.$id - this is the key test
3334+
$posts = $database->find('postsRelId', [
3335+
Query::equal('user.$id', ['user1']),
3336+
]);
3337+
$this->assertCount(2, $posts);
3338+
$this->assertEquals('post1', $posts[0]->getId());
3339+
$this->assertEquals('post2', $posts[1]->getId());
3340+
3341+
// Query posts by different user.$id
3342+
$posts = $database->find('postsRelId', [
3343+
Query::equal('user.$id', ['user2']),
3344+
]);
3345+
$this->assertCount(1, $posts);
3346+
$this->assertEquals('post3', $posts[0]->getId());
3347+
3348+
// Query posts by multiple user.$id values
3349+
$posts = $database->find('postsRelId', [
3350+
Query::equal('user.$id', ['user1', 'user2']),
3351+
]);
3352+
$this->assertCount(3, $posts);
3353+
3354+
// Query users by posts.$id (inverse direction)
3355+
$users = $database->find('usersRelId', [
3356+
Query::equal('posts.$id', ['post1']),
3357+
]);
3358+
$this->assertCount(1, $users);
3359+
$this->assertEquals('user1', $users[0]->getId());
3360+
3361+
// Clean up MANY_TO_ONE test
3362+
$database->deleteCollection('usersRelId');
3363+
$database->deleteCollection('postsRelId');
3364+
3365+
// Test ONE_TO_ONE relationship - query profile by user.$id
3366+
$database->createCollection('usersOtoId');
3367+
$database->createCollection('profilesOtoId');
3368+
3369+
$database->createAttribute('usersOtoId', 'username', Database::VAR_STRING, 255, true);
3370+
$database->createAttribute('profilesOtoId', 'bio', Database::VAR_STRING, 255, true);
3371+
3372+
$database->createRelationship(
3373+
collection: 'usersOtoId',
3374+
relatedCollection: 'profilesOtoId',
3375+
type: Database::RELATION_ONE_TO_ONE,
3376+
twoWay: true,
3377+
id: 'profile',
3378+
twoWayKey: 'user'
3379+
);
3380+
3381+
$userOto1 = $database->createDocument('usersOtoId', new Document([
3382+
'$id' => 'userOto1',
3383+
'$permissions' => [
3384+
Permission::read(Role::any()),
3385+
Permission::update(Role::any()),
3386+
],
3387+
'username' => 'alice',
3388+
]));
3389+
3390+
$database->createDocument('profilesOtoId', new Document([
3391+
'$id' => 'profileOto1',
3392+
'$permissions' => [
3393+
Permission::read(Role::any()),
3394+
Permission::update(Role::any()),
3395+
],
3396+
'bio' => 'Software Engineer',
3397+
'user' => 'userOto1',
3398+
]));
3399+
3400+
// Query profiles by user.$id
3401+
$profiles = $database->find('profilesOtoId', [
3402+
Query::equal('user.$id', ['userOto1']),
3403+
]);
3404+
$this->assertCount(1, $profiles);
3405+
$this->assertEquals('profileOto1', $profiles[0]->getId());
3406+
3407+
// Query users by profile.$id (inverse)
3408+
$users = $database->find('usersOtoId', [
3409+
Query::equal('profile.$id', ['profileOto1']),
3410+
]);
3411+
$this->assertCount(1, $users);
3412+
$this->assertEquals('userOto1', $users[0]->getId());
3413+
3414+
// Clean up ONE_TO_ONE test
3415+
$database->deleteCollection('usersOtoId');
3416+
$database->deleteCollection('profilesOtoId');
3417+
3418+
// Test MANY_TO_MANY relationship - query projects by developer.$id
3419+
$database->createCollection('developersMtmId');
3420+
$database->createCollection('projectsMtmId');
3421+
3422+
$database->createAttribute('developersMtmId', 'devName', Database::VAR_STRING, 255, true);
3423+
$database->createAttribute('projectsMtmId', 'projectName', Database::VAR_STRING, 255, true);
3424+
3425+
$database->createRelationship(
3426+
collection: 'developersMtmId',
3427+
relatedCollection: 'projectsMtmId',
3428+
type: Database::RELATION_MANY_TO_MANY,
3429+
twoWay: true,
3430+
id: 'projects',
3431+
twoWayKey: 'developers'
3432+
);
3433+
3434+
$dev1 = $database->createDocument('developersMtmId', new Document([
3435+
'$id' => 'dev1',
3436+
'$permissions' => [
3437+
Permission::read(Role::any()),
3438+
Permission::update(Role::any()),
3439+
],
3440+
'devName' => 'Alice',
3441+
]));
3442+
3443+
$dev2 = $database->createDocument('developersMtmId', new Document([
3444+
'$id' => 'dev2',
3445+
'$permissions' => [
3446+
Permission::read(Role::any()),
3447+
Permission::update(Role::any()),
3448+
],
3449+
'devName' => 'Bob',
3450+
]));
3451+
3452+
$database->createDocument('projectsMtmId', new Document([
3453+
'$id' => 'project1',
3454+
'$permissions' => [
3455+
Permission::read(Role::any()),
3456+
Permission::update(Role::any()),
3457+
],
3458+
'projectName' => 'Project Alpha',
3459+
'developers' => ['dev1', 'dev2'],
3460+
]));
3461+
3462+
$database->createDocument('projectsMtmId', new Document([
3463+
'$id' => 'project2',
3464+
'$permissions' => [
3465+
Permission::read(Role::any()),
3466+
Permission::update(Role::any()),
3467+
],
3468+
'projectName' => 'Project Beta',
3469+
'developers' => ['dev1'],
3470+
]));
3471+
3472+
// Query projects by developer.$id
3473+
$projects = $database->find('projectsMtmId', [
3474+
Query::equal('developers.$id', ['dev1']),
3475+
]);
3476+
$this->assertCount(2, $projects);
3477+
3478+
$projects = $database->find('projectsMtmId', [
3479+
Query::equal('developers.$id', ['dev2']),
3480+
]);
3481+
$this->assertCount(1, $projects);
3482+
$this->assertEquals('project1', $projects[0]->getId());
3483+
3484+
// Query developers by project.$id (inverse)
3485+
$developers = $database->find('developersMtmId', [
3486+
Query::equal('projects.$id', ['project1']),
3487+
]);
3488+
$this->assertCount(2, $developers);
3489+
3490+
$developers = $database->find('developersMtmId', [
3491+
Query::equal('projects.$id', ['project2']),
3492+
]);
3493+
$this->assertCount(1, $developers);
3494+
$this->assertEquals('dev1', $developers[0]->getId());
3495+
3496+
// Clean up MANY_TO_MANY test
3497+
$database->deleteCollection('developersMtmId');
3498+
$database->deleteCollection('projectsMtmId');
3499+
}
3500+
32553501
/**
32563502
* Comprehensive test for all query types on relationships
32573503
*/

0 commit comments

Comments
 (0)