The concern of managing user group membership should be factored out of the User object (and the UserGroupMembership class), into a separate service.
The new UserGroupStore service should have the following low-level methods:
newGroupMembership( UserIdentity $user, $group, $expiry = null ): UserGroupMembership; addGroup( UserIdentity $user, $group ) removeGroup( UserIdentity $user, $group ) # needs knowledge of former groups table listGroupNames( UserIdentity $user ): string listGroupMemberships( UserIdentity $user ): GroupMembership # cached, needs to check expiry # need batch mode for listusers purgeExpired() getFormerGroups() listDefinedGroups() addUserGroupChangeListener( callable $listener ) # called from wiring
There should probably be a narrow interface for checking a user's group membership, e.g. UserGroupLookup that just contains listGroupNames and maybe something like userIsInGroup(). PermissionManager should consume that narrow interface, and should not have access to methods for changing group membership.
To allow other services (like PermissionManager) to react to changes in user groups, UserGroupStore needs a way to register listeners:
- addUserGroupChangeListener( callable $listener )
Wiring code, not the services themselves, are responsible for registering the necessary callbacks.
In addition, more high level services could be created that offers the following methods:
AutoPromotionManager (refactoring Autopromote): addAutopromoteOnceGroups EffectiveUserGroupLookup: getEffectiveGroups # needed on page view, because we expose via JS # cache in session?