diff --git a/phpstan-baseline.php b/phpstan-baseline.php index 4e6623cc7..d0463dd69 100644 --- a/phpstan-baseline.php +++ b/phpstan-baseline.php @@ -502,7 +502,7 @@ $ignoreErrors[] = [ 'rawMessage' => 'Call to an undefined method CodeIgniter\\Shield\\Models\\UserModel::getLastQuery().', 'identifier' => 'method.notFound', - 'count' => 7, + 'count' => 9, 'path' => __DIR__ . '/tests/Unit/UserTest.php', ]; $ignoreErrors[] = [ diff --git a/src/Authorization/Traits/Authorizable.php b/src/Authorization/Traits/Authorizable.php index e30cea8e6..ee50addf6 100644 --- a/src/Authorization/Traits/Authorizable.php +++ b/src/Authorization/Traits/Authorizable.php @@ -117,7 +117,7 @@ public function syncGroups(string ...$groups): self */ public function setGroupsCache(array $groups): void { - $this->groupCache = $groups === [] ? null : $groups; + $this->groupCache = $groups; } /** @@ -125,7 +125,7 @@ public function setGroupsCache(array $groups): void */ public function setPermissionsCache(array $permissions): void { - $this->permissionsCache = $permissions === [] ? null : $permissions; + $this->permissionsCache = $permissions; } /** diff --git a/src/Models/UserModel.php b/src/Models/UserModel.php index 7f6a9ebb8..7e5050351 100644 --- a/src/Models/UserModel.php +++ b/src/Models/UserModel.php @@ -210,10 +210,6 @@ protected function fetchGroups(array $data): array // Get our groups for all users $groups = $groupModel->getGroupsByUserIds($userIds); - if ($groups === []) { - return $data; - } - $mappedUsers = $this->assignProperties($data, $groups, 'groups'); $data['data'] = $data['singleton'] ? $mappedUsers[$data['id']] : $mappedUsers; @@ -247,10 +243,6 @@ protected function fetchPermissions(array $data): array $permissions = $permissionModel->getPermissionsByUserIds($userIds); - if ($permissions === []) { - return $data; - } - $mappedUsers = $this->assignProperties($data, $permissions, 'permissions'); $data['data'] = $data['singleton'] ? $mappedUsers[$data['id']] : $mappedUsers; @@ -281,9 +273,10 @@ private function assignProperties(array $data, array $properties, string $type): // Build method name $method = 'set' . ucfirst($type) . 'Cache'; - // Now assign the properties to the user - foreach ($properties as $userId => $propertyArray) { - $mappedUsers[$userId]->{$method}($propertyArray); + // Assign properties to all users (empty array if no properties found) + foreach ($mappedUsers as $userId => $user) { + $propertyArray = $properties[$userId] ?? []; + $user->{$method}($propertyArray); } unset($properties); diff --git a/tests/Unit/UserTest.php b/tests/Unit/UserTest.php index dc1aa18d8..4a057e507 100644 --- a/tests/Unit/UserTest.php +++ b/tests/Unit/UserTest.php @@ -188,6 +188,26 @@ public function testModelFindByIdWithGroups(): void ); } + public function testModelFindByIdWithGroupsWhenUserHasNoGroups(): void + { + // User has no groups in the database + $user = model(UserModel::class)->where('active', 1)->withGroups()->findById(1); + + $this->assertInstanceOf(User::class, $user); + + // Verify groups cache is set to empty array (not null) + $this->assertSame([], $user->getGroups()); + $this->assertFalse($user->inGroup('admin')); + + // Verify the last query was the one with WHERE IN + $query = (string) model(UserModel::class)->getLastQuery(); + $this->assertMatchesRegularExpression( + '/WHERE\s+.*\s+IN\s+\([^)]+\)/i', + $query, + 'Groups were not obtained with the single query (missing "WHERE ... IN" condition)', + ); + } + public function testModelFindAllWithPermissionsUserNotExists(): void { $users = model(UserModel::class)->where('active', 0)->withPermissions()->findAll(); @@ -244,6 +264,28 @@ public function testModelFindByIdWithPermissions(): void ); } + public function testModelFindByIdWithPermissionsWhenUserHasNoPermissions(): void + { + // Load both groups and permissions to ensure can() doesn't trigger queries + $user = model(UserModel::class)->where('active', 1)->withGroups()->withPermissions()->findById(1); + + $this->assertInstanceOf(User::class, $user); + + // Verify permissions cache is set to empty array (not null) + $this->assertSame([], $user->getPermissions()); + $this->assertSame([], $user->getGroups()); + $this->assertFalse($user->hasPermission('users.delete')); + $this->assertFalse($user->can('users.delete')); + + // Verify the last query was the one with WHERE IN + $query = (string) model(UserModel::class)->getLastQuery(); + $this->assertMatchesRegularExpression( + '/WHERE\s+.*\s+IN\s+\([^)]+\)/i', + $query, + 'Groups and Permissions were not obtained with the single query (missing "WHERE ... IN" condition)', + ); + } + public function testModelFindByIdWithGroupsAndPermissions(): void { fake(GroupModel::class, ['user_id' => $this->user->id, 'group' => 'superadmin']);